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

org.jpedal.render.output.TextBlock Maven / Gradle / Ivy

The newest version!
/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/java-pdf-library-support/
 *
 * (C) Copyright 1997-2013, IDRsolutions and Contributors.
 *
 * 	This file is part of JPedal
 *
     This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 *
 * ---------------
 * TextBlock.java
 * ---------------
 */

package org.jpedal.render.output;

import java.awt.Rectangle;

import org.jpedal.parser.PdfStreamDecoder;

public class TextBlock {
	private float rotatedXCoord, rawXCoord, rotatedYCoord, rawYCoord, yAdjust;
	private float lastXCoord;
	private float stringWidth;

	// Maximum screen limit
	private Rectangle cropBox;

	// Font metrics
	private int fontSize;
	private String font = "", realFont = "";
	private String weight = "normal";
	private String style = "normal";
	private float spaceWidth;

	private int fontAdjust = 0;

	private String text;
	private int textColRGB;

	// matrix for this block
	private float[][] matrix;

	private float[][] Trm;
	private float lastYUsed;

	private float lastX = 0; // used in odd rotated pages like siggietest.pdf to pick up breaks

	/** max number of spaces we can add into a text string */
	private int maxSpacesAllowed = 3;

	boolean convertSpacesTonbsp;

	int altFontSize;
	int fontCondition = -1;

	int pageRotation;

	public TextBlock() {

		this.text = "";
		this.textColRGB = -1;
		this.stringWidth = 0;
	}

	public int getAltFontSize() {
		return this.altFontSize;
	}

	public TextBlock(String glyf, int fontSize, FontMapper fontMapper, float[][] matrix, float x, float y, float charWidth, int color,
			float spaceWidth, Rectangle cropBox, float[][] trm, int altFontSize, int fontCondition, float rotX, float rotY, int pageRotation) {
		this.text = glyf;
		this.matrix = matrix;

		this.lastXCoord = rotX + charWidth;

		this.rawXCoord = x;
		this.rotatedXCoord = rotX;

		this.rawYCoord = y;
		this.rotatedYCoord = rotY;

		this.stringWidth = charWidth;

		this.fontSize = fontSize;
		this.font = fontMapper.getFont(false);
		this.realFont = fontMapper.getFont(true);
		this.weight = fontMapper.getWeight();
		this.style = fontMapper.getStyle();
		this.fontAdjust = fontMapper.getFontSizeAdjustment();
		this.textColRGB = color;
		this.spaceWidth = spaceWidth;

		this.cropBox = cropBox;

		this.Trm = trm;

		this.altFontSize = altFontSize;
		this.fontCondition = fontCondition;

		this.pageRotation = pageRotation;

		// track value so we can see when line changes in corner case siggietest.pdf
		this.lastX = trm[2][0];

		// If the text is beyond the page boundaries empty this block is empty.
		if (!cropBox.contains(x + charWidth, y)) {
			this.text = "";
		}
	}

	public boolean isEmpty() {
		return this.text.length() == 0;
	}

	public float getX() {
		return this.rawXCoord;
	}

	public float getY() {
		return this.rawYCoord + this.yAdjust;
	}

	public void adjustY(float y) {
		this.yAdjust = y;
	}

	public float getWidth() {
		return this.stringWidth;
	}

	public int getFontSize() {
		if (this.matrix != null && this.matrix[0][0] * this.matrix[0][1] != 0) {

			float x1 = this.matrix[0][0];
			if (x1 < 0) x1 = -x1;

			float x2 = this.matrix[0][1];
			if (x2 < 0) x2 = -x2;

			float sum1 = x1 + this.fontSize * x2;
			float sum2 = this.fontSize * x1 + x2;

			float result = sum1 > sum2 ? sum1 : sum2;

			return (int) Math.ceil((Math.abs(result) / this.fontSize));
		}
		return this.fontSize;
	}

	public String getFont() {
		return this.realFont;
	}

	public String getWeight() {
		return this.weight;
	}

	public int getColor() {
		return this.textColRGB;
	}

	/**
	 * Compare a trm with the current trm ignoring the translation.
	 */
	private boolean compareTrm(float[][] newTrm) {
		if (this.matrix == null) {
			return false;
		}

		for (int y = 0; y < 3; y++) {
			for (int x = 0; x < 2; x++) {
				if (newTrm[x][y] != this.matrix[x][y]) {
					return false;
				}
			}
		}

		return true;
	}

	/**
	 * @return The angle this text is rotated
	 */
	public float getRotationAngle() {
		float sum = Math.abs(this.matrix[0][0]) + Math.abs(this.matrix[0][1]);
		float angle = (float) Math.acos(this.matrix[0][0] / sum);

		if (angle != 0 && this.matrix[1][0] < 0 && this.matrix[0][1] > 0) angle = -angle;

		return angle;
	}

	/**
	 * make sure it is between 0 and 360
	 * 
	 */
	public int getRotationAngleInDegrees() {
		int angle = (int) (getRotationAngle() * 180 / Math.PI);

		while (angle < 0)
			angle = angle + 360;

		return angle;
	}

	/**
	 * @param s
	 *            - add string to the end of text
	 */
	private void concat(String s) {
		this.text += s;
	}

	public String getOutputString(boolean isOutput) {
		String result = this.text;

		if (isOutput && OutputDisplay.Helper != null) {
			result = OutputDisplay.Helper.tidyText(result);

			if (this.convertSpacesTonbsp) result = result.replaceAll(" ", " ");
		}

		return result;
	}

