All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.genexus.reports.PDFReportItext2 Maven / Gradle / Ivy

Go to download

Core classes for the runtime used by Java and Android apps generated with GeneXus

There is a newer version: 4.7.3
Show newest version
package com.genexus.reports;

import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import com.genexus.ApplicationContext;
import com.genexus.CommonUtil;
import com.genexus.ModelContext;
import com.genexus.platform.NativeFunctions;
import com.genexus.webpanels.HttpContextWeb;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.FontFactory;
import com.lowagie.text.Image;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;
import com.lowagie.text.html.simpleparser.HTMLWorker;
import com.lowagie.text.html.simpleparser.StyleSheet;
import com.lowagie.text.pdf.Barcode;
import com.lowagie.text.pdf.Barcode128;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.ColumnText;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfNumber;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;

import com.genexus.reports.fonts.PDFFont;
import com.genexus.reports.fonts.PDFFontDescriptor;
import com.genexus.reports.fonts.Type1FontMetrics;
import org.springframework.core.io.ClassPathResource;

public class PDFReportItext2 extends GXReportPDFCommons
{
    private com.lowagie.text.Rectangle pageSize;   // Contiene las dimensiones de la página
	private BaseFont baseFont;
	private Barcode barcode = null;
    private Document document;
	private PdfWriter writer;
	private PdfTemplate template;
	private BaseFont templateFont;
	public boolean lineCapProjectingSquare = true;
	public boolean barcode128AsImage = true;
	ConcurrentHashMap documentImages;
	public int runDirection = PdfWriter.RUN_DIRECTION_LTR;

	static {
		log = org.apache.logging.log4j.LogManager.getLogger(PDFReportItext2.class);
	}

	public PDFReportItext2(ModelContext context) {
		super(context);
        document = null;
		pageSize = null;
		documentImages = new ConcurrentHashMap<>();
    }

	protected void init() {
      Document.compress = true;
	  try {
		  writer = PdfWriter.getInstance(document, outputStream);
	  }
	  catch(DocumentException de) {
		  log.error("Failed to initialize new iText2 document: ", de);
      }
      document.open();
    }
	
	/**
	 * @param hideCorners indica si se deben ocultar los triangulos de las esquinas cuando el lado que los une esta oculto.
	 */
	private void drawRectangle(PdfContentByte cb, float x, float y, float w, float h, 
		int styleTop, int styleBottom, int styleRight, int styleLeft, 
		float radioTL, float radioTR, float radioBL, float radioBR, float penAux, boolean hideCorners) {
	
		float[] dashPatternTop = getDashedPattern(styleTop);
		float[] dashPatternBottom = getDashedPattern(styleBottom);
		float[] dashPatternLeft = getDashedPattern(styleLeft);
		float[] dashPatternRight = getDashedPattern(styleRight);

		//-------------------bottom line---------------------
		if (styleBottom!=STYLE_NONE_CONST) {
			cb.setLineDash(dashPatternBottom, 0);
		}

		float b = 0.4477f;
		if (radioBL>0) {
			cb.moveTo(x + radioBL, y);
		}
		else {
			if (hideCorners && styleLeft==STYLE_NONE_CONST && radioBL==0) {
				cb.moveTo(x + penAux, y);
			}
			else {
				cb.moveTo(x, y);
			}
		}

		//-------------------bottom right corner---------------------

		if (styleBottom!=STYLE_NONE_CONST){ //si es null es Style None y no traza la linea

			if (hideCorners && styleRight==STYLE_NONE_CONST && radioBR==0) {
				cb.lineTo(x + w - penAux, y);
			}
			else {
				cb.lineTo(x + w - radioBR, y);
			}
			if (radioBR>0 && styleRight!=STYLE_NONE_CONST) {
				cb.curveTo(x + w - radioBR * b, y, x + w, y + radioBR * b, x + w, y + radioBR);
			}
		}

		//-------------------right line---------------------

		if (styleRight!=STYLE_NONE_CONST && dashPatternRight!=dashPatternBottom) {
			cb.stroke();
			cb.setLineDash(dashPatternRight, 0);
			if (hideCorners && styleBottom==STYLE_NONE_CONST && radioBR==0) {
				cb.moveTo(x + w, y + penAux);
			}
			else {
				cb.moveTo(x + w, y + radioBR);
			}
		}

		//-------------------top right corner---------------------
		if (styleRight!=STYLE_NONE_CONST) {
			if (hideCorners && styleTop==STYLE_NONE_CONST && radioTR==0) {
				cb.lineTo (x + w, y + h - penAux);
			}
			else {
				cb.lineTo (x + w, y + h - radioTR);
			}
			if (radioTR>0 && styleTop!=STYLE_NONE_CONST) {
				cb.curveTo(x + w, y + h - radioTR * b, x + w - radioTR * b, y + h, x + w - radioTR, y + h);
			}
		}

		//-------------------top line---------------------

		if (styleTop!=STYLE_NONE_CONST && dashPatternTop!=dashPatternRight) {
			cb.stroke();
			cb.setLineDash(dashPatternTop, 0);
			if (hideCorners && styleRight==STYLE_NONE_CONST && radioTR==0) {
				cb.moveTo(x + w - penAux, y + h);
			}
			else {
				cb.moveTo(x + w - radioTR, y + h);
			}
		}

		//-------------------top left corner---------------------
		if (styleTop!=STYLE_NONE_CONST) {
			if (hideCorners && styleLeft==STYLE_NONE_CONST && radioTL==0) {
				cb.lineTo(x + penAux, y + h);
			}
			else {
				cb.lineTo(x + radioTL, y + h);
			}
			if (radioTL>0 && styleLeft!=STYLE_NONE_CONST) {
				cb.curveTo(x + radioTL * b, y + h, x, y + h - radioTL * b, x, y + h - radioTL);
			}
		}
            
		//-------------------left line---------------------

		if (styleLeft!=STYLE_NONE_CONST  && dashPatternLeft!=dashPatternTop) {
			cb.stroke();
			cb.setLineDash(dashPatternLeft, 0);
			if (hideCorners && styleTop==STYLE_NONE_CONST && radioTL==0) {
				cb.moveTo(x, y + h - penAux);
			}
			else {
				cb.moveTo(x, y + h - radioTL);
			}
		}
            
		//-------------------bottom left corner---------------------
		if (styleLeft!=STYLE_NONE_CONST) {
			if (hideCorners && styleBottom==STYLE_NONE_CONST && radioBL==0) {
				cb.lineTo(x, y + penAux);
			}
			else {
				cb.lineTo(x, y + radioBL);
			}
			if (radioBL>0 && styleBottom!=STYLE_NONE_CONST) {
				cb.curveTo(x, y + radioBL * b, x + radioBL * b, y, x + radioBL, y);
			}
		}
		cb.stroke();
            
	}
	private void roundRectangle(PdfContentByte cb, float x, float y, float w, float h, 
		float radioTL, float radioTR, float radioBL, float radioBR) {

		//-------------------bottom line---------------------

		float b = 0.4477f;
		if (radioBL>0) {
			cb.moveTo(x + radioBL, y);
		}
		else {
			cb.moveTo(x, y);
		}

		//-------------------bottom right corner---------------------

		cb.lineTo(x + w - radioBR, y);
		if (radioBR>0) {
			cb.curveTo(x + w - radioBR * b, y, x + w, y + radioBR * b, x + w, y + radioBR);
		}
		

		cb.lineTo (x + w, y + h - radioTR);
		if (radioTR>0) {
			cb.curveTo(x + w, y + h - radioTR * b, x + w - radioTR * b, y + h, x + w - radioTR, y + h);
		}

		cb.lineTo(x + radioTL, y + h);
		if (radioTL>0) {
			cb.curveTo(x + radioTL * b, y + h, x, y + h - radioTL * b, x, y + h - radioTL);
		}
		cb.lineTo(x, y + radioBL);
		if (radioBL>0) {
			cb.curveTo(x, y + radioBL * b, x + radioBL * b, y, x + radioBL, y);
		}
            
	}

