
org.jpedal.objects.acroforms.overridingImplementations.ReadOnlyTextIcon Maven / Gradle / Ivy
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/support/
*
* (C) Copyright 1997-2015 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
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.Canvas;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.StringTokenizer;
import javax.swing.*;
import org.jpedal.io.PdfObjectReader;
import org.jpedal.objects.acroforms.GUIData;
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.LogWriter;
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 {
//
/** used to tell the paint method that we need to scale up the image for printing */
private boolean currentlyPrinting;
private int printMultiplier = 1;
private int alignment=-1;
private static final long serialVersionUID = 8946195842453749725L;
/** stores the root image for selected and unselected icons */
private BufferedImage rootImage;
/** stores the final image after any icon rotation */
private BufferedImage finalImage;
private PdfObject fakeObj;
/** tells us if the text for this icon has chnaged and so if we need to redraw the icon*/
private boolean textChanged;
private String preFontStream="",betweenFontAndTextStream="",afterTextStream="",text="";
private String fontName="",fontSize="12",fontCommand="";
/** our full command Stream*/
private String fullCommandString;
private final PdfObjectReader currentpdffile;
private int subtype=-1;
private final PdfObject resources;
private final PdfObject form;
/** 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(final PdfObject form, final int iconRot, final PdfObjectReader pdfObjectReader, final PdfObject res){
super(iconRot);
this.form=form;
currentpdffile = pdfObjectReader;
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(){
final Image image;
checkAndCreateimage();
image = 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(){
final BufferedImage bufImg = new BufferedImage(getIconWidth(), getIconHeight(), BufferedImage.TYPE_INT_ARGB);
final Graphics g = bufImg.getGraphics();
paintIcon(null, g, 0, 0);
g.dispose();
//
return bufImg;
}
@Override
public synchronized void paintIcon(final Component c, final Graphics g, final int x, final int y) {
//
final BufferedImage image = (BufferedImage) getImage();
if (image == null) {
return;
}
if (c!=null && c.isEnabled()) {
g.setColor(c.getBackground());
} else {
g.setColor(Color.gray);
}
//
final Graphics2D g2 = (Graphics2D) g;
if (iconWidth > 0 && iconHeight > 0) {
int drawWidth = iconWidth;
int drawHeight = iconHeight;
if(displaySingle && (iconRotation==270 || 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 = iconHeight;
drawHeight = 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(currentpdffile!=null){
//work out w,h which we want to draw inside our icon to maintain aspect ratio.
final float ws = (float)drawWidth / (float)image.getWidth(null);
final float hs = (float)drawHeight / (float)image.getHeight(null);
if(ws (rootImage.getWidth(null))
|| newHeight > (rootImage.getHeight(null))
|| newWidth < (rootImage.getWidth(null)/MAXSCALEFACTOR)
|| newHeight < (rootImage.getHeight(null)/MAXSCALEFACTOR)
//
){
//System.out.println(fakeObj.getObjectRefAsString()+" command="+fullCommandString);
rootImage = FormStream.decode(form,currentpdffile, fakeObj, subtype,newWidth,newHeight,0,1);
//
finalImage = FormStream.rotate(rootImage,iconRotation);
//
//make text as redrawn
textChanged = false;
}//icon rotation is always defined in the constructor so we dont need to change it
}
public void setText(String str){
if(str==null) {
str = "";
}
if(str.equals(text)) {
return;
}
textChanged = true;
this.text = str;
//check afterTextStream to try and sto duplicate TJs with same wording
final PdfObject xobj = new PdfObject("1 10 X");
while(true){
xobj.setDecodedStream(StringUtils.toBytes(afterTextStream));
final String tj = FormStream.decipherTextFromAP(currentpdffile,xobj);
if(tj!=null && text.contains(tj)){
final int endOfTj = afterTextStream.indexOf(" Tj", afterTextStream.indexOf(tj))+3;
afterTextStream = afterTextStream.substring(endOfTj);
if(afterTextStream.isEmpty()) {
break;
}
}else {
break;
}
}
try {
if(text.contains("\n")){//multiline text
final StringTokenizer lines=new StringTokenizer(text,"\n",false);
String nextLine;
//Add 2 to simulate form padding
String textAlignment;
int alignmentX = 2;
int alignmentY = ((FormObject)form).getBoundingRectangle().height;
textAlignment = " "+alignmentX+ ' ' +alignmentY+" Td ";
this.fullCommandString = preFontStream+fontName+fontSize+fontCommand+
betweenFontAndTextStream + textAlignment;
// int y = ((FormObject)form).getBoundingRectangle().height;
int xPoint = 0;
while(lines.hasMoreTokens()){
nextLine=lines.nextToken();
final FontMetrics fm = new Canvas().getFontMetrics(new Font(fontName, Font.PLAIN, (int)Float.parseFloat(fontSize)));
final Rectangle2D r = fm.getStringBounds(nextLine, null);
if(((FormObject)form).getAlignment()!=-1){
switch(((FormObject)form).getAlignment()){
case SwingConstants.LEFT :
alignmentX=0;
break;
case SwingConstants.CENTER :
alignmentX = ((int)(((FormObject)form).getBoundingRectangle().width - r.getWidth()))/2;
alignmentX -= xPoint;
break;
case SwingConstants.RIGHT :
alignmentX = ((int)(((FormObject)form).getBoundingRectangle().width - r.getWidth()));
alignmentX -= xPoint;
break;
}
}
alignmentY = (int)-(r.getHeight()+2);
//Construction alignment string
textAlignment = " "+alignmentX+ ' ' +alignmentY+" Td ";
this.fullCommandString += textAlignment+'(' +nextLine+")Tj ";
xPoint += alignmentX;
}
this.fullCommandString += afterTextStream;
}else{
//Add 2 to simulate form padding
int alignmentX = 2;
int alignmentY = ((int) (((FormObject) form).getBoundingRectangle().height - Float.parseFloat(fontSize))) / 2;
if(alignmentY<2){
alignmentY = 2;
}
if (((FormObject) form).getAlignment() != SwingConstants.LEFT) {
final FontMetrics fm = new Canvas().getFontMetrics(new Font(fontName, Font.PLAIN, (int) Float.parseFloat(fontSize)));
final Rectangle2D r = fm.getStringBounds(text, null);
switch (((FormObject) form).getAlignment()) {
case SwingConstants.CENTER:
alignmentX = ((int) (((FormObject) form).getBoundingRectangle().width - r.getWidth())) / 2;
break;
case SwingConstants.RIGHT:
alignmentX = ((int) (((FormObject) form).getBoundingRectangle().width - r.getWidth())) - 2;
break;
}
}
//Construction alignment string
String textAlignment = alignmentX+" "+alignmentY+" Td ";
this.fullCommandString = preFontStream+fontName+fontSize+fontCommand+
betweenFontAndTextStream+ textAlignment+'(' +text+")Tj "+afterTextStream;
}
Color BG = null;
// Color BC = new Color(0, 0, 0, 0);
if(form.getDictionary(PdfDictionary.MK)!=null){
final PdfObject MK = form.getDictionary(PdfDictionary.MK);
// float[] bc = MK.getFloatArray(PdfDictionary.BC);
final float[] bg = MK.getFloatArray(PdfDictionary.BG);
/*
* Some files do not store colour values between 0 and 1.
* Catch these and convert them
*/
//Check BG values
if(bg!=null){
boolean colorOutOfBounds = false;
for(int i=0; i!=bg.length; i++){
if(bg[i]>1.0f){
colorOutOfBounds = true;
break;
}
}
if(colorOutOfBounds){
for(int i=0; i!=bg.length; i++){
if(bg[i]>1.0f) {
bg[i] /= 255;
}
}
}
}
//Check BC values
// if(bc!=null){
// boolean colorOutOfBounds = false;
// for(int i=0; i!=bc.length; i++){
// if(bc[i]>1.0f){
// colorOutOfBounds = true;
// break;
// }
// }
// if(colorOutOfBounds){
// for(int i=0; i!=bc.length; i++){
// if(bg[i]>1.0f)
// bc[i] = bc[i]/255;
// }
// }
// }
//
//
// if(bc != null && bc.length>0){
// switch(bc.length){
// case 1 :
// BC = new Color(bc[0],bc[0],bc[0],1.0f);
// break;
// case 3 :
// BC = new Color(bc[0],bc[1],bc[2],1.0f);
// break;
// case 4 :
//// BC = new Color(bc[0],bc[0],bc[0],1.0f);
// break;
// }
// }
if(bg != null && bg.length>0){
switch(bg.length){
case 1 :
BG = new Color(bg[0],bg[0],bg[0],1.0f);
break;
case 3 :
BG = new Color(bg[0],bg[1],bg[2],1.0f);
break;
case 4 :
// BG = new Color(bg[0],bg[0],bg[0],1.0f);
break;
}
}
}
//Fill Background if set
if(BG!=null){
this.fullCommandString = BG.getRed()+" "+BG.getGreen()+ ' ' +BG.getBlue()+" rg 0 0 "+(((FormObject)form).getBoundingRectangle().width-3)+ ' ' +(((FormObject)form).getBoundingRectangle().height-3)+" re f "+fullCommandString;
}
//we may need to actually check encoding on font here rather than assume win
fakeObj.setDecodedStream(fullCommandString.getBytes("Cp1252"));
} catch (final IOException e) {
//tell user and log
if(LogWriter.isOutput()) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
//
}
}
/** 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(final FormObject form) {
//read the command from file if there is one
String fontStr="";
final PdfObject appObj = form.getDictionary(PdfDictionary.AP).getDictionary(PdfDictionary.N);
if(appObj!=null){
final byte[] bytes = appObj.getDecodedStream();
//
if(bytes!=null){
int startTf=-1;
int endTf=-1;
int startTj;
int endTj=-1;
final int 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') &&
(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') &&
(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;
}
}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){
}else if(bytes[i]>47 && bytes[i]<58){
//number
}else {
if(bytes[i]=='g' && i+1
© 2015 - 2025 Weber Informatics LLC | Privacy Policy