	public boolean isSameFont(int otherfontSize, FontMapper otherMapper, float[][] newTrm, int color) {

		// allow for close match on text on large text
		float ratio;
		if (this.fontSize < 18 || otherfontSize < 18) ratio = 0;
		else
			if (this.fontSize < otherfontSize) ratio = (float) this.fontSize / (float) otherfontSize;
			else ratio = (float) otherfontSize / (float) this.fontSize;

		return ((compareTrm(newTrm) && this.fontSize == otherfontSize) || ratio > 0.8f) && compareFontMapper(otherMapper) && this.textColRGB == color;
	}

	private boolean compareFontMapper(FontMapper mapper) {
		return mapper.getFont(false).equals(this.font) && mapper.getWeight().equals(this.weight);
	}

	/**
	 * Append a glyf to the current string.
	 * 
	 * @param glyf
	 * @param charWidth
	 * @param x
	 * @param y
	 * @return false if glyf can be appended
	 */
	public boolean appendText(String glyf, float charWidth, float x, float y, boolean groupTJGlyphs, boolean checkGaps, float realX, float realY) {

		// Discard if glyf is to be drawn beyond screen boundary
		if (!this.cropBox.contains(realX, realY)) {
			return false;
		}

		float lineYChange = this.rotatedYCoord - y;

		// ignore tiny changes
		if (Math.abs(lineYChange) < 0.01f) {
			lineYChange = 0f;
			this.rotatedYCoord = y;
		}

		float lineXChange = x - this.lastXCoord;

		/**
		 * Text with 180 rotation gets printed out in reverse so has to be accounted for by subtracting the charWidth from the lineXChange
		 */
		if (this.getRotationAngleInDegrees() == 180) {
			lineXChange = lineXChange - charWidth;
		}

		// ignoreable space (ie "Certifi cat" of Lohnausweis.pdf)
		if (!groupTJGlyphs && glyf.equals(" ") && lineYChange == 0 && lineXChange < (-charWidth)) {
			return true;
		}

		if (!groupTJGlyphs && glyf.equals(" ")) {
			return false;
		}

		// Account for occasions where there is a long line of repeating ..........
		// if(text.endsWith(".") && glyf.equals(".")) {
		// return false;
		// }

		// Allow for slight variation
		if (lineXChange < 0 && lineXChange > -charWidth) {
			lineXChange = 0;
		}

		// ignore test in case of rotated text (see siggietest.pdf) by looking for change on X
		if (this.Trm[0][0] == 0 && this.Trm[1][1] == 0 && this.Trm[0][1] > 0 && this.Trm[1][0] < 0) {

			{ // usual rotated case
				// avoid big gap as probably cursor moving

				float estimatedCharDiff = (this.lastYUsed - y) / this.fontSize;
				if (estimatedCharDiff > 1.5) {
					return false;
				}

				else
					if (this.Trm[0][0] == 0 && this.Trm[1][1] == 0 && (this.lastX == x)) {// Zombies.pdf fix
					// return false;
					}
					else
						if ((lineYChange != 0 || lineXChange < -1.5f)) {// costena space fix
							return false;
						}
			}

		}
		else {

			// note duplication above if we ever change
			if ((lineYChange != 0 || lineXChange < -1.5f)) {
				return false;
			}
		}

		String spaces = "";

		int spaceCount = (int) (lineXChange / this.spaceWidth);
		float percentageOfSpaceSize = lineXChange % this.spaceWidth;
		percentageOfSpaceSize = percentageOfSpaceSize / this.spaceWidth;

		// Close enough to be a space
		if (percentageOfSpaceSize > PdfStreamDecoder.currentThreshold) {
			spaceCount++;
		}

		if (checkGaps || spaceCount > this.maxSpacesAllowed) { // allow us to keep text blocks in TJ together but avoid big spaces as hard to space
																// correctly
			if (spaceCount > 2 || (spaceCount > 0 && this.matrix[0][0] == 0 && this.matrix[1][1] == 0)) // avoid this case // cropping/4.pdf
			return false;
		}

		while (spaceCount-- > 0) {
			spaces += " ";
		}

		// stick the glyf on the end
		spaces += glyf;

		concat(spaces);

		/**
		 * Store current width of the text string
		 */

		if (this.Trm[0][0] == 0 && this.Trm[1][1] == 0 && this.Trm[0][1] > 0 && this.Trm[1][0] < 0 && this.pageRotation == 0) {

			this.stringWidth = this.rotatedYCoord - y + charWidth;
		}
		else {
			this.stringWidth = x - this.rotatedXCoord + charWidth; // for code to work x and xcoords have to be different values.
			this.lastXCoord = x + charWidth;
		}

		this.lastYUsed = y;

		return true;
	}

	public boolean hasSameFont(TextBlock otherBlock) {
		return this.font.equals(otherBlock.font) && this.weight.equals(otherBlock.weight) && getFontSize() == otherBlock.getFontSize();
	}

	public String getStyle() {
		return this.style;
	}

	@Override
	public String toString() {
		String result;
		result = "text[" + this.text + "]\tfontSize[" + this.fontSize + "]\tspaceWidth[" + this.spaceWidth + "]\tcoord[" + this.rawXCoord + ", "
				+ this.rawYCoord + ']';
		return result;
	}

	/**
	 * For over large fonts that can't be mapped to a suitable equivalent.
	 * 
	 */
	public int getFontAdjustment() {
		return this.fontAdjust;
	}

	/*
	 * @return true if glfy is on ignore list
	 */
	public static boolean ignoreGlyf(String glfy) {
		return glfy.codePointAt(0) == 65533;
	}

	public void convertSpacesTonbsp(boolean convertSpacesTonbsp) {
		this.convertSpacesTonbsp = convertSpacesTonbsp;
	}

	public int getFontCondition() {
		return this.fontCondition;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy