org.jpedal.objects.acroforms.FormRenderUtilsG2 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of OpenViewerFX Show documentation
Show all versions of OpenViewerFX Show documentation
An Open Source JavaFX PDF Viewer
/*
* ===========================================
* 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-2016 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
*
* ---------------
* FormRenderUtilsG2.java
* ---------------
*/
package org.jpedal.objects.acroforms;
import com.idrsolutions.pdf.color.blends.BlendMode;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.util.StringTokenizer;
import org.jpedal.objects.raw.FormObject;
import org.jpedal.objects.raw.PdfArrayIterator;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.parser.DecoderOptions;
/**
* Swing specific implementation of Form Rendering
*
*/
public class FormRenderUtilsG2 {
private static Color getBorderColor(FormObject formObject){
Color BC = new Color(0, 0, 0, 0);
if (formObject.getDictionary(PdfDictionary.MK) != null) {
final PdfObject MK = formObject.getDictionary(PdfDictionary.MK);
final float[] bc = MK.getFloatArray(PdfDictionary.BC);
BC = FormObject.generateColor(bc);
}
return BC;
}
private static Color getBorderBackgroundColor(FormObject formObject){
Color BG = new Color(0, 0, 0, 0);
if (formObject.getDictionary(PdfDictionary.MK) != null) {
final PdfObject MK = formObject.getDictionary(PdfDictionary.MK);
final float[] bg = MK.getFloatArray(PdfDictionary.BG);
BG = FormObject.generateColor(bg);
}
return BG;
}
private static void renderBorderSolid(Graphics2D g2, int borderWidth, int x, int y, int w, int h) {
g2.setStroke(new BasicStroke(borderWidth));
g2.drawRect(x + borderWidth - 1,
y + borderWidth - 1,
w - (borderWidth * 2) + 1,
h - (borderWidth * 2) + 1);
}
private static void renderBorderDashed(Graphics2D g2, PdfArrayIterator dashPattern, int borderWidth, int x, int y, int w, int h) {
float[] dash = {3};
int phase = 0;
if (dashPattern.getTokenCount() > 0) {
final int count = dashPattern.getTokenCount();
if (count > 0) {
dash = dashPattern.getNextValueAsFloatArray();
}
if (count > 1) {
phase = dashPattern.getNextValueAsInteger();
}
}
if (dash.length == 0) {
g2.setStroke(new BasicStroke(borderWidth));
} else if (dash.length > 0) {
g2.setStroke(new BasicStroke(borderWidth, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER, 10.0f, dash, phase));
}
g2.drawRect(x + borderWidth - 1,
y + borderWidth - 1,
w - (borderWidth * 2) + 1,
h - (borderWidth * 2) + 1);
}
private static void renderBorderBeveled(Graphics2D g2, Color BG, int borderWidth, int x, int y, int w, int h) {
final Color bckUp = g2.getColor();
//Outer Line
g2.setStroke(new BasicStroke(borderWidth, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER, 10.0f));
g2.drawRect(x,
y,
w,
h);
//Inner Line
g2.setStroke(new BasicStroke(1));
x += 1;
y += 1;
w -= 2;
h -= 2;
//DARKER
g2.setColor(BG.darker());
g2.fillPolygon(
new int[]{x + w, x + w, x + w - borderWidth, x + w - borderWidth, x + borderWidth, x},
new int[]{y + h, y, y + borderWidth, y + h - borderWidth, y + h - borderWidth, y + h},
6);
//LIGHTER
g2.setColor(BG.brighter());
g2.fillPolygon(
new int[]{x, x, x + borderWidth, x + borderWidth, x + w - borderWidth, x + w},
new int[]{y, y + h, y + h - borderWidth, y + borderWidth, y + borderWidth, y},
6);
g2.setColor(bckUp);
}
private static void renderBorderInset(Graphics2D g2, int borderWidth, int x, int y, int w, int h) {
final Color bckUp = g2.getColor();
//Outer Line
g2.setStroke(new BasicStroke(borderWidth));
g2.drawRect(x,
y,
w,
h);
//Inner Line
g2.setStroke(new BasicStroke(1));
x += 1;
y += 1;
w -= 2;
h -= 2;
//LIGHTER
g2.setColor(Color.LIGHT_GRAY);
g2.fillPolygon(
new int[]{x + w, x + w, x + w - borderWidth, x + w - borderWidth, x + borderWidth, x},
new int[]{y + h, y, y + borderWidth, y + h - borderWidth, y + h - borderWidth, y + h},
6);
//DARKER
g2.setColor(Color.GRAY);
g2.fillPolygon(
new int[]{x, x, x + borderWidth, x + borderWidth, x + w - borderWidth, x + w},
new int[]{y, y + h, y + h - borderWidth, y + borderWidth, y + borderWidth, y},
6);
g2.setColor(bckUp);
}
private static void renderBorderUnderline(Graphics2D g2, int borderWidth, int x, int y, int w) {
g2.setStroke(new BasicStroke(borderWidth));
g2.drawLine(x + borderWidth - 1,
y + borderWidth - 1,
x + w - 1,
y + borderWidth - 1);
}
public static int renderBorder(Graphics2D g2, FormObject formObject, int pageHeight){
int x = formObject.getBoundingRectangle().x;
int y = pageHeight - (formObject.getBoundingRectangle().y + formObject.getBoundingRectangle().height);
int w = formObject.getBoundingRectangle().width;
int h = formObject.getBoundingRectangle().height;
return renderBorder(g2, formObject, x, y, w, h);
}
public static int renderBorder(Graphics2D g2, FormObject formObject, int x, int y, int w, int h){
//Turn off antialiasing for border and background
final Object antiA = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
//Variables needed for Border
int borderWidth = 0;
Color BC = getBorderColor(formObject);
Color BG = getBorderBackgroundColor(formObject);
boolean borderCreated = false;
//Handle Border BS dictionary
if (formObject.getDictionary(PdfDictionary.BS) != null) {
final PdfObject BS = formObject.getDictionary(PdfDictionary.BS);
final String s = BS.getName(PdfDictionary.S);
borderWidth = BS.getInt(PdfDictionary.W);
if(borderWidth==-1){
borderWidth = 1;
}
if (borderWidth > 0) {//Ignore border is width is 0 or less
//Set to border color
g2.setColor(BC);
if (s == null || s.equals("S")) {
renderBorderSolid(g2, borderWidth, x, y, w, h);
borderCreated = true;
} else if (s.equals("D")) {
final PdfArrayIterator dashPattern = BS.getMixedArray(PdfDictionary.D);
renderBorderDashed(g2, dashPattern, borderWidth, x, y, w, h);
borderCreated = true;
} else if (s.equals("B")) {
renderBorderBeveled(g2, BG, borderWidth, x, y, w, h);
borderCreated = true;
} else if (s.equals("I")) {
renderBorderInset(g2, borderWidth, x, y, w, h);
borderCreated = true;
} else if (s.equals("U")) {
renderBorderUnderline(g2, borderWidth, x, y, w);
borderCreated = true;
}
//Reset to old color
//g2.setColor(old);
}
} else if (formObject.getObjectArray(PdfDictionary.Border) != null) {
//borderCreated = true;
throw new RuntimeException("Border Array not implemented yet");
}
//No border created so use the defaults
if (!borderCreated) {
//Set to border color
g2.setColor(BC);
borderWidth = 1;
g2.setStroke(new BasicStroke(borderWidth));
g2.drawRect((x + borderWidth) - 1,
(y + borderWidth) - 1,
w - (borderWidth * 2)+1,
h - (borderWidth * 2)+1);
}
//Reset Antialiasing for the text
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiA);
return borderWidth;
}
public static FontMetrics renderFont(Graphics2D g2, FormObject formObject, String textValue, int borderWidth) {
//If font size is 0, resize to fit text within area.
if (formObject.getTextSize() <= 0) {
if (!formObject.getFieldFlags()[FormObject.MULTILINE_ID]) {
//Create font for string shorter than form
final float fh = (formObject.getBoundingRectangle().height - (borderWidth * 2)) - 2;
g2.setFont(formObject.getTextFont().deriveFont(fh));
//Create metrics and bounds for text using current font
FontMetrics metrics = g2.getFontMetrics(formObject.getTextFont().deriveFont(fh));
Rectangle2D r = metrics.getStringBounds(textValue, g2);
//Find scaling based on string and form width
float scale = (float) (((formObject.getBoundingRectangle().width - (borderWidth * 2)) - 4) / r.getWidth());
final float hScale = (float) (formObject.getBoundingRectangle().height / r.getHeight());
if (scale > 1.0f && scale > hScale) {
scale = hScale;
}
//Apply scaling to make font needed if scaling down and reset font metrics
if (scale < 1.0f) {
g2.setFont(formObject.getTextFont().deriveFont(fh * scale));
}
} else {
//Sets a default for mutliLine forms as using full form size is incorrect.
g2.setFont(formObject.getTextFont().deriveFont(12.0f));
}
} else {
g2.setFont(formObject.getTextFont());
}
if (formObject.getTextColor() != null) {
g2.setColor(formObject.getTextColor());
} else {
g2.setColor(Color.BLACK);
}
return g2.getFontMetrics();
}
public static void renderComboForms(Graphics2D g2, FormObject formObject, FontMetrics metrics, Rectangle2D r, int borderWidth, int justification, int pageHeight) {
final String[] values = formObject.getItemsList();
if (values != null) {
final int[] selected = formObject.getIntArray(PdfDictionary.I);
int startingIndex = formObject.getInt(PdfDictionary.TI);
if (startingIndex < 0) {
startingIndex = 0;
}
//Add highlighting to form if any
if (selected != null) {
final Color c = g2.getColor();
final Color highlight = new Color(DecoderOptions.highlightColor.getRed() / 255, DecoderOptions.highlightColor.getGreen() / 255, DecoderOptions.highlightColor.getBlue() / 255, DecoderOptions.highlightComposite);
g2.setColor(highlight);
if (formObject.getBoundingRectangle().getHeight() < (metrics.getHeight() * values.length)) {
startingIndex = selected[0];
}
for (int i = 0; i != selected.length; i++) {
final int x = formObject.getBoundingRectangle().x + (borderWidth);
int y = (pageHeight - (formObject.getBoundingRectangle().y + (formObject.getBoundingRectangle().height))) - (borderWidth);
//Text is drawn a font baseline and not the font descent
//Add descent to the coords for the highlight to position correctly.
y += metrics.getDescent();
y += (metrics.getHeight() * (selected[i] - startingIndex));
g2.fillRect(x, y + borderWidth,
formObject.getBoundingRectangle().width - (borderWidth * 2) + 2,
metrics.getHeight());
}
g2.setColor(c);
}
g2.setClip(new Rectangle(formObject.getBoundingRectangle().x + borderWidth - 1,
pageHeight - (formObject.getBoundingRectangle().y + formObject.getBoundingRectangle().height) + borderWidth - 1,
formObject.getBoundingRectangle().width - (borderWidth * 2) + 2,
formObject.getBoundingRectangle().height - (borderWidth * 2) + 2));
for (int i = startingIndex; i != values.length; i++) {
final int x = formObject.getBoundingRectangle().x + (borderWidth) + 2;
int y = ((pageHeight - (formObject.getBoundingRectangle().y + (formObject.getBoundingRectangle().height))) + (borderWidth) + (metrics.getHeight())) - borderWidth;
y += (metrics.getHeight() * (i - startingIndex));
renderTextString(g2, formObject, values[i], r, x, y, borderWidth, justification);
}
}
}
public static void renderMultilineTextField(Graphics2D g2, FormObject formObject, FontMetrics metrics, Rectangle2D r, String textValue, int borderWidth, int justification, int pageHeight) {
final int x = formObject.getBoundingRectangle().x + (borderWidth);
int y = (pageHeight - (formObject.getBoundingRectangle().y + (formObject.getBoundingRectangle().height))) - (borderWidth) - borderWidth;
final StringTokenizer tokenizer = new StringTokenizer(textValue, "\n");
while (tokenizer.hasMoreTokens()) {
y += metrics.getHeight();
renderTextString(g2, formObject, tokenizer.nextToken(), r, x, y, borderWidth, justification);
}
}
public static void renderSingleLineTextField(Graphics2D g2, FormObject formObject, FontMetrics metrics, Rectangle2D r, String textValue, int borderWidth, int justification, int pageHeight){
final int x = formObject.getBoundingRectangle().x + (borderWidth);
final int y = (pageHeight - (formObject.getBoundingRectangle().y)) - (formObject.getBoundingRectangle().height - metrics.getHeight());
renderTextString(g2, formObject, textValue, r, x, y, borderWidth, justification);
}
private static void renderTextString(Graphics2D g2, FormObject formObject, String textValue, Rectangle2D r, int x, int y, int borderWidth, int justification){
switch (justification) {
case 0: //JTextField.CENTER
g2.drawString(textValue, (int) (x + ((formObject.getBoundingRectangle().width - (borderWidth * 2) - r.getWidth()) / 2)), y);
break;
case 4: //JTextField.RIGHT
g2.drawString(textValue, (int) (x + formObject.getBoundingRectangle().width - (borderWidth * 2) - r.getWidth()) - 2, y);
break;
default:
g2.drawString(textValue, x + 2, y);
break;
}
}
public static void renderQuadPoint(Graphics2D g2, FormObject formObject, Color bgColor, int pageHeight){
final float[] quadPoints = formObject.getFloatArray(PdfDictionary.QuadPoints);
if (quadPoints != null) {
final Color c = g2.getColor();
final Composite com = g2.getComposite();
//Loop through values as have found cases where coords are stored in incorrect order.
for (int i = 0; i != quadPoints.length / 8; i++) {
float minX = 0;
float minY = 0;
float maxX = 0;
float maxY = 0;
for (int j = 0; j != 8; j++) {
if (j < 2) {
if (j % 2 == 0) { //x coord
minX = quadPoints[(i * 8) + j];
maxX = quadPoints[(i * 8) + j];
} else { //y coord
minY = quadPoints[(i * 8) + j];
maxY = quadPoints[(i * 8) + j];
}
} else {
if (j % 2 == 0) { //x coord
if (quadPoints[(i * 8) + j] < minX) {
minX = quadPoints[(i * 8) + j];
}
if (quadPoints[(i * 8) + j] > maxX) {
maxX = quadPoints[(i * 8) + j];
}
} else { //y coord
if (quadPoints[(i * 8) + j] < minY) {
minY = quadPoints[(i * 8) + j];
}
if (quadPoints[(i * 8) + j] > maxY) {
maxY = quadPoints[(i * 8) + j];
}
}
}
}
if (formObject.getParameterConstant(PdfDictionary.Subtype) == PdfDictionary.Highlight) {
g2.setColor(bgColor);
g2.setComposite(new BlendMode(PdfDictionary.Multiply, 0.5f));
g2.fillRect((int) minX, (int) (pageHeight - maxY), (int) (maxX - minX), (int) (maxY - minY));
} else { //Draw for link
}
g2.setColor(c);
g2.setComposite(com);
}
}
}
public static void renderPopupWindow(Graphics2D g2, FormObject formObject, Color bgColor, boolean isPrinting, int pageHeight) {
//read in date for title bar
final String mStream = formObject.getParentPdfObj().getTextStreamValue(PdfDictionary.M);
StringBuffer date = null;
if (mStream != null) {
date = new StringBuffer(mStream);
date.delete(0, 2);//delete D:
date.insert(10, ':');
date.insert(13, ':');
date.insert(16, ' ');
final String year = date.substring(0, 4);
final String day = date.substring(6, 8);
date.delete(6, 8);
date.delete(0, 4);
date.insert(0, day);
date.insert(4, year);
date.insert(2, '/');
date.insert(5, '/');
date.insert(10, ' ');
//date.delete(19, date.length());//delete the +01'00' Time zone definition
}
//setup title text for popup
final String subject = formObject.getParentPdfObj().getTextStreamValue(PdfDictionary.Subj);
String popupTitle = formObject.getParentPdfObj().getTextStreamValue(PdfDictionary.T);
if (popupTitle == null) {
popupTitle = "";
}
String title = "";
if (subject != null) {
title += subject + '\t';
}
if (date != null) {
title += date;
}
title += '\n' + popupTitle;
String contents = formObject.getParentPdfObj().getTextStreamValue(PdfDictionary.Contents);
if (contents == null) {
contents = "";
}
if (contents.indexOf('\r') != -1) {
contents = contents.replaceAll("\r", "\n");
}
final float[] rect = formObject.getFloatArray(PdfDictionary.Rect);
final int titleBarHeight = 24;
if (isPrinting) {
g2.translate(0, titleBarHeight);
}
// //Add Border, useful for debugging
// g2.setColor(Color.BLACK);
// g2.drawRect((int)(rect[0]), (int)((pageData.getCropBoxHeight(page)-(int)rect[3])), (int)((rect[2]-rect[0])), (int)((rect[3]-rect[1])));
g2.setColor(Color.WHITE);
g2.fillRect((int) (rect[0]), (pageHeight - (int) rect[3]), (int) ((rect[2] - rect[0])), (int) ((rect[3] - rect[1])));
g2.setColor(bgColor);
g2.fillRect((int) (rect[0]), (pageHeight - (int) rect[3]), (int) ((rect[2] - rect[0])), titleBarHeight + 2);
g2.setColor(Color.BLACK);
g2.setFont(new Font("Monospaced", Font.PLAIN, 12));
//Title Font size set
g2.setFont(g2.getFont().deriveFont(8.0f));
if (title.indexOf('\n') == -1) {
g2.drawString(title, (int) (rect[0]) + 2, (int) (((pageHeight - (int) rect[3])) + g2.getFont().getSize2D()) + 2);
} else {
final StringTokenizer tokenizer = new StringTokenizer(title, "\n", true);
int lineCount = 1;
while (tokenizer.hasMoreTokens()) {
final String t = tokenizer.nextToken();
if (!t.equals("\n")) {
g2.drawString(t, (int) (rect[0]) + 2, (int) (((pageHeight - (int) rect[3])) + ((g2.getFont().getSize2D() + 2) * lineCount)));
} else {
lineCount++;
}
}
}
//Content Font size set
g2.setFont(g2.getFont().deriveFont(7.0f));
if (contents.indexOf('\n') == -1) {
g2.drawString(contents, (int) (rect[0]), (int) (((pageHeight - (int) rect[3])) + g2.getFont().getSize2D() + titleBarHeight));
} else {
final StringTokenizer tokenizer = new StringTokenizer(contents, "\n", true);
int lineCount = 1;
while (tokenizer.hasMoreTokens()) {
final String t = tokenizer.nextToken();
if (!t.equals("\n")) {
g2.drawString(t, (int) (rect[0]) + 2, (int) (((pageHeight - (int) rect[3])) + ((g2.getFont().getSize2D() + 3) * lineCount) + titleBarHeight));
} else {
lineCount++;
}
}
}
}
}