	public void GxDrawRect(int left, int top, int right, int bottom, int pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue, 
		int styleTop, int styleBottom, int styleRight, int styleLeft, int cornerRadioTL, int cornerRadioTR, int cornerRadioBL, int cornerRadioBR) {

		PdfContentByte cb = writer.getDirectContent();

		float penAux = (float)convertScale(pen);
		float rightAux = (float)convertScale(right);
		float bottomAux = (float)convertScale(bottom);
		float leftAux = (float)convertScale(left);
		float topAux = (float)convertScale(top);
		
		cb.saveState();
		
		float x1, y1, x2, y2;
		x1 = leftAux + leftMargin;
		y1 = pageSize.getTop() - bottomAux - topMargin -bottomMargin;
		x2 = rightAux + leftMargin;
		y2 = pageSize.getTop() - topAux - topMargin -bottomMargin;

		cb.setLineWidth(penAux);		
		cb.setLineCap(PdfContentByte.LINE_CAP_PROJECTING_SQUARE);

		if (cornerRadioBL==0 && cornerRadioBR==0 && cornerRadioTL==0 && cornerRadioTR==0 && styleBottom==0 && styleLeft==0 && styleRight==0 && styleTop==0) {
			//Tengo que hacer eso para que el borde quede del mismo color que el fill si se indica que no se quiere borde,
			//porque no funciona el setLineWidth
			if (pen > 0)
				cb.setRGBColorStroke(foreRed, foreGreen, foreBlue);
			else
				cb.setRGBColorStroke(backRed, backGreen, backBlue);

			cb.rectangle(x1, y1, x2 - x1, y2 - y1);

			if (backMode!=0) {
				cb.setColorFill(new Color(backRed, backGreen, backBlue));
				cb.fillStroke();
			}
			cb.closePathStroke();
		}
		else {
			float w = x2 - x1;
			float h = y2 - y1;
			if (w < 0) {
				x1 += w;
				w = -w;
			}
			if (h < 0) {
				y1 += h;
				h = -h;
			}

			float cRadioTL = (float)convertScale(cornerRadioTL);
			float cRadioTR = (float)convertScale(cornerRadioTR);
			float cRadioBL = (float)convertScale(cornerRadioBL);
			float cRadioBR = (float)convertScale(cornerRadioBR);

			// Scale the radius if it's too large or to small to fit.
			int max = (int)Math.min(w, h);
			cRadioTL = Math.max(0, Math.min(cRadioTL, max/2));
			cRadioTR = Math.max(0, Math.min(cRadioTR, max/2));
			cRadioBL = Math.max(0, Math.min(cRadioBL, max/2));
			cRadioBR = Math.max(0, Math.min(cRadioBR, max/2));

			if (backMode!=0) {
				//Interior del rectangulo
				cb.setRGBColorStroke(backRed, backGreen, backBlue);
				cb.setLineWidth(0);		
					roundRectangle(cb, x1, y1, w, h,
						cRadioTL, cRadioTR,
						cRadioBL, cRadioBR);
				cb.setColorFill(new Color(backRed, backGreen, backBlue));
				cb.fillStroke();
				cb.setLineWidth(penAux);		
			}
			if (pen > 0) {
				//Bordes del rectangulo
				cb.setRGBColorStroke(foreRed, foreGreen, foreBlue);
					drawRectangle(cb, x1, y1, w, h, 
						styleTop, styleBottom, styleRight, styleLeft,
						cRadioTL, cRadioTR,
						cRadioBL, cRadioBR, penAux, false);
			}
		}
		cb.restoreState();

		log.debug("GxDrawRect -> (" + left + "," + top + ") - (" + right + "," + bottom + ")  BackMode: " + backMode + " Pen:" + pen);
    }
	
    public void GxDrawLine(int left, int top, int right, int bottom, int width, int foreRed, int foreGreen, int foreBlue, int style) {
		PdfContentByte cb = writer.getDirectContent();

		float widthAux = (float)convertScale(width);
		float rightAux = (float)convertScale(right);
		float bottomAux = (float)convertScale(bottom);
		float leftAux = (float)convertScale(left);
		float topAux = (float)convertScale(top);

		log.debug("GxDrawLine -> (" + left + "," + top + ") - (" + right + "," + bottom + ") Width: " + width);

		float x1, y1, x2, y2;

		x1 = leftAux + leftMargin;
		y1 = pageSize.getTop() - bottomAux - topMargin -bottomMargin;
		x2 = rightAux + leftMargin;
		y2 = pageSize.getTop() - topAux - topMargin -bottomMargin;

		cb.saveState();
		cb.setRGBColorStroke(foreRed, foreGreen, foreBlue);
		cb.setLineWidth(widthAux);

		if (lineCapProjectingSquare) {
			cb.setLineCap(PdfContentByte.LINE_CAP_PROJECTING_SQUARE); //Hace que lineas de width 10 por ejemplo que forman una esquina no quedan igual que en disenio porque "rellena" la esquina.
		}
		if (style!=0) {
			float[] dashPattern = getDashedPattern(style);
			cb.setLineDash(dashPattern, 0);
		}
		cb.moveTo(x1, y1);
		cb.lineTo(x2, y2);			
		cb.stroke();
			
		cb.restoreState();
    }
	
    public void GxDrawBitMap(String bitmap, int left, int top, int right, int bottom, int aspectRatio) {
		try {
			com.lowagie.text.Image image;
			try {
				if (documentImages != null && documentImages.containsKey(bitmap)) {
					image = documentImages.get(bitmap);
				}
				else {

					if (!NativeFunctions.isWindows() && new File(bitmap).isAbsolute() && bitmap.startsWith(httpContext.getStaticContentBase())) {
						bitmap = bitmap.replace(httpContext.getStaticContentBase(), "");
					}				
				
					if (!new File(bitmap).isAbsolute() && !bitmap.toLowerCase().startsWith("http:") && !bitmap.toLowerCase().startsWith("https:")) {
						if (bitmap.startsWith(httpContext.getStaticContentBase())) {
							bitmap = bitmap.replace(httpContext.getStaticContentBase(), "");
						}					
						// Si la ruta a la imagen NO es absoluta, en aplicaciones Web le agregamos al comienzo la ruta al root de la aplicación
						// más la staticContentBaseURL si ésta es relativa.
						bitmap = defaultRelativePrepend + bitmap;
						if (ApplicationContext.getInstance().isSpringBootApp() && !new File(bitmap).exists()) {
							image = com.lowagie.text.Image.getInstance(new ClassPathResource(bitmap).getContentAsByteArray());
						}
						else {
							image = com.lowagie.text.Image.getInstance(bitmap);
							if (image == null) { // Si all\uFFFDEno se encuentra la imagen, entonces la buscamos bajo el webAppDir (para mantener compatibilidad)
								bitmap = webAppDir + bitmap;
								image = com.lowagie.text.Image.getInstance(bitmap);
							}
						}
					}
					else {
						image = com.lowagie.text.Image.getInstance(bitmap);
					}
				}
			}
			catch(java.lang.IllegalArgumentException ex) {//Puede ser una url absoluta
				java.net.URL url= new java.net.URL(bitmap);
				image = com.lowagie.text.Image.getInstance(url);
			}

			if (documentImages == null) {
				documentImages = new ConcurrentHashMap<>();
			}
			documentImages.putIfAbsent(bitmap, image);

			log.debug("GxDrawBitMap -> '" + bitmap + "' [" + left + "," + top + "] - Size: (" + (right - left) + "," + (bottom - top) + ")");

	        if(image != null) { // Si la imagen NO se encuentra, no hago nada
				float rightAux = (float)convertScale(right);
				float bottomAux = (float)convertScale(bottom);
				float leftAux = (float)convertScale(left);
				float topAux = (float)convertScale(top);

				image.setAbsolutePosition(leftAux + leftMargin, this.pageSize.getTop() - bottomAux - topMargin - bottomMargin);
				if (aspectRatio == 0)
					image.scaleAbsolute(rightAux - leftAux , bottomAux - topAux);
				else
					image.scaleToFit(rightAux - leftAux , bottomAux - topAux);
				PdfContentByte cb = writer.getDirectContent();
				cb.addImage(image);
			}
		}
		catch(DocumentException de) {
			log.error("GxDrawBitMap failed:", de);
		}
		catch(IOException ioe) {
			log.error("GxDrawBitMap failed:", ioe);
		}
		catch(Exception e) {
			log.error("GxDrawBitMap failed:", e);
		}
    }

    public void GxAttris(String fontName, int fontSize, boolean fontBold, boolean fontItalic, boolean fontUnderline, boolean fontStrikethru, int Pen, int foreRed, int foreGreen, int foreBlue, int backMode, int backRed, int backGreen, int backBlue) {
		boolean isCJK = false;
		boolean embeedFont = isEmbeddedFont(fontName);
		String originalFontName = fontName;
		if (!embeedFont) {
			fontName = getSubstitute(fontName); // Veo si hay substitutos solo si el font no va a ir embebido
		}

		String fontSubstitute = "";
		if (!originalFontName.equals(fontName)) {
			fontSubstitute = "Original Font: " + originalFontName + " Substitute";
		}
		log.debug("GxAttris: ");
		log.debug("\\-> " + fontSubstitute + "Font: " + fontName + " (" + fontSize + ")" + (fontBold ? " BOLD" : "") + (fontItalic ? " ITALIC" : "") + (fontStrikethru ? " Strike" : ""));
		log.debug("\\-> Fore (" + foreRed + ", " + foreGreen + ", " + foreBlue + ")");log.debug("\\-> Back (" + backRed + ", " + backGreen + ", " + backBlue + ")");

		if (barcode128AsImage && fontName.toLowerCase().indexOf("barcode 128") >= 0 || fontName.toLowerCase().indexOf("barcode128") >= 0) {
			barcode = new Barcode128();
			barcode.setCodeType(Barcode128.CODE128);
		}
		else {
			barcode = null;
		}
		this.fontUnderline = fontUnderline;        
		this.fontStrikethru = fontStrikethru;        
        this.fontSize = fontSize;
        this.fontBold = fontBold;
        this.fontItalic = fontItalic;
		foreColor = new Color(foreRed, foreGreen, foreBlue);
        backColor = new Color(backRed, backGreen, backBlue);
		
		backFill = (backMode != 0);
		try {
			if (PDFFont.isType1(fontName)) {
				//Me fijo si es un Asian font
				for(int i = 0; i < Type1FontMetrics.CJKNames.length; i++) {
					if(Type1FontMetrics.CJKNames[i][0].equalsIgnoreCase(fontName) ||
					   Type1FontMetrics.CJKNames[i][1].equalsIgnoreCase(fontName)) {
						String style = "";
						if (fontBold && fontItalic)
							style = "BoldItalic";
						else {
							if (fontItalic)
								style = "Italic";
							if (fontBold)
								style = "Bold";
						}
						setAsianFont(fontName, style);
						isCJK = true;
						break;
					}
				}
				if (!isCJK) {
					int style = 0;
					if (fontBold && fontItalic)
						style = style + 3;
					else {
						if (fontItalic)
							style = style + 2;
						if (fontBold)
							style = style + 1;
					}
					for(int i=0;i= 2 && !((align & 16) == 16) && htmlformat != 1) {
			if (valign == VerticalAlign.TOP.value())
				bottom = top + (int)reconvertScale(lineHeight);
			else if (valign == VerticalAlign.BOTTOM.value())
				top = bottom - (int)reconvertScale(lineHeight);
		}

		float bottomAux = (float)convertScale(bottom) - ((float)convertScale(bottom-top) - captionHeight)/2;   
		//Al bottom de los textos se le resta espacio entre el texto y el borde del textblock, 
		//porque en el reporte genexus la x,y de un
		//text es la x,y del cuadro que contiene el texto, y la api de itext espera la x,y del texto en si.
		//Generalmente el cuadro es mas grande que lo que ocupa el texto realmente (depende del tipo de font)
		//captionHeight esta convertido, bottom y top no.
		float topAux = (float)convertScale(top) + ((float)convertScale(bottom-top) - captionHeight)/2;   


		float startHeight = bottomAux - topAux - captionHeight;
		
		float leftAux = (float)convertScale(left);
		float rightAux = (float)convertScale(right);
		int alignment = align & 3;
		boolean autoResize = (align & 256) == 256;
		
        if (htmlformat == 1) {
            StyleSheet styles = new StyleSheet();
			Hashtable locations = getFontLocations();
			for (Enumeration e = locations.keys(); e.hasMoreElements() ;) {
				String fontName = (String)e.nextElement();
				String fontPath = (String)locations.get(fontName);
				if (fontPath.equals("")) {
					fontPath = PDFFontDescriptor.getTrueTypeFontLocation(fontName, props);
				}
				if (!fontPath.equals("")) {
					FontFactory.register(fontPath, fontName);
					styles.loadTagStyle("body", "face", fontName);

					if (isEmbeddedFont(fontName)){
						styles.loadTagStyle("body", "encoding", BaseFont.IDENTITY_H);
					}
					else{
						styles.loadTagStyle("body", "encoding", BaseFont.WINANSI);
					}
				}
			}

            //Bottom y top son los absolutos, sin considerar la altura real a la que se escriben las letras.
            bottomAux = (float)convertScale(bottom);
            topAux = (float)convertScale(top);

            ColumnText Col = new ColumnText(cb);
			int colAlignment = columnAlignment(alignment);
			if (colAlignment!=0)
				Col.setAlignment(colAlignment);
			ColumnText simulationCol = new ColumnText(null);
			float drawingPageHeight = (float)this.pageSize.getTop() - topMargin - bottomMargin;
			Col.setSimpleColumn(leftAux + leftMargin,drawingPageHeight - bottomAux,rightAux + leftMargin,drawingPageHeight - topAux);
			simulationCol.setSimpleColumn(leftAux + leftMargin,drawingPageHeight - bottomAux,rightAux + leftMargin,drawingPageHeight - topAux);

            try {
                ArrayList objects = HTMLWorker.parseToList(new StringReader(sTxt), styles);
                for (int k = 0; k < objects.size(); ++k) {
						if (pageHeightExceeded(bottomAux, drawingPageHeight)) {
							simulationCol.addElement((Element)objects.get(k));
							simulationCol.go(true);

							if (simulationCol.getYLine() < bottomMargin) {
								GxEndPage();
								GxStartPage();
								simulationCol = new ColumnText(null);
								simulationCol.setSimpleColumn(leftAux + leftMargin,drawingPageHeight - bottomAux,rightAux + leftMargin,drawingPageHeight - topAux);
								simulationCol.addElement((Element)objects.get(k));

								Col = new ColumnText(cb);
								if (colAlignment!=0)
									Col.setAlignment(colAlignment);
								Col.setSimpleColumn(leftAux + leftMargin,drawingPageHeight - bottomAux,rightAux + leftMargin,drawingPageHeight - topAux);

								bottomAux = bottomAux - drawingPageHeight;
							}
						}
						if (objects.get(k) instanceof Paragraph && colAlignment!=0)
							((Paragraph)objects.get(k)).setAlignment(colAlignment);

						Col.addElement((Element)objects.get(k));
						Col.go();
				}
			}catch(Exception de){
				log.error("ERROR printing HTML text ", de.getMessage());
			}
		}
		else 
		if (barcode!=null) {
			log.debug("Barcode: --> " + barcode.getClass().getName());
			try {
				barcode.setCode(sTxt);
				barcode.setTextAlignment(alignment);
				com.lowagie.text.Rectangle rectangle = new com.lowagie.text.Rectangle(0, 0);
				//El rectangulo tiene tamaño ok.
				switch (alignment) {
					case 1: // Center Alignment
						rectangle = new com.lowagie.text.Rectangle((leftAux + rightAux) / 2 + leftMargin - rectangleWidth / 2,
							(float)this.pageSize.getTop() - (float)convertScale(bottom) - topMargin - bottomMargin,
							(leftAux + rightAux) / 2 + leftMargin + rectangleWidth / 2,
							(float)this.pageSize.getTop() - (float)convertScale(top) - topMargin - bottomMargin);
						break;
					case 2: // Right Alignment
						rectangle = new com.lowagie.text.Rectangle(rightAux + leftMargin - rectangleWidth,
							(float)this.pageSize.getTop() - (float)convertScale(bottom) - topMargin - bottomMargin,
							rightAux + leftMargin,
							(float)this.pageSize.getTop() - (float)convertScale(top) - topMargin - bottomMargin);
						break;
					case 0: // Left Alignment
						rectangle = new com.lowagie.text.Rectangle(leftAux + leftMargin,
							(float)this.pageSize.getTop() - (float)convertScale(bottom) - topMargin - bottomMargin,
							leftAux + leftMargin + rectangleWidth,
							(float)this.pageSize.getTop() - (float)convertScale(top) - topMargin - bottomMargin);
						break;
				}
				barcode.setAltText("");
				barcode.setBaseline(0);

				if (fontSize < Const.LARGE_FONT_SIZE)
					barcode.setX(Const.OPTIMAL_MINIMU_BAR_WIDTH_SMALL_FONT);
				else
					barcode.setX(Const.OPTIMAL_MINIMU_BAR_WIDTH_LARGE_FONT);

				Image imageCode = barcode.createImageWithBarcode(cb, backFill ? backColor : null, foreColor);
				imageCode.setAbsolutePosition(leftAux + leftMargin, rectangle.getBottom());
				barcode.setBarHeight(rectangle.getHeight());
				imageCode.scaleToFit(rectangle.getWidth(), rectangle.getHeight());
				document.add(imageCode);
			}
			catch (Exception ex) {
				log.error("GxDrawText: Error generating Barcode " + barcode.getClass().getName(), ex);
			}
		}
		else {

			if(backFill) {
				com.lowagie.text.Rectangle rectangle = new com.lowagie.text.Rectangle(0,0);
				//Si el texto tiene background lo dibujo de esta forma
				switch(alignment) {
				case 1: // Center Alignment
					rectangle = new com.lowagie.text.Rectangle((leftAux + rightAux)/2 + leftMargin - rectangleWidth/2, (float)this.pageSize.getTop() -  bottomAux - topMargin -bottomMargin , (leftAux + rightAux)/2 + leftMargin + rectangleWidth/2, (float)this.pageSize.getTop() - topAux - topMargin -bottomMargin);				
					break;
				case 2: // Right Alignment
					rectangle = new com.lowagie.text.Rectangle(rightAux + leftMargin - rectangleWidth, (float)this.pageSize.getTop() -  bottomAux - topMargin -bottomMargin , rightAux + leftMargin, (float)this.pageSize.getTop() - topAux - topMargin -bottomMargin);				
					break;
				case 0: // Left Alignment
					rectangle = new com.lowagie.text.Rectangle(leftAux + leftMargin, (float)this.pageSize.getTop() -  bottomAux - topMargin -bottomMargin , leftAux + leftMargin + rectangleWidth, (float)this.pageSize.getTop() - topAux - topMargin -bottomMargin);				
					break;
				}
				rectangle.setBackgroundColor(backColor);
				try {
					document.add(rectangle);
				}
				catch(DocumentException de) {
					log.error("backfill in GxDrawText failed: ", de);
				}
			}
			
			float underlineSeparation = lineHeight / 5;//Separacion entre el texto y la linea del subrayado
			int underlineHeight = (int)underlineSeparation + (int)(underlineSeparation/4);
			com.lowagie.text.Rectangle underline;
			
			//Si el texto esta subrayado
			if (fontUnderline) {
				underline = new com.lowagie.text.Rectangle(0,0);
				
				switch(alignment) {
				case 1: // Center Alignment
						underline = new com.lowagie.text.Rectangle( 
							(leftAux + rightAux)/2 + leftMargin - rectangleWidth/2, 
							this.pageSize.getTop() - bottomAux - topMargin -bottomMargin + startHeight - underlineSeparation, 
							(leftAux + rightAux)/2 + leftMargin + rectangleWidth/2, 
							this.pageSize.getTop() - bottomAux - topMargin -bottomMargin + startHeight - underlineHeight);				
					break;
				case 2: // Right Alignment
						underline = new com.lowagie.text.Rectangle( rightAux + leftMargin - rectangleWidth , 
							this.pageSize.getTop() - bottomAux - topMargin -bottomMargin + startHeight - underlineSeparation, 
							rightAux + leftMargin, 
							this.pageSize.getTop() - bottomAux  - topMargin -bottomMargin + startHeight - underlineHeight);				
					break;
				case 0: // Left Alignment
						underline = new com.lowagie.text.Rectangle( leftAux + leftMargin , 
							this.pageSize.getTop() - bottomAux - topMargin -bottomMargin + startHeight - underlineSeparation, 
							leftAux + leftMargin + rectangleWidth, 
							this.pageSize.getTop() - bottomAux  - topMargin -bottomMargin + startHeight - underlineHeight);				
					break;
				}
				underline.setBackgroundColor(foreColor);
				try {
					document.add(underline);
				}
				catch(DocumentException de) {
					log.error("fontUnderline in GxDrawText failed: ", de);
				}
			}
			
			//Si el texto esta tachado
			if (fontStrikethru) {
				underline = new com.lowagie.text.Rectangle(0,0);
				float strikethruSeparation = lineHeight / 2;
				
				switch(alignment)
				{
				case 1: // Center Alignment
						underline = new com.lowagie.text.Rectangle( 
							(leftAux + rightAux)/2 + leftMargin - rectangleWidth/2, 
							this.pageSize.getTop() - bottomAux - topMargin -bottomMargin + startHeight - underlineSeparation + strikethruSeparation, 
							(leftAux + rightAux)/2 + leftMargin + rectangleWidth/2, 
							this.pageSize.getTop() - bottomAux - topMargin -bottomMargin + startHeight - underlineHeight + strikethruSeparation);				
					break;
				 case 2: // Right Alignment
						underline = new com.lowagie.text.Rectangle( rightAux + leftMargin - rectangleWidth , 
							this.pageSize.getTop() - bottomAux - topMargin -bottomMargin + startHeight - underlineSeparation + strikethruSeparation, 
							rightAux + leftMargin, 
							this.pageSize.getTop() - bottomAux  - topMargin -bottomMargin + startHeight - underlineHeight + strikethruSeparation);				
					break;
				case 0: // Left Alignment
						underline = new com.lowagie.text.Rectangle( leftAux + leftMargin , 
							this.pageSize.getTop() - bottomAux - topMargin -bottomMargin + startHeight - underlineSeparation + strikethruSeparation, 
							leftAux + leftMargin + rectangleWidth, 
							this.pageSize.getTop() - bottomAux  - topMargin -bottomMargin + startHeight - underlineHeight + strikethruSeparation);
					break;
				}
				underline.setBackgroundColor(foreColor);
				try {
					document.add(underline);
				}
				catch(DocumentException de) {
					log.error("fontStrikethru in GxDrawText failed: ", de);
				}
			}
			
			if(sTxt.trim().equalsIgnoreCase("{{Pages}}")) {// Si el texto es la cantidad de páginas del documento
				if (!templateCreated) {
					template = cb.createTemplate(right - left, bottom - top);
					templateCreated = true;
				}
				cb.addTemplate(template, leftAux + leftMargin, this.pageSize.getTop() -  bottomAux - topMargin -bottomMargin);
				templateFont = baseFont;
				templateFontSize = fontSize;
				templateColorFill = foreColor;
				return;
			}

			float textBlockWidth = rightAux - leftAux;
			float TxtWidth = baseFont.getWidthPoint(sTxt, fontSize);
			boolean justified = (alignment == 3) && textBlockWidth < TxtWidth;
			boolean wrap = ((align & 16) == 16);

			//Justified
            if (wrap || justified) {
                //Bottom y top son los absolutos, sin considerar la altura real a la que se escriben las letras.
                bottomAux = (float)convertScale(bottomOri);
                topAux = (float)convertScale(topOri);

                //La constante 2 para LEADING indica la separacion que se deja entre un renglon y otro. (es lo que mas se asemeja a la api vieja).
                float leading = (float)(Double.valueOf(props.getGeneralProperty(Const.LEADING)).doubleValue());
                Paragraph p = new Paragraph(sTxt, font);

				float llx = leftAux + leftMargin;
				float lly = (float)this.pageSize.getTop() - bottomAux - topMargin - bottomMargin;
				float urx = rightAux + leftMargin;
				float ury = (float)this.pageSize.getTop() - topAux - topMargin - bottomMargin;

                try{
					DrawColumnText(cb, llx, lly, urx, ury, p, leading, runDirection, valign, alignment);
                }
			    catch (DocumentException ex) {
					log.error("wrap in GxDrawText failed: ", ex);
			    }
            }
			else{ //no wrap
				startHeight=0;
				if (!autoResize) {
					//Va quitando el ultimo char del texto hasta que llega a un string cuyo ancho se pasa solo por un caracter
					//del ancho del textblock ("se pasa solo por un caracter": esto es porque en el caso general es ese el texto que
					//mas se parece a lo que se disenia en genexus).
					String newsTxt = sTxt;
					while(TxtWidth > textBlockWidth && (newsTxt.length()-1>=0)) {
						sTxt = newsTxt;
						newsTxt = newsTxt.substring(0, newsTxt.length()-1);
						TxtWidth = baseFont.getWidthPoint(newsTxt, fontSize);
					}
				}

				Phrase phrase = new Phrase(sTxt, font);
				switch(alignment) {
				case 1: // Center Alignment
					ColumnText.showTextAligned(cb, cb.ALIGN_CENTER, phrase, ((leftAux + rightAux) / 2) + leftMargin, this.pageSize.getTop() - bottomAux - topMargin - bottomMargin + startHeight, 0, runDirection, arabicOptions);
					break;
				case 2: // Right Alignment
					ColumnText.showTextAligned(cb, cb.ALIGN_RIGHT, phrase, rightAux + leftMargin, this.pageSize.getTop() - bottomAux - topMargin - bottomMargin + startHeight, 0, runDirection, arabicOptions);
					break;
				case 0: // Left Alignment
				case 3: // Justified, only one text line
					ColumnText.showTextAligned(cb, cb.ALIGN_LEFT, phrase, leftAux + leftMargin, this.pageSize.getTop() - bottomAux - topMargin - bottomMargin + startHeight, 0, runDirection, arabicOptions);
					break;
				}
			}
		}
    }
	boolean pageHeightExceeded(float bottomAux, float drawingPageHeight){
		return super.pageHeightExceeded(bottomAux,drawingPageHeight);
	}
	ColumnText SimulateDrawColumnText(PdfContentByte cb, Rectangle rect, Paragraph p, float leading, int runDirection, int alignment) throws DocumentException {
		ColumnText Col = new ColumnText(cb);
		Col.setRunDirection(runDirection);
		Col.setAlignment(alignment);
		Col.setLeading(leading, 1);
		Col.setSimpleColumn(rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop());
		Col.addText(p);
		Col.go(true);
		return Col;
	}
	void DrawColumnText(PdfContentByte cb, float llx, float lly, float urx, float ury, Paragraph p, float leading, int runDirection, int valign, int alignment) throws DocumentException {
		Rectangle rect = new Rectangle(llx, lly, urx, ury);
		ColumnText ct = SimulateDrawColumnText(cb, rect, p, leading, runDirection, alignment);//add the column in simulation mode
		float y = ct.getYLine();
		int linesCount = ct.getLinesWritten();

		//calculate a new rectangle for valign = middle 
		if (valign == VerticalAlign.MIDDLE.value())
			ury = ury - ((y - lly) / 2) + leading;
		else if (valign == VerticalAlign.BOTTOM.value())
			ury = ury - (y - lly- leading);
		else if (valign == VerticalAlign.TOP.value())
			ury = ury + leading/2;

		rect = new Rectangle(llx, lly, urx, ury); //Rectangle for new ury

		ColumnText Col = new ColumnText(cb);
		Col.setRunDirection(runDirection);
		if (linesCount <= 1)
			Col.setLeading(0, 1);
		else
			Col.setLeading(leading, 1);
		Col.setSimpleColumn(rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop());

		Col.setAlignment(columnAlignment(alignment));
		Col.addText(p);
		Col.go();
	}
	private int columnAlignment(int alignment) {
		if (alignment == Element.ALIGN_JUSTIFIED)
			return justifiedType;
		else
			return alignment;
	}

    public boolean GxPrintInit(String output, int gxXPage[], int gxYPage[], String iniFile, String form, String printer, int mode, int orientation, int pageSize, int pageLength, int pageWidth, int scale, int copies, int defSrc, int quality, int color, int duplex) {
		boolean preResult = super.GxPrintInit(output, gxXPage, gxYPage, iniFile, form, printer, mode, orientation, pageSize, pageLength, pageWidth, scale, copies, defSrc, quality, color, duplex);

		runDirection = Integer.valueOf(props.getGeneralProperty(Const.RUN_DIRECTION)).intValue();

		if (props.getBooleanGeneralProperty(Const.JUSTIFIED_TYPE_ALL, false))
			justifiedType = Element.ALIGN_JUSTIFIED_ALL;
		else
			justifiedType = Element.ALIGN_JUSTIFIED;

		//Se ignora el parametro orientation para el calculo del pageSize, los valores de alto y ancho ya vienen invertidos si Orientation=2=landscape.
        this.pageSize = computePageSize(leftMargin, topMargin, pageWidth, pageLength, props.getBooleanGeneralProperty(Const.MARGINS_INSIDE_BORDER, false));
        gxXPage[0] = (int)this.pageSize.getRight();
		if (props.getBooleanGeneralProperty(Const.FIX_SAC24437, true))
			gxYPage[0] = (int)(pageLength / GX_PAGE_SCALE_Y); // Cuanto menor sea GX_PAGE_SCALE_Y, GeneXus imprime mayor parte de cada hoja
		else
			gxYPage[0] = (int)(pageLength / GX_PAGE_SCALE_Y_OLD); // Cuanto menor sea GX_PAGE_SCALE_Y, GeneXus imprime mayor parte de cada hoja

		document = new Document(this.pageSize,0,0,0,0);

        init();

		if (!preResult)
			return !preResult;
		else
			return true;
    }

	private com.lowagie.text.Rectangle computePageSize(float leftMargin, float topMargin, int width, int length, boolean marginsInsideBorder) {
		if ((leftMargin == 0 && topMargin == 0)||marginsInsideBorder) {
			if (length == 23818 && width == 16834)
				return PageSize.A3;
			else if (length == 16834 && width == 11909)
				return PageSize.A4;
			else if (length == 11909 && width == 8395)
				return PageSize.A5;
			else if (length == 20016 && width == 5731)
				return PageSize.B4;
			else if (length == 14170 && width == 9979)
				return PageSize.B5;
			else if (length == 15120 && width == 10440)
				return PageSize.EXECUTIVE;
			else if (length == 20160 && width == 12240)
				return PageSize.LEGAL;
			else if (length == 15840 && width == 12240)
				return PageSize.LETTER;
			else
				return new com.lowagie.text.Rectangle((int)(width / PAGE_SCALE_X) , (int)(length / PAGE_SCALE_Y) );
		}
		return new com.lowagie.text.Rectangle((int)(width / PAGE_SCALE_X) + leftMargin, (int)(length / PAGE_SCALE_Y) + topMargin);
	}

    public void GxEndDocument() {
      if(document.getPageNumber() == 0) { // Si no hay ninguna página en el documento, agrego una vacia}
        writer.setPageEmpty(false);
      }

        //Ahora proceso los comandos GeneXus {{Pages}}
	    if (template != null) {
	  		template.beginText();
			template.setFontAndSize(templateFont, templateFontSize);
			template.setTextMatrix(0,0);
			template.setColorFill(templateColorFill);
			template.showText(String.valueOf(pages));
			template.endText();
		}
		int copies = 1;
		try {
			copies = Integer.parseInt(printerSettings.getProperty(form, Const.COPIES));
			log.debug("Setting number of copies to " + copies);
			writer.addViewerPreference(PdfName.NUMCOPIES, new PdfNumber(copies));

			int duplex= Integer.parseInt(printerSettings.getProperty(form, Const.DUPLEX));
			PdfName duplexValue;
			switch (duplex){
				case 1: duplexValue = PdfName.SIMPLEX; break;
				case 2: duplexValue = PdfName.DUPLEX; break;
				case 3: duplexValue = PdfName.DUPLEXFLIPSHORTEDGE;break;
				case 4: duplexValue = PdfName.DUPLEXFLIPLONGEDGE;break;
				default: duplexValue = PdfName.NONE;
			}
			log.debug("Setting duplex to " + duplexValue.toString());
			writer.addViewerPreference(PdfName.DUPLEX, duplexValue);
		}
		catch(Exception ex) {
			log.error("GxEndDocument failed to apply viewer preferences: ", ex);
		}

		String serverPrinting = props.getGeneralProperty(Const.SERVER_PRINTING);
		boolean fit= props.getGeneralProperty(Const.ADJUST_TO_PAPER).equals("true");
		if ((outputType==Const.OUTPUT_PRINTER || outputType==Const.OUTPUT_STREAM_PRINTER) && (httpContext instanceof HttpContextWeb && serverPrinting.equals("false"))) {
				writer.addJavaScript("var pp = this.getPrintParams();\n");
				String printerAux=printerSettings.getProperty(form, Const.PRINTER);
				String printer = replace(printerAux, "\\", "\\\\");

				if (printer!=null && !printer.equals("")) {
	                writer.addJavaScript("pp.printerName = \"" + printer + "\";\n");
				}
			if (fit) {
				writer.addJavaScript("pp.pageHandling = pp.constants.handling.fit;\n");
			}
			else {
				writer.addJavaScript("pp.pageHandling = pp.constants.handling.none;\n");
			}

				if (printerSettings.getProperty(form, Const.MODE, "3").startsWith("0")){//Show printer dialog Never
					writer.addJavaScript("pp.interactive = pp.constants.interactionLevel.automatic;\n");
					//No print dialog is displayed. During printing a progress monitor and cancel
					//dialog is displayed and removed automatically when printing is complete.
					for(int i = 0; i < copies; i++) {
						writer.addJavaScript("this.print(pp);\n");
					}
				}
				else{ //Show printer dialog is sent directly to printer | always
					writer.addJavaScript("pp.interactive = pp.constants.interactionLevel.full;\n");
					//Displays the print dialog allowing the user to change print settings and requiring
					//the user to press OK to continue. During printing a progress monitor and cancel
					//dialog is displayed and removed automatically when printing is complete.
					writer.addJavaScript("this.print(pp);\n");
				}
				
		}

		document.close();

        log.debug("GxEndDocument!");

		try{ props.save(); } catch(IOException e) { ; }

        // OK, ahora que ya terminamos el PDF, vemos si tenemos que mostrarlo en pantalla
        switch(outputType) {
        case Const.OUTPUT_SCREEN:
            try{ outputStream.close(); } catch(IOException e) { ; } // Cierro el archivo
            try{ showReport(docName, modal); } catch(Exception e) { // Si no se puede mostrar el reporte
                e.printStackTrace();
            }
//  Comento la próxima lú‹ea, porque por manejo interno del Acrobat, si ya habú} una instancia del
//  Acrobat corriendo, el modal no funciona (x que el proceso levantado le avisa al que ya estaba abierto qu\uFFFDE
//  archivo abrir y luego se mata automáticamente)
//            if(modal)if(new File(docName).delete())TemporaryFiles.getInstance().removeFileFromList(docName); // Intento eliminar el docName aqu\uFFFDE
            break;
        case Const.OUTPUT_PRINTER:
            try{ outputStream.close(); } catch(IOException e) { ; } // Cierro el archivo
            try{
				if (!(httpContext instanceof HttpContextWeb) || !serverPrinting.equals("false")) {
					printReport(docName, this.printerOutputMode == 1); 
				}
			} catch(Exception e){ // Si no se puede mostrar el reporte
               	log.error("GxEndDocument: failed to show report ", e);
            }
            break;
        case Const.OUTPUT_FILE:
            try{ outputStream.close(); } catch(IOException e) { log.error("GxEndDocument: failed to save report to file ", e); } // Cierro el archivo
            break;
        case Const.OUTPUT_STREAM:
		case Const.OUTPUT_STREAM_PRINTER:
        default: break;
        }
        outputStream = null;
    }
    public void GxStartPage() {
		boolean ret = document.newPage();
		pages = pages +1;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy