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

org.jpedal.objects.acroforms.overridingImplementations.ReadOnlyTextIcon 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


 *
 * ---------------
 * ReadOnlyTextIcon.java
 * ---------------
 */
package org.jpedal.objects.acroforms.overridingImplementations;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;

import javax.swing.Icon;
import javax.swing.SwingConstants;

import org.jpedal.io.PdfObjectReader;
import org.jpedal.objects.acroforms.formData.ComponentData;
import org.jpedal.objects.raw.FormObject;
import org.jpedal.objects.raw.FormStream;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.objects.raw.XObject;
import org.jpedal.utils.StringUtils;

/** this class is used to display the text fields in the defined font, but is used for readonly fields only. */
public class ReadOnlyTextIcon extends CustomImageIcon implements Icon, SwingConstants {

	private int alignment = -1;

	private static final long serialVersionUID = 8946195842453749725L;

	/** stores the root image for selected and unselected icons */
	private BufferedImage rootImage = null;
	/** stores the final image after any icon rotation */
	private BufferedImage finalImage = null;

	private PdfObject fakeObj = null;

	/** tells us if the text for this icon has chnaged and so if we need to redraw the icon */
	private boolean textChanged = false;
	private String preFontStream = "", betweenFontAndTextStream = "", afterTextStream = "", text = "";
	private String fontName = "", fontSize = "", fontCommand = "";

	/** our full command Stream */
	private String fullCommandString;

	private PdfObjectReader currentpdffile = null;
	private int subtype = -1;
	private PdfObject resources;

	/**
	 * new code to store the data to create the image when needed to the size needed offset = if 0 no change, 1 offset image, 2 invert image 
* NOTE if decipherAppObject ios not called this will cause problems. */ public ReadOnlyTextIcon(int iconRot, PdfObjectReader pdfObjectReader, PdfObject res) { super(iconRot); this.currentpdffile = pdfObjectReader; this.resources = res; // if(selObj.getObjectRefAsString().equals("128 0 R") || selObj.getObjectRefAsString().equals("130 0 R")) // debug = true; } /** returns the currently selected Image */ @Override public Image getImage() { Image image; checkAndCreateimage(); image = this.finalImage; return image; } /** * draws the form to a BufferedImage the size of the Icon and returns it, uses the paintIcon method for the drawing so future changes should only * be in one place */ public BufferedImage drawToBufferedImage() { BufferedImage bufImg = new BufferedImage(getIconWidth(), getIconHeight(), BufferedImage.TYPE_INT_ARGB); Graphics g = bufImg.getGraphics(); paintIcon(null, g, 0, 0); g.dispose(); return bufImg; } @Override public synchronized void paintIcon(Component c, Graphics g, int x, int y) { BufferedImage image = (BufferedImage) getImage(); if (image == null) return; if (c != null && c.isEnabled()) { g.setColor(c.getBackground()); } else { g.setColor(Color.gray); } Graphics2D g2 = (Graphics2D) g; if (this.iconWidth > 0 && this.iconHeight > 0) { int drawWidth = this.iconWidth; int drawHeight = this.iconHeight; if (this.displaySingle && (this.iconRotation == 270 || this.iconRotation == 90)) { // swap width and height so that the image is drawn in the corect orientation // without changing the raw width and height for the icon size drawWidth = this.iconHeight; drawHeight = this.iconWidth; } // only work out scaling if we have a dictionary of an image, as otherwise it could be a blank image (i.e. 1 x 1). if (this.currentpdffile != null) { // work out w,h which we want to draw inside our icon to maintain aspect ratio. float ws = (float) drawWidth / (float) image.getWidth(null); float hs = (float) drawHeight / (float) image.getHeight(null); if (ws < hs) { drawWidth = (int) (ws * image.getWidth(null)); drawHeight = (int) (ws * image.getHeight(null)); } else { drawWidth = (int) (hs * image.getWidth(null)); drawHeight = (int) (hs * image.getHeight(null)); } } // now work out the x,y position to keep the icon in the centre of the icon int posX = 0, posY = 0; if (this.currentpdffile != null) { if (this.displaySingle && (this.iconRotation == 270 || this.iconRotation == 90)) { posX = (this.iconHeight - drawWidth) / 2; posY = (this.iconWidth - drawHeight) / 2; } else { posX = (this.iconWidth - drawWidth) / 2; posY = (this.iconHeight - drawHeight) / 2; } } if (this.alignment == SwingConstants.LEFT) posX = 0; int finalRotation; if (this.displaySingle) { finalRotation = validateRotationValue(this.pageRotate - this.iconRotation); } else { finalRotation = this.pageRotate; } /** with new decode at needed size code the resize (drawImage) may not be needed. */ if (finalRotation == 270) { g2.rotate(-Math.PI / 2); g2.translate(-drawWidth, 0); g2.drawImage(image, -posX, posY, drawWidth, drawHeight, null); } else if (finalRotation == 90) { g2.rotate(Math.PI / 2); g2.translate(0, -drawHeight); g2.drawImage(image, posX, -posY, drawWidth, drawHeight, null); } else if (finalRotation == 180) { g2.rotate(Math.PI); g2.translate(-drawWidth, -drawHeight); g2.drawImage(image, -posX, -posY, drawWidth, drawHeight, null); } else { g2.drawImage(image, posX, posY, drawWidth, drawHeight, null); } } else g2.drawImage(image, 0, 0, null); g2.translate(-x, -y); } private void checkAndCreateimage() { // check if pdf object reader is defined, as we still use opaque images which do NOT need redecoding if (this.currentpdffile == null) return; /** * NOTE the image code may need changing so that we store up to a certain size image and not store large images, once the user has rescaled to * a more normal size. we could store the root width and height for the 100% size and use 200% as the highest image size to keep. * * if we do this the best way would be to have an object that we move the decode routine to, and then when we read the 100% values from the * image object we can store them in that size. */ // define normal sizes for normal use int newWidth = this.iconWidth, newHeight = this.iconHeight; // decode images at needed size if (this.textChanged || this.rootImage == null || newWidth > (this.rootImage.getWidth(null)) || newHeight > (this.rootImage.getHeight(null)) || newWidth < (this.rootImage.getWidth(null) / MAXSCALEFACTOR) || newHeight < (this.rootImage.getHeight(null) / MAXSCALEFACTOR)) { // System.out.println(fakeObj.getObjectRefAsString()+" command="+fullCommandString); this.rootImage = FormStream.decode(this.currentpdffile, this.fakeObj, this.subtype, newWidth, newHeight, 0, 1); this.finalImage = FormStream.rotate(this.rootImage, this.iconRotation); // make text as redrawn this.textChanged = false; }// icon rotation is always defined in the constructor so we dont need to change it } /** * set the font and text of the form, ie if it changes, set this and it will redraw the image. if font is null it will not be changed. */ public void setFont(String fontName, float fontSize, String fontCommand) { this.fontName = fontName; if (fontName.length() != 0) { this.fontSize = " " + fontSize + ' '; } else { this.fontSize = ""; } this.fontCommand = fontCommand; } public void setText(String str) { if (str == null) str = ""; if (str.equals(this.text)) return; this.textChanged = true; this.text = str; this.fullCommandString = this.preFontStream + this.fontName + this.fontSize + this.fontCommand + this.betweenFontAndTextStream + '(' + this.text + ")Tj " + this.afterTextStream; this.fakeObj.setDecodedStream(StringUtils.toBytes(this.fullCommandString)); } public String getText() { return this.text; } /** * decodes and saves all information needed to decode the object on the fly, the test and font can be altered with specific methods. * * @return boolean true if it all worked. */ public boolean decipherAppObject(FormObject form) { // read the command from file if there is one String fontStr = ""; PdfObject appObj = form.getDictionary(PdfDictionary.AP).getDictionary(PdfDictionary.N); if (appObj != null) { byte[] bytes = appObj.getDecodedStream(); if (bytes != null) { int startTf = -1, endTf = -1, startTj, endTj = -1, end = bytes.length; // find index of Tf command for (int i = 0; i < end - 1; i++) { if (((char) bytes[i]) == 'T' && ((char) bytes[i + 1]) == 'f') { if (i + 2 >= end || bytes[i + 2] == 10 || bytes[i + 2] == 13 || bytes[i + 2] == ' ') { endTf = i + 2; break; } } } if (endTf == -1) { startTf = 0; endTf = 0; } else { // find beginning of Tf command // int strs = 0; // boolean strFound = false; for (int i = endTf - 3; i > startTf; i--) { // this is kept until its passed tests. // if(bytes[i]==' ' || bytes[i]==10 || bytes[i]==13){ // if(strFound){ // strs++; // if(strs==2){ // startTj = i+1;//to allow for the gap // //should give the same index as the '/' // break; // } // } // continue; // }else if (bytes[i] == '/') { startTf = i; break; // }else { // strFound = true; } } // ******startTf and endTf should both have a value, and start should be before end****** } // find index of Tj command for (int i = endTf; i < end - 1; i++) { if (((char) bytes[i]) == 'T' && ((char) bytes[i + 1]) == 'j') { if (i + 2 >= end || bytes[i + 2] == 10 || bytes[i + 2] == 13 || bytes[i + 2] == ' ') { endTj = i + 2; break; } } } if (endTj == -1) { startTj = endTf; endTj = endTf; } else { startTj = endTf; // find the start of the Tj command int brackets = 0; boolean strFound = false; for (int i = endTj - 3; i > startTj; i--) { if (bytes[i] == ' ' || bytes[i] == 10 || bytes[i] == 13) { if (strFound && brackets == 0) { // +1 as we dont want the gap we just found in our text string startTj = i + 1; break; } continue; } else if (bytes[i] == ')') { brackets++; } else if (bytes[i] == '(') { brackets--; if (brackets == 0 && strFound) { startTj = i; break; } } else { strFound = true; } } // ******* startTJ and endTj should both have a value and start should be before end ****** } // find actual end of Tf including any rg or g command after the Tf. for (int i = endTf; i < startTj; i++) { if (bytes[i] == ' ' || bytes[i] == 10 || bytes[i] == 13) { continue; } else if (bytes[i] > 47 && bytes[i] < 58) { // number continue; } else { if (bytes[i] == 'g' && i + 1 < startTj && (bytes[i + 1] == ' ' || bytes[i + 1] == 10 || bytes[i + 1] == 13)) { endTf = i + 1; break; } else if (bytes[i] == 'r' && i + 2 < startTj && bytes[i + 1] == 'g' && (bytes[i + 2] == ' ' || bytes[i + 2] == 10 || bytes[i + 2] == 13)) { endTf = i + 2; break; } else { // not what we want leave endTf as is. break; } } } if (endTj != endTf) { // there is a Tj (text) if (endTf == 0) { // we dont have a font command defined so allow for one this.preFontStream = new String(bytes, 0, startTj); this.betweenFontAndTextStream = " "; } else { // we have a font command this.preFontStream = new String(bytes, 0, startTf); fontStr = new String(bytes, startTf, endTf - startTf); this.betweenFontAndTextStream = new String(bytes, endTf, startTj - endTf); } // -3 to ignore the Tj command letters at the end as we add that ourselves. this.text = new String(bytes, startTj, endTj - 3 - startTj); this.afterTextStream = new String(bytes, endTj, bytes.length - endTj); } else { // theres no TJ if (endTf == 0) { // store as command1, and if not valid we deal with below with default command this.preFontStream = new String(bytes); } else { // we have a font command this.preFontStream = new String(bytes, 0, startTf); fontStr = new String(bytes, startTf, endTf - startTf); // add rest to middleCommand so Text can be added to end this.betweenFontAndTextStream = new String(bytes, endTf, bytes.length - endTf); } } } } // get the forms font string String DA = form.getTextStreamValue(PdfDictionary.DA); if (DA == null || DA.length() == 0) { if (fontStr.length() != 0) { // set font we have found form.setTextStreamValue(PdfDictionary.DA, StringUtils.toBytes(fontStr)); FormStream.decodeFontCommandObj(fontStr, form); } // use old methods as appropriate info not present. return false; } else { // updated by Mark as previous code had bug // we replace the TF string (ie /FO_0 8 Tf) with the DA value (ie /Helv 8 Tf) to get the font name // this though assumes that Tm is 1 0 0 1 (scaling is done by fotnsize not matrix) // on sample file Tf was 1 and Tm was 8 0 0 8 so we ended up with text 8 times too big as we changed // /Fo_0 1 Tf to /Helv 8 Tf while not altering Tm // I have fixed by keeping Tf value and using /DA font part if (fontStr.length() == 0) // use defined DA and remove any whitespace at front (ORIGINAL version) fontStr = DA.trim(); else {// get font name from DA but use original fontsize String fontname = DA.substring(0, DA.indexOf(' ')); String fontsize = fontStr.substring(fontStr.indexOf(' '), fontStr.length()); fontStr = fontname + fontsize; fontStr.trim(); } } // create a fake XObject to make use of the code we already have to generate image this.fakeObj = new XObject(form.getObjectRefAsString()); // value does not matter // do not think we need but here for completeness // XObject.setFloatArray(PdfDictionary.Matrix,new float[]{1,0,0,1,0,0}); // forms can have resources (Fonts, XOBjects, etc) which are in the DR value - // we store this in DefaultAcroRenderer if (this.resources != null) this.fakeObj.setDictionary(PdfDictionary.Resources, this.resources); Rectangle BBox = form.getBoundingRectangle(); this.fakeObj.setFloatArray(PdfDictionary.BBox, new float[] { BBox.width, 0, 0, BBox.height, 0, 0 }); this.subtype = -1; // could use PdfDictionary.Highlight for transparency // if no command in file. if (this.preFontStream.length() == 0 || !this.preFontStream.contains("BT")) { // build a fake command stream to decode this.preFontStream = "BT 0 0 0 RG 1 TFS "; this.betweenFontAndTextStream = " 1 0 0 1 0 0 Tm "; this.afterTextStream = ""; } // find the start and end of the size param int sizeSt = fontStr.indexOf(' '); int sizeEn = -1; boolean strFound = false; for (int i = sizeSt; i < fontStr.length(); i++) { char chr = fontStr.charAt(i); if (chr == ' ' || chr == 10 || chr == 13) { if (strFound) { sizeEn = i; break; } continue; } else { strFound = true; } } float size = 12; if (sizeEn != -1) { // store the name, and command this.fontName = fontStr.substring(0, sizeSt); this.fontCommand = fontStr.substring(sizeEn); size = Float.parseFloat(fontStr.substring(sizeSt, sizeEn)); } // store the seperate font attributes if (this.fontName.length() == 0) { Font textFont = form.getTextFont(); this.fontName = '/' + textFont.getFontName(); this.fontCommand = "Tf "; } // check if font size needs autosizing if (size == 0 || size == -1) { // call our calculate routine to work out a good size size = ComponentData.calculateFontSize(BBox.height, BBox.width, false, this.text); } this.fontSize = " " + size + ' '; return true; } public void setAlignment(int alignment) { this.alignment = alignment; } /** generates higher quality images */ public void setPrinting(boolean print, int multiplier) { checkAndCreateimage(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy