1815 lines
54 KiB
Java
1815 lines
54 KiB
Java
|
/**
|
||
|
* Page.java
|
||
|
*
|
||
|
Copyright (c) 2018, Innovatics Inc.
|
||
|
All rights reserved.
|
||
|
|
||
|
Redistribution and use in source and binary forms, with or without modification,
|
||
|
are permitted provided that the following conditions are met:
|
||
|
|
||
|
* Redistributions of source code must retain the above copyright notice,
|
||
|
this list of conditions and the following disclaimer.
|
||
|
|
||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||
|
this list of conditions and the following disclaimer in the documentation
|
||
|
and / or other materials provided with the distribution.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
package com.pdfjet;
|
||
|
|
||
|
import java.io.*;
|
||
|
import java.text.*;
|
||
|
import java.util.*;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Used to create PDF page objects.
|
||
|
*
|
||
|
* Please note:
|
||
|
* <pre>
|
||
|
* The coordinate (0f, 0f) is the top left corner of the page.
|
||
|
* The size of the pages are represented in points.
|
||
|
* 1 point is 1/72 inches.
|
||
|
* </pre>
|
||
|
*
|
||
|
*/
|
||
|
public class Page {
|
||
|
|
||
|
protected PDF pdf;
|
||
|
protected PDFobj pageObj;
|
||
|
protected int objNumber;
|
||
|
protected ByteArrayOutputStream buf;
|
||
|
protected float[] tm = new float[] {1f, 0f, 0f, 1f};
|
||
|
protected int renderingMode = 0;
|
||
|
protected float width;
|
||
|
protected float height;
|
||
|
protected List<Integer> contents;
|
||
|
protected List<Annotation> annots;
|
||
|
protected List<Destination> destinations;
|
||
|
protected float[] cropBox = null;
|
||
|
protected float[] bleedBox = null;
|
||
|
protected float[] trimBox = null;
|
||
|
protected float[] artBox = null;
|
||
|
protected List<StructElem> structures = new ArrayList<StructElem>();
|
||
|
|
||
|
private float[] pen = {0f, 0f, 0f};
|
||
|
private float[] brush = {0f, 0f, 0f};
|
||
|
private float pen_width = -1.0f;
|
||
|
private int line_cap_style = 0;
|
||
|
private int line_join_style = 0;
|
||
|
private String linePattern = "[] 0";
|
||
|
private Font font;
|
||
|
private List<State> savedStates = new ArrayList<State>();
|
||
|
private int mcid = 0;
|
||
|
|
||
|
protected float savedHeight = Float.MAX_VALUE;
|
||
|
|
||
|
/*
|
||
|
* From Android's Matrix object:
|
||
|
*/
|
||
|
public static final int MSCALE_X = 0;
|
||
|
public static final int MSKEW_X = 1;
|
||
|
public static final int MTRANS_X = 2;
|
||
|
public static final int MSKEW_Y = 3;
|
||
|
public static final int MSCALE_Y = 4;
|
||
|
public static final int MTRANS_Y = 5;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Creates page object and add it to the PDF document.
|
||
|
*
|
||
|
* Please note:
|
||
|
* <pre>
|
||
|
* The coordinate (0f, 0f) is the top left corner of the page.
|
||
|
* The size of the pages are represented in points.
|
||
|
* 1 point is 1/72 inches.
|
||
|
* </pre>
|
||
|
*
|
||
|
* @param pdf the pdf object.
|
||
|
* @param pageSize the page size of this page.
|
||
|
*/
|
||
|
public Page(PDF pdf, float[] pageSize) throws Exception {
|
||
|
this(pdf, pageSize, true);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Creates page object and add it to the PDF document.
|
||
|
*
|
||
|
* Please note:
|
||
|
* <pre>
|
||
|
* The coordinate (0f, 0f) is the top left corner of the page.
|
||
|
* The size of the pages are represented in points.
|
||
|
* 1 point is 1/72 inches.
|
||
|
* </pre>
|
||
|
*
|
||
|
* @param pdf the pdf object.
|
||
|
* @param pageSize the page size of this page.
|
||
|
* @param addPageToPDF boolean flag.
|
||
|
*/
|
||
|
public Page(PDF pdf, float[] pageSize, boolean addPageToPDF) throws Exception {
|
||
|
this.pdf = pdf;
|
||
|
contents = new ArrayList<Integer>();
|
||
|
annots = new ArrayList<Annotation>();
|
||
|
destinations = new ArrayList<Destination>();
|
||
|
width = pageSize[0];
|
||
|
height = pageSize[1];
|
||
|
buf = new ByteArrayOutputStream(8192);
|
||
|
if (addPageToPDF) {
|
||
|
pdf.addPage(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
public Page(PDF pdf, PDFobj pageObj) throws Exception {
|
||
|
this.pdf = pdf;
|
||
|
this.pageObj = pageObj;
|
||
|
width = pageObj.getPageSize()[0];
|
||
|
height = pageObj.getPageSize()[1];
|
||
|
buf = new ByteArrayOutputStream(8192);
|
||
|
append("Q\n");
|
||
|
append("q\n");
|
||
|
if (pageObj.gsNumber != -1) {
|
||
|
append("/GS");
|
||
|
append(pageObj.gsNumber + 1);
|
||
|
append(" gs\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
public Font addResource(CoreFont coreFont, Map<Integer, PDFobj> objects) {
|
||
|
return pageObj.addResource(coreFont, objects);
|
||
|
}
|
||
|
|
||
|
|
||
|
public void addResource(Image image, Map<Integer, PDFobj> objects) {
|
||
|
pageObj.addResource(image, objects);
|
||
|
}
|
||
|
|
||
|
|
||
|
public void addResource(Font font, Map<Integer, PDFobj> objects) {
|
||
|
pageObj.addResource(font, objects);
|
||
|
}
|
||
|
|
||
|
|
||
|
public void complete(Map<Integer, PDFobj> objects) throws Exception {
|
||
|
append("Q\n");
|
||
|
pageObj.addContent(getContent(), objects);
|
||
|
}
|
||
|
|
||
|
|
||
|
public byte[] getContent() {
|
||
|
return buf.toByteArray();
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Adds destination to this page.
|
||
|
*
|
||
|
* @param name The destination name.
|
||
|
* @param yPosition The vertical position of the destination on this page.
|
||
|
*
|
||
|
* @return the destination.
|
||
|
*/
|
||
|
public Destination addDestination(String name, float yPosition) {
|
||
|
Destination dest = new Destination(name, height - yPosition);
|
||
|
destinations.add(dest);
|
||
|
return dest;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Returns the width of this page.
|
||
|
*
|
||
|
* @return the width of the page.
|
||
|
*/
|
||
|
public float getWidth() {
|
||
|
return width;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Returns the height of this page.
|
||
|
*
|
||
|
* @return the height of the page.
|
||
|
*/
|
||
|
public float getHeight() {
|
||
|
return height;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws a line on the page, using the current color, between the points (x1, y1) and (x2, y2).
|
||
|
*
|
||
|
* @param x1 the first point's x coordinate.
|
||
|
* @param y1 the first point's y coordinate.
|
||
|
* @param x2 the second point's x coordinate.
|
||
|
* @param y2 the second point's y coordinate.
|
||
|
*/
|
||
|
public void drawLine(
|
||
|
double x1,
|
||
|
double y1,
|
||
|
double x2,
|
||
|
double y2) throws IOException {
|
||
|
drawLine((float) x1, (float) y1, (float) x2, (float) y2);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws a line on the page, using the current color, between the points (x1, y1) and (x2, y2).
|
||
|
*
|
||
|
* @param x1 the first point's x coordinate.
|
||
|
* @param y1 the first point's y coordinate.
|
||
|
* @param x2 the second point's x coordinate.
|
||
|
* @param y2 the second point's y coordinate.
|
||
|
*/
|
||
|
public void drawLine(
|
||
|
float x1,
|
||
|
float y1,
|
||
|
float x2,
|
||
|
float y2) throws IOException {
|
||
|
moveTo(x1, y1);
|
||
|
lineTo(x2, y2);
|
||
|
strokePath();
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws the text given by the specified string,
|
||
|
* using the specified main font and the current brush color.
|
||
|
* If the main font is missing some glyphs - the fallback font is used.
|
||
|
* The baseline of the leftmost character is at position (x, y) on the page.
|
||
|
*
|
||
|
* @param font1 the main font.
|
||
|
* @param font2 the fallback font.
|
||
|
* @param str the string to be drawn.
|
||
|
* @param x the x coordinate.
|
||
|
* @param y the y coordinate.
|
||
|
*/
|
||
|
public void drawString(
|
||
|
Font font1,
|
||
|
Font font2,
|
||
|
String str,
|
||
|
float x,
|
||
|
float y) throws IOException {
|
||
|
if (font2 == null) {
|
||
|
drawString(font1, str, x, y);
|
||
|
}
|
||
|
else {
|
||
|
Font activeFont = font1;
|
||
|
StringBuilder buf = new StringBuilder();
|
||
|
for (int i = 0; i < str.length(); i++) {
|
||
|
int ch = str.charAt(i);
|
||
|
if ((font1.isCJK && ch >= 0x4E00 && ch <= 0x9FCC)
|
||
|
|| (!font1.isCJK && font1.unicodeToGID[ch] != 0)) {
|
||
|
if (font1 != activeFont) {
|
||
|
String str2 = buf.toString();
|
||
|
drawString(activeFont, str2, x, y);
|
||
|
x += activeFont.stringWidth(str2);
|
||
|
buf.setLength(0);
|
||
|
activeFont = font1;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (font2 != activeFont) {
|
||
|
String str2 = buf.toString();
|
||
|
drawString(activeFont, str2, x, y);
|
||
|
x += activeFont.stringWidth(str2);
|
||
|
buf.setLength(0);
|
||
|
activeFont = font2;
|
||
|
}
|
||
|
}
|
||
|
buf.append((char) ch);
|
||
|
}
|
||
|
drawString(activeFont, buf.toString(), x, y);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws the text given by the specified string,
|
||
|
* using the specified font and the current brush color.
|
||
|
* The baseline of the leftmost character is at position (x, y) on the page.
|
||
|
*
|
||
|
* @param font the font to use.
|
||
|
* @param str the string to be drawn.
|
||
|
* @param x the x coordinate.
|
||
|
* @param y the y coordinate.
|
||
|
*/
|
||
|
public void drawString(
|
||
|
Font font,
|
||
|
String str,
|
||
|
double x,
|
||
|
double y) throws IOException {
|
||
|
drawString(font, str, (float) x, (float) y);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws the text given by the specified string,
|
||
|
* using the specified font and the current brush color.
|
||
|
* The baseline of the leftmost character is at position (x, y) on the page.
|
||
|
*
|
||
|
* @param font the font to use.
|
||
|
* @param str the string to be drawn.
|
||
|
* @param x the x coordinate.
|
||
|
* @param y the y coordinate.
|
||
|
*/
|
||
|
public void drawString(
|
||
|
Font font,
|
||
|
String str,
|
||
|
float x,
|
||
|
float y) throws IOException {
|
||
|
|
||
|
if (str == null || str.equals("")) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
append("BT\n");
|
||
|
|
||
|
if (font.fontID == null) {
|
||
|
setTextFont(font);
|
||
|
}
|
||
|
else {
|
||
|
append('/');
|
||
|
append(font.fontID);
|
||
|
append(' ');
|
||
|
append(font.size);
|
||
|
append(" Tf\n");
|
||
|
}
|
||
|
|
||
|
if (renderingMode != 0) {
|
||
|
append(renderingMode);
|
||
|
append(" Tr\n");
|
||
|
}
|
||
|
|
||
|
float skew = 0f;
|
||
|
if (font.skew15 &&
|
||
|
tm[0] == 1f &&
|
||
|
tm[1] == 0f &&
|
||
|
tm[2] == 0f &&
|
||
|
tm[3] == 1f) {
|
||
|
skew = 0.26f;
|
||
|
}
|
||
|
|
||
|
append(tm[0]);
|
||
|
append(' ');
|
||
|
append(tm[1]);
|
||
|
append(' ');
|
||
|
append(tm[2] + skew);
|
||
|
append(' ');
|
||
|
append(tm[3]);
|
||
|
append(' ');
|
||
|
append(x);
|
||
|
append(' ');
|
||
|
append(height - y);
|
||
|
append(" Tm\n");
|
||
|
|
||
|
append("[<");
|
||
|
drawString(font, str);
|
||
|
append(">] TJ\n");
|
||
|
|
||
|
append("ET\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
private void drawString(Font font, String str) throws IOException {
|
||
|
int len = str.length();
|
||
|
for (int i = 0; i < len; i++) {
|
||
|
if (font.isCoreFont) {
|
||
|
drawOneByteChar(str.charAt(i), font, str, i);
|
||
|
}
|
||
|
else {
|
||
|
drawTwoByteChar(str.charAt(i), font);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private void drawOneByteChar(
|
||
|
int c1, Font font, String str, int i) throws IOException {
|
||
|
if (c1 < font.firstChar || c1 > font.lastChar) {
|
||
|
append(String.format("%02X", 0x20));
|
||
|
return;
|
||
|
}
|
||
|
append(String.format("%02X", c1));
|
||
|
|
||
|
if (font.isCoreFont && font.kernPairs && i < (str.length() - 1)) {
|
||
|
c1 -= 32;
|
||
|
int c2 = str.charAt(i + 1);
|
||
|
if (c2 < font.firstChar || c2 > font.lastChar) {
|
||
|
c2 = 32;
|
||
|
}
|
||
|
for (int j = 2; j < font.metrics[c1].length; j += 2) {
|
||
|
if (font.metrics[c1][j] == c2) {
|
||
|
append(">");
|
||
|
append(-font.metrics[c1][j + 1]);
|
||
|
append("<");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
private void drawTwoByteChar(int c1, Font font) throws IOException {
|
||
|
if (c1 < font.firstChar || c1 > font.lastChar) {
|
||
|
if (font.isCJK) {
|
||
|
append(String.format("%04X", 0x0020));
|
||
|
}
|
||
|
else {
|
||
|
append(String.format("%04X", font.unicodeToGID[0x0020]));
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (font.isCJK) {
|
||
|
append(String.format("%04X", c1));
|
||
|
}
|
||
|
else {
|
||
|
append(String.format("%04X", font.unicodeToGID[c1]));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the graphics state. Please see Example_31.
|
||
|
*
|
||
|
* @param gs the graphics state to use.
|
||
|
*/
|
||
|
public void setGraphicsState(GraphicsState gs) throws IOException {
|
||
|
StringBuilder buf = new StringBuilder();
|
||
|
buf.append("/CA ");
|
||
|
buf.append(gs.get_CA());
|
||
|
buf.append(" ");
|
||
|
buf.append("/ca ");
|
||
|
buf.append(gs.get_ca());
|
||
|
setGraphicsState(buf.toString());
|
||
|
}
|
||
|
|
||
|
|
||
|
// String state = "/CA 0.5 /ca 0.5";
|
||
|
private void setGraphicsState(String state) throws IOException {
|
||
|
Integer n = pdf.states.get(state);
|
||
|
if (n == null) {
|
||
|
n = pdf.states.size() + 1;
|
||
|
pdf.states.put(state, n);
|
||
|
}
|
||
|
append("/GS");
|
||
|
append(n);
|
||
|
append(" gs\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the color for stroking operations.
|
||
|
* The pen color is used when drawing lines and splines.
|
||
|
*
|
||
|
* @param r the red component is float value from 0.0 to 1.0.
|
||
|
* @param g the green component is float value from 0.0 to 1.0.
|
||
|
* @param b the blue component is float value from 0.0 to 1.0.
|
||
|
*/
|
||
|
public void setPenColor(
|
||
|
double r, double g, double b) throws IOException {
|
||
|
setPenColor((float) r, (float) g, (float) b);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the color for stroking operations.
|
||
|
* The pen color is used when drawing lines and splines.
|
||
|
*
|
||
|
* @param r the red component is float value from 0.0f to 1.0f.
|
||
|
* @param g the green component is float value from 0.0f to 1.0f.
|
||
|
* @param b the blue component is float value from 0.0f to 1.0f.
|
||
|
*/
|
||
|
public void setPenColor(
|
||
|
float r, float g, float b) throws IOException {
|
||
|
if (pen[0] != r ||
|
||
|
pen[1] != g ||
|
||
|
pen[2] != b) {
|
||
|
setColor(r, g, b);
|
||
|
append(" RG\n");
|
||
|
pen[0] = r;
|
||
|
pen[1] = g;
|
||
|
pen[2] = b;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the color for brush operations.
|
||
|
* This is the color used when drawing regular text and filling shapes.
|
||
|
*
|
||
|
* @param r the red component is float value from 0.0 to 1.0.
|
||
|
* @param g the green component is float value from 0.0 to 1.0.
|
||
|
* @param b the blue component is float value from 0.0 to 1.0.
|
||
|
*/
|
||
|
public void setBrushColor(
|
||
|
double r, double g, double b) throws IOException {
|
||
|
setBrushColor((float) r, (float) g, (float) b);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the color for brush operations.
|
||
|
* This is the color used when drawing regular text and filling shapes.
|
||
|
*
|
||
|
* @param r the red component is float value from 0.0f to 1.0f.
|
||
|
* @param g the green component is float value from 0.0f to 1.0f.
|
||
|
* @param b the blue component is float value from 0.0f to 1.0f.
|
||
|
*/
|
||
|
public void setBrushColor(
|
||
|
float r, float g, float b) throws IOException {
|
||
|
if (brush[0] != r ||
|
||
|
brush[1] != g ||
|
||
|
brush[2] != b) {
|
||
|
setColor(r, g, b);
|
||
|
append(" rg\n");
|
||
|
brush[0] = r;
|
||
|
brush[1] = g;
|
||
|
brush[2] = b;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the color for brush operations.
|
||
|
*
|
||
|
* @param color the color.
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
public void setBrushColor(float[] color) throws IOException {
|
||
|
setBrushColor(color[0], color[1], color[2]);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Returns the brush color.
|
||
|
*
|
||
|
* @return the brush color.
|
||
|
*/
|
||
|
public float[] getBrushColor() {
|
||
|
return brush;
|
||
|
}
|
||
|
|
||
|
|
||
|
private void setColor(
|
||
|
float r, float g, float b) throws IOException {
|
||
|
append(r);
|
||
|
append(' ');
|
||
|
append(g);
|
||
|
append(' ');
|
||
|
append(b);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the pen color.
|
||
|
*
|
||
|
* @param color the color. See the Color class for predefined values or define your own using 0x00RRGGBB packed integers.
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
public void setPenColor(int color) throws IOException {
|
||
|
float r = ((color >> 16) & 0xff)/255f;
|
||
|
float g = ((color >> 8) & 0xff)/255f;
|
||
|
float b = ((color) & 0xff)/255f;
|
||
|
setPenColor(r, g, b);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the brush color.
|
||
|
*
|
||
|
* @param color the color. See the Color class for predefined values or define your own using 0x00RRGGBB packed integers.
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
public void setBrushColor(int color) throws IOException {
|
||
|
float r = ((color >> 16) & 0xff)/255f;
|
||
|
float g = ((color >> 8) & 0xff)/255f;
|
||
|
float b = ((color) & 0xff)/255f;
|
||
|
setBrushColor(r, g, b);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the line width to the default.
|
||
|
* The default is the finest line width.
|
||
|
*/
|
||
|
public void setDefaultLineWidth() throws IOException {
|
||
|
if (pen_width != 0f) {
|
||
|
pen_width = 0f;
|
||
|
append(pen_width);
|
||
|
append(" w\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
|
||
|
* It is specified by a dash array and a dash phase.
|
||
|
* The elements of the dash array are positive numbers that specify the lengths of
|
||
|
* alternating dashes and gaps.
|
||
|
* The dash phase specifies the distance into the dash pattern at which to start the dash.
|
||
|
* The elements of both the dash array and the dash phase are expressed in user space units.
|
||
|
* <pre>
|
||
|
* Examples of line dash patterns:
|
||
|
*
|
||
|
* "[Array] Phase" Appearance Description
|
||
|
* _______________ _________________ ____________________________________
|
||
|
*
|
||
|
* "[] 0" ----------------- Solid line
|
||
|
* "[3] 0" --- --- --- 3 units on, 3 units off, ...
|
||
|
* "[2] 1" - -- -- -- -- 1 on, 2 off, 2 on, 2 off, ...
|
||
|
* "[2 1] 0" -- -- -- -- -- -- 2 on, 1 off, 2 on, 1 off, ...
|
||
|
* "[3 5] 6" --- --- 2 off, 3 on, 5 off, 3 on, 5 off, ...
|
||
|
* "[2 3] 11" - -- -- -- 1 on, 3 off, 2 on, 3 off, 2 on, ...
|
||
|
* </pre>
|
||
|
*
|
||
|
* @param pattern the line dash pattern.
|
||
|
*/
|
||
|
public void setLinePattern(String pattern) throws IOException {
|
||
|
if (!pattern.equals(linePattern)) {
|
||
|
linePattern = pattern;
|
||
|
append(linePattern);
|
||
|
append(" d\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the default line dash pattern - solid line.
|
||
|
*/
|
||
|
public void setDefaultLinePattern() throws IOException {
|
||
|
append("[] 0");
|
||
|
append(" d\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the pen width that will be used to draw lines and splines on this page.
|
||
|
*
|
||
|
* @param width the pen width.
|
||
|
*/
|
||
|
public void setPenWidth(double width) throws IOException {
|
||
|
setPenWidth((float) width);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the pen width that will be used to draw lines and splines on this page.
|
||
|
*
|
||
|
* @param width the pen width.
|
||
|
*/
|
||
|
public void setPenWidth(float width) throws IOException {
|
||
|
if (pen_width != width) {
|
||
|
pen_width = width;
|
||
|
append(pen_width);
|
||
|
append(" w\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the current line cap style.
|
||
|
*
|
||
|
* @param style the cap style of the current line. Supported values: Cap.BUTT, Cap.ROUND and Cap.PROJECTING_SQUARE
|
||
|
*/
|
||
|
public void setLineCapStyle(int style) throws IOException {
|
||
|
if (line_cap_style != style) {
|
||
|
line_cap_style = style;
|
||
|
append(line_cap_style);
|
||
|
append(" J\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the line join style.
|
||
|
*
|
||
|
* @param style the line join style code. Supported values: Join.MITER, Join.ROUND and Join.BEVEL
|
||
|
*/
|
||
|
public void setLineJoinStyle(int style) throws IOException {
|
||
|
if (line_join_style != style) {
|
||
|
line_join_style = style;
|
||
|
append(line_join_style);
|
||
|
append(" j\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Moves the pen to the point with coordinates (x, y) on the page.
|
||
|
*
|
||
|
* @param x the x coordinate of new pen position.
|
||
|
* @param y the y coordinate of new pen position.
|
||
|
*/
|
||
|
public void moveTo(double x, double y) throws IOException {
|
||
|
moveTo((float) x, (float) y);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Moves the pen to the point with coordinates (x, y) on the page.
|
||
|
*
|
||
|
* @param x the x coordinate of new pen position.
|
||
|
* @param y the y coordinate of new pen position.
|
||
|
*/
|
||
|
public void moveTo(float x, float y) throws IOException {
|
||
|
append(x);
|
||
|
append(' ');
|
||
|
append(height - y);
|
||
|
append(" m\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws a line from the current pen position to the point with coordinates (x, y),
|
||
|
* using the current pen width and stroke color.
|
||
|
* Make sure you call strokePath(), closePath() or fillPath() after the last call to this method.
|
||
|
*/
|
||
|
public void lineTo(double x, double y) throws IOException {
|
||
|
lineTo((float) x, (float) y);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws a line from the current pen position to the point with coordinates (x, y),
|
||
|
* using the current pen width and stroke color.
|
||
|
* Make sure you call strokePath(), closePath() or fillPath() after the last call to this method.
|
||
|
*/
|
||
|
public void lineTo(float x, float y) throws IOException {
|
||
|
append(x);
|
||
|
append(' ');
|
||
|
append(height - y);
|
||
|
append(" l\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws the path using the current pen color.
|
||
|
*/
|
||
|
public void strokePath() throws IOException {
|
||
|
append("S\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Closes the path and draws it using the current pen color.
|
||
|
*/
|
||
|
public void closePath() throws IOException {
|
||
|
append("s\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Closes and fills the path with the current brush color.
|
||
|
*/
|
||
|
public void fillPath() throws IOException {
|
||
|
append("f\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws the outline of the specified rectangle on the page.
|
||
|
* The left and right edges of the rectangle are at x and x + w.
|
||
|
* The top and bottom edges are at y and y + h.
|
||
|
* The rectangle is drawn using the current pen color.
|
||
|
*
|
||
|
* @param x the x coordinate of the rectangle to be drawn.
|
||
|
* @param y the y coordinate of the rectangle to be drawn.
|
||
|
* @param w the width of the rectangle to be drawn.
|
||
|
* @param h the height of the rectangle to be drawn.
|
||
|
*/
|
||
|
public void drawRect(double x, double y, double w, double h)
|
||
|
throws IOException {
|
||
|
drawRect((float) x, (float) y, (float) w, (float) h);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws the outline of the specified rectangle on the page.
|
||
|
* The left and right edges of the rectangle are at x and x + w.
|
||
|
* The top and bottom edges are at y and y + h.
|
||
|
* The rectangle is drawn using the current pen color.
|
||
|
*
|
||
|
* @param x the x coordinate of the rectangle to be drawn.
|
||
|
* @param y the y coordinate of the rectangle to be drawn.
|
||
|
* @param w the width of the rectangle to be drawn.
|
||
|
* @param h the height of the rectangle to be drawn.
|
||
|
*/
|
||
|
public void drawRect(float x, float y, float w, float h)
|
||
|
throws IOException {
|
||
|
moveTo(x, y);
|
||
|
lineTo(x+w, y);
|
||
|
lineTo(x+w, y+h);
|
||
|
lineTo(x, y+h);
|
||
|
closePath();
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Fills the specified rectangle on the page.
|
||
|
* The left and right edges of the rectangle are at x and x + w.
|
||
|
* The top and bottom edges are at y and y + h.
|
||
|
* The rectangle is drawn using the current pen color.
|
||
|
*
|
||
|
* @param x the x coordinate of the rectangle to be drawn.
|
||
|
* @param y the y coordinate of the rectangle to be drawn.
|
||
|
* @param w the width of the rectangle to be drawn.
|
||
|
* @param h the height of the rectangle to be drawn.
|
||
|
*/
|
||
|
public void fillRect(double x, double y, double w, double h)
|
||
|
throws IOException {
|
||
|
fillRect((float) x, (float) y, (float) w, (float) h);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Fills the specified rectangle on the page.
|
||
|
* The left and right edges of the rectangle are at x and x + w.
|
||
|
* The top and bottom edges are at y and y + h.
|
||
|
* The rectangle is drawn using the current pen color.
|
||
|
*
|
||
|
* @param x the x coordinate of the rectangle to be drawn.
|
||
|
* @param y the y coordinate of the rectangle to be drawn.
|
||
|
* @param w the width of the rectangle to be drawn.
|
||
|
* @param h the height of the rectangle to be drawn.
|
||
|
*/
|
||
|
public void fillRect(float x, float y, float w, float h)
|
||
|
throws IOException {
|
||
|
moveTo(x, y);
|
||
|
lineTo(x+w, y);
|
||
|
lineTo(x+w, y+h);
|
||
|
lineTo(x, y+h);
|
||
|
fillPath();
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws or fills the specified path using the current pen or brush.
|
||
|
*
|
||
|
* @param path the path.
|
||
|
* @param operation specifies 'stroke' or 'fill' operation.
|
||
|
*/
|
||
|
public void drawPath(
|
||
|
List<Point> path, char operation) throws Exception {
|
||
|
if (path.size() < 2) {
|
||
|
throw new Exception(
|
||
|
"The Path object must contain at least 2 points");
|
||
|
}
|
||
|
Point point = path.get(0);
|
||
|
moveTo(point.x, point.y);
|
||
|
boolean curve = false;
|
||
|
for (int i = 1; i < path.size(); i++) {
|
||
|
point = path.get(i);
|
||
|
if (point.isControlPoint) {
|
||
|
curve = true;
|
||
|
append(point);
|
||
|
}
|
||
|
else {
|
||
|
if (curve) {
|
||
|
curve = false;
|
||
|
append(point);
|
||
|
append("c\n");
|
||
|
}
|
||
|
else {
|
||
|
lineTo(point.x, point.y);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
append(operation);
|
||
|
append('\n');
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws a circle on the page.
|
||
|
*
|
||
|
* The outline of the circle is drawn using the current pen color.
|
||
|
*
|
||
|
* @param x the x coordinate of the center of the circle to be drawn.
|
||
|
* @param y the y coordinate of the center of the circle to be drawn.
|
||
|
* @param r the radius of the circle to be drawn.
|
||
|
*/
|
||
|
public void drawCircle(
|
||
|
double x,
|
||
|
double y,
|
||
|
double r) throws Exception {
|
||
|
drawEllipse((float) x, (float) y, (float) r, (float) r, Operation.STROKE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws a circle on the page.
|
||
|
*
|
||
|
* The outline of the circle is drawn using the current pen color.
|
||
|
*
|
||
|
* @param x the x coordinate of the center of the circle to be drawn.
|
||
|
* @param y the y coordinate of the center of the circle to be drawn.
|
||
|
* @param r the radius of the circle to be drawn.
|
||
|
*/
|
||
|
public void drawCircle(
|
||
|
float x,
|
||
|
float y,
|
||
|
float r) throws Exception {
|
||
|
drawEllipse(x, y, r, r, Operation.STROKE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws the specified circle on the page and fills it with the current brush color.
|
||
|
*
|
||
|
* @param x the x coordinate of the center of the circle to be drawn.
|
||
|
* @param y the y coordinate of the center of the circle to be drawn.
|
||
|
* @param r the radius of the circle to be drawn.
|
||
|
* @param operation must be Operation.STROKE, Operation.CLOSE or Operation.FILL.
|
||
|
*/
|
||
|
public void drawCircle(
|
||
|
double x,
|
||
|
double y,
|
||
|
double r,
|
||
|
char operation) throws Exception {
|
||
|
drawEllipse((float) x, (float) y, (float) r, (float) r, operation);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws the specified circle on the page and fills it with the current brush color.
|
||
|
*
|
||
|
* @param x the x coordinate of the center of the circle to be drawn.
|
||
|
* @param y the y coordinate of the center of the circle to be drawn.
|
||
|
* @param r the radius of the circle to be drawn.
|
||
|
* @param operation must be Operation.STROKE, Operation.CLOSE or Operation.FILL.
|
||
|
*/
|
||
|
public void drawCircle(
|
||
|
float x,
|
||
|
float y,
|
||
|
float r,
|
||
|
char operation) throws Exception {
|
||
|
drawEllipse(x, y, r, r, operation);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws an ellipse on the page using the current pen color.
|
||
|
*
|
||
|
* @param x the x coordinate of the center of the ellipse to be drawn.
|
||
|
* @param y the y coordinate of the center of the ellipse to be drawn.
|
||
|
* @param r1 the horizontal radius of the ellipse to be drawn.
|
||
|
* @param r2 the vertical radius of the ellipse to be drawn.
|
||
|
*/
|
||
|
public void drawEllipse(
|
||
|
double x,
|
||
|
double y,
|
||
|
double r1,
|
||
|
double r2) throws Exception {
|
||
|
drawEllipse((float) x, (float) y, (float) r1, (float) r2, Operation.STROKE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws an ellipse on the page using the current pen color.
|
||
|
*
|
||
|
* @param x the x coordinate of the center of the ellipse to be drawn.
|
||
|
* @param y the y coordinate of the center of the ellipse to be drawn.
|
||
|
* @param r1 the horizontal radius of the ellipse to be drawn.
|
||
|
* @param r2 the vertical radius of the ellipse to be drawn.
|
||
|
*/
|
||
|
public void drawEllipse(
|
||
|
float x,
|
||
|
float y,
|
||
|
float r1,
|
||
|
float r2) throws Exception {
|
||
|
drawEllipse(x, y, r1, r2, Operation.STROKE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Fills an ellipse on the page using the current pen color.
|
||
|
*
|
||
|
* @param x the x coordinate of the center of the ellipse to be drawn.
|
||
|
* @param y the y coordinate of the center of the ellipse to be drawn.
|
||
|
* @param r1 the horizontal radius of the ellipse to be drawn.
|
||
|
* @param r2 the vertical radius of the ellipse to be drawn.
|
||
|
*/
|
||
|
public void fillEllipse(
|
||
|
double x,
|
||
|
double y,
|
||
|
double r1,
|
||
|
double r2) throws Exception {
|
||
|
drawEllipse((float) x, (float) y, (float) r1, (float) r2, Operation.FILL);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Fills an ellipse on the page using the current pen color.
|
||
|
*
|
||
|
* @param x the x coordinate of the center of the ellipse to be drawn.
|
||
|
* @param y the y coordinate of the center of the ellipse to be drawn.
|
||
|
* @param r1 the horizontal radius of the ellipse to be drawn.
|
||
|
* @param r2 the vertical radius of the ellipse to be drawn.
|
||
|
*/
|
||
|
public void fillEllipse(
|
||
|
float x,
|
||
|
float y,
|
||
|
float r1,
|
||
|
float r2) throws Exception {
|
||
|
drawEllipse(x, y, r1, r2, Operation.FILL);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws an ellipse on the page and fills it using the current brush color.
|
||
|
*
|
||
|
* @param x the x coordinate of the center of the ellipse to be drawn.
|
||
|
* @param y the y coordinate of the center of the ellipse to be drawn.
|
||
|
* @param r1 the horizontal radius of the ellipse to be drawn.
|
||
|
* @param r2 the vertical radius of the ellipse to be drawn.
|
||
|
* @param operation the operation.
|
||
|
*/
|
||
|
private void drawEllipse(
|
||
|
float x,
|
||
|
float y,
|
||
|
float r1,
|
||
|
float r2,
|
||
|
char operation) throws Exception {
|
||
|
// The best 4-spline magic number
|
||
|
float m4 = 0.551784f;
|
||
|
|
||
|
// Starting point
|
||
|
moveTo(x, y - r2);
|
||
|
|
||
|
appendPointXY(x + m4*r1, y - r2);
|
||
|
appendPointXY(x + r1, y - m4*r2);
|
||
|
appendPointXY(x + r1, y);
|
||
|
append("c\n");
|
||
|
|
||
|
appendPointXY(x + r1, y + m4*r2);
|
||
|
appendPointXY(x + m4*r1, y + r2);
|
||
|
appendPointXY(x, y + r2);
|
||
|
append("c\n");
|
||
|
|
||
|
appendPointXY(x - m4*r1, y + r2);
|
||
|
appendPointXY(x - r1, y + m4*r2);
|
||
|
appendPointXY(x - r1, y);
|
||
|
append("c\n");
|
||
|
|
||
|
appendPointXY(x - r1, y - m4*r2);
|
||
|
appendPointXY(x - m4*r1, y - r2);
|
||
|
appendPointXY(x, y - r2);
|
||
|
append("c\n");
|
||
|
|
||
|
append(operation);
|
||
|
append('\n');
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws a point on the page using the current pen color.
|
||
|
*
|
||
|
* @param p the point.
|
||
|
*/
|
||
|
public void drawPoint(Point p) throws Exception {
|
||
|
if (p.shape != Point.INVISIBLE) {
|
||
|
List<Point> list;
|
||
|
if (p.shape == Point.CIRCLE) {
|
||
|
if (p.fillShape) {
|
||
|
drawCircle(p.x, p.y, p.r, 'f');
|
||
|
}
|
||
|
else {
|
||
|
drawCircle(p.x, p.y, p.r, 'S');
|
||
|
}
|
||
|
}
|
||
|
else if (p.shape == Point.DIAMOND) {
|
||
|
list = new ArrayList<Point>();
|
||
|
list.add(new Point(p.x, p.y - p.r));
|
||
|
list.add(new Point(p.x + p.r, p.y));
|
||
|
list.add(new Point(p.x, p.y + p.r));
|
||
|
list.add(new Point(p.x - p.r, p.y));
|
||
|
if (p.fillShape) {
|
||
|
drawPath(list, 'f');
|
||
|
}
|
||
|
else {
|
||
|
drawPath(list, 's');
|
||
|
}
|
||
|
}
|
||
|
else if (p.shape == Point.BOX) {
|
||
|
list = new ArrayList<Point>();
|
||
|
list.add(new Point(p.x - p.r, p.y - p.r));
|
||
|
list.add(new Point(p.x + p.r, p.y - p.r));
|
||
|
list.add(new Point(p.x + p.r, p.y + p.r));
|
||
|
list.add(new Point(p.x - p.r, p.y + p.r));
|
||
|
if (p.fillShape) {
|
||
|
drawPath(list, 'f');
|
||
|
}
|
||
|
else {
|
||
|
drawPath(list, 's');
|
||
|
}
|
||
|
}
|
||
|
else if (p.shape == Point.PLUS) {
|
||
|
drawLine(p.x - p.r, p.y, p.x + p.r, p.y);
|
||
|
drawLine(p.x, p.y - p.r, p.x, p.y + p.r);
|
||
|
}
|
||
|
else if (p.shape == Point.UP_ARROW) {
|
||
|
list = new ArrayList<Point>();
|
||
|
list.add(new Point(p.x, p.y - p.r));
|
||
|
list.add(new Point(p.x + p.r, p.y + p.r));
|
||
|
list.add(new Point(p.x - p.r, p.y + p.r));
|
||
|
if (p.fillShape) {
|
||
|
drawPath(list, 'f');
|
||
|
}
|
||
|
else {
|
||
|
drawPath(list, 's');
|
||
|
}
|
||
|
}
|
||
|
else if (p.shape == Point.DOWN_ARROW) {
|
||
|
list = new ArrayList<Point>();
|
||
|
list.add(new Point(p.x - p.r, p.y - p.r));
|
||
|
list.add(new Point(p.x + p.r, p.y - p.r));
|
||
|
list.add(new Point(p.x, p.y + p.r));
|
||
|
if (p.fillShape) {
|
||
|
drawPath(list, 'f');
|
||
|
}
|
||
|
else {
|
||
|
drawPath(list, 's');
|
||
|
}
|
||
|
}
|
||
|
else if (p.shape == Point.LEFT_ARROW) {
|
||
|
list = new ArrayList<Point>();
|
||
|
list.add(new Point(p.x + p.r, p.y + p.r));
|
||
|
list.add(new Point(p.x - p.r, p.y));
|
||
|
list.add(new Point(p.x + p.r, p.y - p.r));
|
||
|
if (p.fillShape) {
|
||
|
drawPath(list, 'f');
|
||
|
}
|
||
|
else {
|
||
|
drawPath(list, 's');
|
||
|
}
|
||
|
}
|
||
|
else if (p.shape == Point.RIGHT_ARROW) {
|
||
|
list = new ArrayList<Point>();
|
||
|
list.add(new Point(p.x - p.r, p.y - p.r));
|
||
|
list.add(new Point(p.x + p.r, p.y));
|
||
|
list.add(new Point(p.x - p.r, p.y + p.r));
|
||
|
if (p.fillShape) {
|
||
|
drawPath(list, 'f');
|
||
|
}
|
||
|
else {
|
||
|
drawPath(list, 's');
|
||
|
}
|
||
|
}
|
||
|
else if (p.shape == Point.H_DASH) {
|
||
|
drawLine(p.x - p.r, p.y, p.x + p.r, p.y);
|
||
|
}
|
||
|
else if (p.shape == Point.V_DASH) {
|
||
|
drawLine(p.x, p.y - p.r, p.x, p.y + p.r);
|
||
|
}
|
||
|
else if (p.shape == Point.X_MARK) {
|
||
|
drawLine(p.x - p.r, p.y - p.r, p.x + p.r, p.y + p.r);
|
||
|
drawLine(p.x - p.r, p.y + p.r, p.x + p.r, p.y - p.r);
|
||
|
}
|
||
|
else if (p.shape == Point.MULTIPLY) {
|
||
|
drawLine(p.x - p.r, p.y - p.r, p.x + p.r, p.y + p.r);
|
||
|
drawLine(p.x - p.r, p.y + p.r, p.x + p.r, p.y - p.r);
|
||
|
drawLine(p.x - p.r, p.y, p.x + p.r, p.y);
|
||
|
drawLine(p.x, p.y - p.r, p.x, p.y + p.r);
|
||
|
}
|
||
|
else if (p.shape == Point.STAR) {
|
||
|
float angle = (float) Math.PI / 10;
|
||
|
float sin18 = (float) Math.sin(angle);
|
||
|
float cos18 = (float) Math.cos(angle);
|
||
|
float a = p.r * cos18;
|
||
|
float b = p.r * sin18;
|
||
|
float c = 2 * a * sin18;
|
||
|
float d = 2 * a * cos18 - p.r;
|
||
|
list = new ArrayList<Point>();
|
||
|
list.add(new Point(p.x, p.y - p.r));
|
||
|
list.add(new Point(p.x + c, p.y + d));
|
||
|
list.add(new Point(p.x - a, p.y - b));
|
||
|
list.add(new Point(p.x + a, p.y - b));
|
||
|
list.add(new Point(p.x - c, p.y + d));
|
||
|
if (p.fillShape) {
|
||
|
drawPath(list, 'f');
|
||
|
}
|
||
|
else {
|
||
|
drawPath(list, 's');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the text rendering mode.
|
||
|
*
|
||
|
* @param mode the rendering mode.
|
||
|
*/
|
||
|
public void setTextRenderingMode(int mode) throws Exception {
|
||
|
if (mode >= 0 && mode <= 7) {
|
||
|
this.renderingMode = mode;
|
||
|
}
|
||
|
else {
|
||
|
throw new Exception("Invalid text rendering mode: " + mode);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the text direction.
|
||
|
*
|
||
|
* @param degrees the angle.
|
||
|
*/
|
||
|
public void setTextDirection(int degrees) throws Exception {
|
||
|
if (degrees > 360) degrees %= 360;
|
||
|
if (degrees == 0) {
|
||
|
tm = new float[] { 1f, 0f, 0f, 1f};
|
||
|
}
|
||
|
else if (degrees == 90) {
|
||
|
tm = new float[] { 0f, 1f, -1f, 0f};
|
||
|
}
|
||
|
else if (degrees == 180) {
|
||
|
tm = new float[] {-1f, 0f, 0f, -1f};
|
||
|
}
|
||
|
else if (degrees == 270) {
|
||
|
tm = new float[] { 0f, -1f, 1f, 0f};
|
||
|
}
|
||
|
else if (degrees == 360) {
|
||
|
tm = new float[] { 1f, 0f, 0f, 1f};
|
||
|
}
|
||
|
else {
|
||
|
float sinOfAngle = (float) Math.sin(degrees * (Math.PI / 180));
|
||
|
float cosOfAngle = (float) Math.cos(degrees * (Math.PI / 180));
|
||
|
tm = new float[] {cosOfAngle, sinOfAngle, -sinOfAngle, cosOfAngle};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Draws a bezier curve starting from the current point.
|
||
|
* <strong>Please note:</strong> You must call the fillPath, closePath or strokePath method after the last bezierCurveTo call.
|
||
|
* <p><i>Author:</i> <strong>Pieter Libin</strong>, pieter@emweb.be</p>
|
||
|
*
|
||
|
* @param p1 first control point
|
||
|
* @param p2 second control point
|
||
|
* @param p3 end point
|
||
|
*/
|
||
|
public void bezierCurveTo(Point p1, Point p2, Point p3) throws IOException {
|
||
|
append(p1);
|
||
|
append(p2);
|
||
|
append(p3);
|
||
|
append("c\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the start of text block.
|
||
|
* Please see Example_32. This method must have matching call to setTextEnd().
|
||
|
*/
|
||
|
public void setTextStart() throws IOException {
|
||
|
append("BT\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the text location.
|
||
|
* Please see Example_32.
|
||
|
*
|
||
|
* @param x the x coordinate of new text location.
|
||
|
* @param y the y coordinate of new text location.
|
||
|
*/
|
||
|
public void setTextLocation(float x, float y) throws IOException {
|
||
|
append(x);
|
||
|
append(' ');
|
||
|
append(height - y);
|
||
|
append(" Td\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
public void setTextBegin(float x, float y) throws IOException {
|
||
|
append("BT\n");
|
||
|
append(x);
|
||
|
append(' ');
|
||
|
append(height - y);
|
||
|
append(" Td\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the text leading.
|
||
|
* Please see Example_32.
|
||
|
*
|
||
|
* @param leading the leading.
|
||
|
*/
|
||
|
public void setTextLeading(float leading) throws IOException {
|
||
|
append(leading);
|
||
|
append(" TL\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
public void setCharSpacing(float spacing) throws IOException {
|
||
|
append(spacing);
|
||
|
append(" Tc\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
public void setWordSpacing(float spacing) throws IOException {
|
||
|
append(spacing);
|
||
|
append(" Tw\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
public void setTextScaling(float scaling) throws IOException {
|
||
|
append(scaling);
|
||
|
append(" Tz\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
public void setTextRise(float rise) throws IOException {
|
||
|
append(rise);
|
||
|
append(" Ts\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
public void setTextFont(Font font) throws IOException {
|
||
|
this.font = font;
|
||
|
append("/F");
|
||
|
append(font.objNumber);
|
||
|
append(' ');
|
||
|
append(font.size);
|
||
|
append(" Tf\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Prints a line of text and moves to the next line.
|
||
|
* Please see Example_32.
|
||
|
*/
|
||
|
public void println(String str) throws IOException {
|
||
|
print(str);
|
||
|
println();
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Prints a line of text.
|
||
|
* Please see Example_32.
|
||
|
*/
|
||
|
public void print(String str) throws IOException {
|
||
|
if (font == null) {
|
||
|
return;
|
||
|
}
|
||
|
append("[<");
|
||
|
drawString(font, str);
|
||
|
append(">] TJ\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Move to the next line.
|
||
|
* Please see Example_32.
|
||
|
*/
|
||
|
public void println() throws IOException {
|
||
|
append("T*\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the end of text block.
|
||
|
* Please see Example_32.
|
||
|
*/
|
||
|
public void setTextEnd() throws IOException {
|
||
|
append("ET\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
// Code provided by:
|
||
|
// Dominique Andre Gunia <contact@dgunia.de>
|
||
|
// <<
|
||
|
public void drawRectRoundCorners(
|
||
|
float x, float y, float w, float h, float r1, float r2, char operation)
|
||
|
throws Exception {
|
||
|
|
||
|
// The best 4-spline magic number
|
||
|
float m4 = 0.551784f;
|
||
|
|
||
|
List<Point> list = new ArrayList<Point>();
|
||
|
|
||
|
// Starting point
|
||
|
list.add(new Point(x + w - r1, y));
|
||
|
list.add(new Point(x + w - r1 + m4*r1, y, Point.CONTROL_POINT));
|
||
|
list.add(new Point(x + w, y + r2 - m4*r2, Point.CONTROL_POINT));
|
||
|
list.add(new Point(x + w, y + r2));
|
||
|
|
||
|
list.add(new Point(x + w, y + h - r2));
|
||
|
list.add(new Point(x + w, y + h - r2 + m4*r2, Point.CONTROL_POINT));
|
||
|
list.add(new Point(x + w - m4*r1, y + h, Point.CONTROL_POINT));
|
||
|
list.add(new Point(x + w - r1, y + h));
|
||
|
|
||
|
list.add(new Point(x + r1, y + h));
|
||
|
list.add(new Point(x + r1 - m4*r1, y + h, Point.CONTROL_POINT));
|
||
|
list.add(new Point(x, y + h - m4*r2, Point.CONTROL_POINT));
|
||
|
list.add(new Point(x, y + h - r2));
|
||
|
|
||
|
list.add(new Point(x, y + r2));
|
||
|
list.add(new Point(x, y + r2 - m4*r2, Point.CONTROL_POINT));
|
||
|
list.add(new Point(x + m4*r1, y, Point.CONTROL_POINT));
|
||
|
list.add(new Point(x + r1, y));
|
||
|
list.add(new Point(x + w - r1, y));
|
||
|
|
||
|
drawPath(list, operation);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Clips the path.
|
||
|
*/
|
||
|
public void clipPath() throws IOException {
|
||
|
append("W\n");
|
||
|
append("n\n"); // Close the path without painting it.
|
||
|
}
|
||
|
|
||
|
|
||
|
public void clipRect(float x, float y, float w, float h)
|
||
|
throws IOException {
|
||
|
moveTo(x, y);
|
||
|
lineTo(x + w, y);
|
||
|
lineTo(x + w, y + h);
|
||
|
lineTo(x, y + h);
|
||
|
clipPath();
|
||
|
}
|
||
|
|
||
|
|
||
|
public void save() throws IOException {
|
||
|
append("q\n");
|
||
|
savedStates.add(new State(
|
||
|
pen, brush, pen_width, line_cap_style, line_join_style, linePattern));
|
||
|
savedHeight = height;
|
||
|
}
|
||
|
|
||
|
|
||
|
public void restore() throws IOException {
|
||
|
append("Q\n");
|
||
|
if (savedStates.size() > 0) {
|
||
|
State savedState = savedStates.remove(savedStates.size() - 1);
|
||
|
pen = savedState.getPen();
|
||
|
brush = savedState.getBrush();
|
||
|
pen_width = savedState.getPenWidth();
|
||
|
line_cap_style = savedState.getLineCapStyle();
|
||
|
line_join_style = savedState.getLineJoinStyle();
|
||
|
linePattern = savedState.getLinePattern();
|
||
|
}
|
||
|
if (savedHeight != Float.MAX_VALUE) {
|
||
|
height = savedHeight;
|
||
|
savedHeight = Float.MAX_VALUE;
|
||
|
}
|
||
|
}
|
||
|
// <<
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the page CropBox.
|
||
|
* See page 77 of the PDF32000_2008.pdf specification.
|
||
|
*
|
||
|
* @param upperLeftX the top left X coordinate of the CropBox.
|
||
|
* @param upperLeftY the top left Y coordinate of the CropBox.
|
||
|
* @param lowerRightX the bottom right X coordinate of the CropBox.
|
||
|
* @param lowerRightY the bottom right Y coordinate of the CropBox.
|
||
|
*/
|
||
|
public void setCropBox(
|
||
|
float upperLeftX, float upperLeftY, float lowerRightX, float lowerRightY) {
|
||
|
this.cropBox = new float[] {upperLeftX, upperLeftY, lowerRightX, lowerRightY};
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the page BleedBox.
|
||
|
* See page 77 of the PDF32000_2008.pdf specification.
|
||
|
*
|
||
|
* @param upperLeftX the top left X coordinate of the BleedBox.
|
||
|
* @param upperLeftY the top left Y coordinate of the BleedBox.
|
||
|
* @param lowerRightX the bottom right X coordinate of the BleedBox.
|
||
|
* @param lowerRightY the bottom right Y coordinate of the BleedBox.
|
||
|
*/
|
||
|
public void setBleedBox(
|
||
|
float upperLeftX, float upperLeftY, float lowerRightX, float lowerRightY) {
|
||
|
this.bleedBox = new float[] {upperLeftX, upperLeftY, lowerRightX, lowerRightY};
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the page TrimBox.
|
||
|
* See page 77 of the PDF32000_2008.pdf specification.
|
||
|
*
|
||
|
* @param upperLeftX the top left X coordinate of the TrimBox.
|
||
|
* @param upperLeftY the top left Y coordinate of the TrimBox.
|
||
|
* @param lowerRightX the bottom right X coordinate of the TrimBox.
|
||
|
* @param lowerRightY the bottom right Y coordinate of the TrimBox.
|
||
|
*/
|
||
|
public void setTrimBox(
|
||
|
float upperLeftX, float upperLeftY, float lowerRightX, float lowerRightY) {
|
||
|
this.trimBox = new float[] {upperLeftX, upperLeftY, lowerRightX, lowerRightY};
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets the page ArtBox.
|
||
|
* See page 77 of the PDF32000_2008.pdf specification.
|
||
|
*
|
||
|
* @param upperLeftX the top left X coordinate of the ArtBox.
|
||
|
* @param upperLeftY the top left Y coordinate of the ArtBox.
|
||
|
* @param lowerRightX the bottom right X coordinate of the ArtBox.
|
||
|
* @param lowerRightY the bottom right Y coordinate of the ArtBox.
|
||
|
*/
|
||
|
public void setArtBox(
|
||
|
float upperLeftX, float upperLeftY, float lowerRightX, float lowerRightY) {
|
||
|
this.artBox = new float[] {upperLeftX, upperLeftY, lowerRightX, lowerRightY};
|
||
|
}
|
||
|
|
||
|
|
||
|
private void appendPointXY(float x, float y) throws IOException {
|
||
|
append(x);
|
||
|
append(' ');
|
||
|
append(height - y);
|
||
|
append(' ');
|
||
|
}
|
||
|
|
||
|
|
||
|
private void append(Point point) throws IOException {
|
||
|
append(point.x);
|
||
|
append(' ');
|
||
|
append(height - point.y);
|
||
|
append(' ');
|
||
|
}
|
||
|
|
||
|
|
||
|
protected void append(String str) throws IOException {
|
||
|
int len = str.length();
|
||
|
for (int i = 0; i < len; i++) {
|
||
|
buf.write((byte) str.charAt(i));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
protected void append(int num) throws IOException {
|
||
|
append(Integer.toString(num));
|
||
|
}
|
||
|
|
||
|
|
||
|
protected void append(float val) throws IOException {
|
||
|
append(PDF.df.format(val));
|
||
|
}
|
||
|
|
||
|
|
||
|
protected void append(char ch) throws IOException {
|
||
|
buf.write((byte) ch);
|
||
|
}
|
||
|
|
||
|
|
||
|
protected void append(byte b) throws IOException {
|
||
|
buf.write(b);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Appends the specified array of bytes to the page.
|
||
|
*/
|
||
|
public void append(byte[] buffer) throws IOException {
|
||
|
buf.write(buffer);
|
||
|
}
|
||
|
|
||
|
|
||
|
protected void drawString(
|
||
|
Font font,
|
||
|
String str,
|
||
|
float x,
|
||
|
float y,
|
||
|
Map<String, Integer> colors) throws Exception {
|
||
|
setTextBegin(x, y);
|
||
|
setTextFont(font);
|
||
|
|
||
|
StringBuilder buf1 = new StringBuilder();
|
||
|
StringBuilder buf2 = new StringBuilder();
|
||
|
for (int i = 0; i < str.length(); i++) {
|
||
|
char ch = str.charAt(i);
|
||
|
if (Character.isLetterOrDigit(ch)) {
|
||
|
printBuffer(buf2, colors);
|
||
|
buf1.append(ch);
|
||
|
}
|
||
|
else {
|
||
|
printBuffer(buf1, colors);
|
||
|
buf2.append(ch);
|
||
|
}
|
||
|
}
|
||
|
printBuffer(buf1, colors);
|
||
|
printBuffer(buf2, colors);
|
||
|
|
||
|
setTextEnd();
|
||
|
}
|
||
|
|
||
|
|
||
|
private void printBuffer(
|
||
|
StringBuilder buf,
|
||
|
Map<String, Integer> colors) throws Exception {
|
||
|
String str = buf.toString();
|
||
|
if (str.length() > 0) {
|
||
|
if (colors.containsKey(str)) {
|
||
|
setBrushColor(colors.get(str));
|
||
|
}
|
||
|
else {
|
||
|
setBrushColor(Color.black);
|
||
|
}
|
||
|
}
|
||
|
print(str);
|
||
|
buf.setLength(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
protected void setStructElementsPageObjNumber(
|
||
|
int pageObjNumber) throws Exception {
|
||
|
for (StructElem element : structures) {
|
||
|
element.pageObjNumber = pageObjNumber;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
public void addBMC(
|
||
|
String structure,
|
||
|
String altDescription,
|
||
|
String actualText) throws Exception {
|
||
|
addBMC(structure, null, altDescription, actualText);
|
||
|
}
|
||
|
|
||
|
|
||
|
public void addBMC(
|
||
|
String structure,
|
||
|
String language,
|
||
|
String altDescription,
|
||
|
String actualText) throws Exception {
|
||
|
if (pdf.compliance == Compliance.PDF_UA) {
|
||
|
StructElem element = new StructElem();
|
||
|
element.structure = structure;
|
||
|
element.mcid = mcid;
|
||
|
element.language = language;
|
||
|
element.altDescription = altDescription;
|
||
|
element.actualText = actualText;
|
||
|
structures.add(element);
|
||
|
|
||
|
append("/");
|
||
|
append(structure);
|
||
|
append(" <</MCID ");
|
||
|
append(mcid++);
|
||
|
append(">>\n");
|
||
|
append("BDC\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
public void addEMC() throws Exception {
|
||
|
if (pdf.compliance == Compliance.PDF_UA) {
|
||
|
append("EMC\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
protected void addAnnotation(Annotation annotation) {
|
||
|
annots.add(annotation);
|
||
|
if (pdf.compliance == Compliance.PDF_UA) {
|
||
|
StructElem element = new StructElem();
|
||
|
element.structure = StructElem.LINK;
|
||
|
element.language = annotation.language;
|
||
|
element.altDescription = annotation.altDescription;
|
||
|
element.actualText = annotation.actualText;
|
||
|
element.annotation = annotation;
|
||
|
structures.add(element);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
protected void beginTransform(
|
||
|
float x, float y, float xScale, float yScale) throws Exception {
|
||
|
append("q\n");
|
||
|
|
||
|
append(xScale);
|
||
|
append(" 0 0 ");
|
||
|
append(yScale);
|
||
|
append(' ');
|
||
|
append(x);
|
||
|
append(' ');
|
||
|
append(y);
|
||
|
append(" cm\n");
|
||
|
|
||
|
append(xScale);
|
||
|
append(" 0 0 ");
|
||
|
append(yScale);
|
||
|
append(' ');
|
||
|
append(x);
|
||
|
append(' ');
|
||
|
append(y);
|
||
|
append(" Tm\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
protected void endTransform() throws Exception {
|
||
|
append("Q\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
public void drawContents(
|
||
|
byte[] content,
|
||
|
float h, // The height of the graphics object in points.
|
||
|
float x,
|
||
|
float y,
|
||
|
float xScale,
|
||
|
float yScale) throws Exception {
|
||
|
beginTransform(x, (this.height - yScale * h) - y, xScale, yScale);
|
||
|
append(content);
|
||
|
endTransform();
|
||
|
}
|
||
|
|
||
|
|
||
|
public void drawString(
|
||
|
Font font, String str, float x, float y, float dx) throws Exception {
|
||
|
float x1 = x;
|
||
|
for (int i = 0; i < str.length(); i++) {
|
||
|
drawString(font, str.substring(i, i + 1), x1, y);
|
||
|
x1 += dx;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
public void addWatermark(
|
||
|
Font font, String text) throws Exception {
|
||
|
float hypotenuse = (float)
|
||
|
Math.sqrt(this.height * this.height + this.width * this.width);
|
||
|
float stringWidth = font.stringWidth(text);
|
||
|
float offset = (hypotenuse - stringWidth) / 2f;
|
||
|
double angle = Math.atan(this.height / this.width);
|
||
|
TextLine watermark = new TextLine(font);
|
||
|
watermark.setColor(Color.lightgrey);
|
||
|
watermark.setText(text);
|
||
|
watermark.setLocation(
|
||
|
(float) (offset * Math.cos(angle)),
|
||
|
(this.height - (float) (offset * Math.sin(angle))));
|
||
|
watermark.setTextDirection((int) (angle * (180.0 / Math.PI)));
|
||
|
watermark.drawOn(this);
|
||
|
}
|
||
|
|
||
|
|
||
|
public void invertYAxis() throws Exception {
|
||
|
append("1 0 0 -1 0 ");
|
||
|
append(this.height);
|
||
|
append(" cm\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Transformation matrix.
|
||
|
* Use save before, restore afterwards!
|
||
|
* 9 value array like generated by androids Matrix.getValues()
|
||
|
*
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
public void transform(float[] values) throws IOException {
|
||
|
float scalex = (values[MSCALE_X]);
|
||
|
float scaley = (values[MSCALE_Y]);
|
||
|
float transx = values[MTRANS_X];
|
||
|
float transy = values[MTRANS_Y];
|
||
|
|
||
|
append(scalex);
|
||
|
append(" ");
|
||
|
append(values[MSKEW_X]);
|
||
|
append(" ");
|
||
|
append(values[MSKEW_Y]);
|
||
|
append(" ");
|
||
|
append(scaley);
|
||
|
append(" ");
|
||
|
|
||
|
if (Math.asin(values[MSKEW_Y]) != 0f) {
|
||
|
transx -= values[MSKEW_Y] * height / scaley;
|
||
|
}
|
||
|
|
||
|
append(transx);
|
||
|
append(" ");
|
||
|
append(-transy);
|
||
|
append(" cm\n");
|
||
|
|
||
|
// Weil mit der Hoehe immer die Y-Koordinate im PDF-Koordinatensystem berechnet wird:
|
||
|
height = height / scaley;
|
||
|
}
|
||
|
|
||
|
} // End of Page.java
|