From 6025b6016517c6d898d8957d1d7e03ba71431912 Mon Sep 17 00:00:00 2001
From: tknall
+ * Note: if the object has been read from an input stream, the only
+ * time you can be sure if isExplicit is returning the true state of
+ * affairs is if it returns false. An implicitly tagged object may appear
+ * to be explicitly tagged, so you need to understand the context under
+ * which the reading was done as well, see getObject below.
+ */
+ public boolean isExplicit()
+ {
+ return explicit;
+ }
+
+ public boolean isEmpty()
+ {
+ return empty;
+ }
+
+ /**
+ * return whatever was following the tag.
+ *
+ * Note: tagged objects are generally context dependent if you're
+ * trying to extract a tagged object you should be going via the
+ * appropriate getInstance method.
+ */
+ public DERObject getObject()
+ {
+ if (obj != null)
+ {
+ return obj.getDERObject();
+ }
+
+ return null;
+ }
+
+ abstract void encode(DEROutputStream out)
+ throws IOException;
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERConstructedOctetString.java b/src/main/java/com/lowagie/bc/asn1/BERConstructedOctetString.java
new file mode 100644
index 0000000..5c44187
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERConstructedOctetString.java
@@ -0,0 +1,168 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+public class BERConstructedOctetString
+ extends DEROctetString
+{
+ /**
+ * convert a vector of octet strings into a single byte string
+ */
+ static private byte[] toBytes(
+ Vector octs)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != octs.size(); i++)
+ {
+ DEROctetString o = (DEROctetString)octs.elementAt(i);
+
+ try
+ {
+ bOut.write(o.getOctets());
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("exception converting octets " + e.toString());
+ }
+ }
+
+ return bOut.toByteArray();
+ }
+
+ private Vector octs;
+
+ /**
+ * @param string the octets making up the octet string.
+ */
+ public BERConstructedOctetString(
+ byte[] string)
+ {
+ super(string);
+ }
+
+ public BERConstructedOctetString(
+ Vector octs)
+ {
+ super(toBytes(octs));
+
+ this.octs = octs;
+ }
+
+ public BERConstructedOctetString(
+ DERObject obj)
+ {
+ super(obj);
+ }
+
+ public BERConstructedOctetString(
+ DEREncodable obj)
+ {
+ super(obj.getDERObject());
+ }
+
+ public byte[] getOctets()
+ {
+ return string;
+ }
+
+ /**
+ * return the DER octets that make up this string.
+ */
+ public Enumeration getObjects()
+ {
+ if (octs == null)
+ {
+ return generateOcts().elements();
+ }
+
+ return octs.elements();
+ }
+
+ private Vector generateOcts()
+ {
+ int start = 0;
+ int end = 0;
+ Vector vec = new Vector();
+
+ while ((end + 1) < string.length)
+ {
+ if (string[end] == 0 && string[end + 1] == 0)
+ {
+ byte[] nStr = new byte[end - start + 1];
+
+ System.arraycopy(string, start, nStr, 0, nStr.length);
+
+ vec.addElement(new DEROctetString(nStr));
+ start = end + 1;
+ }
+ end++;
+ }
+
+ byte[] nStr = new byte[string.length - start];
+
+ System.arraycopy(string, start, nStr, 0, nStr.length);
+
+ vec.addElement(new DEROctetString(nStr));
+
+ return vec;
+ }
+
+ public void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
+ {
+ out.write(CONSTRUCTED | OCTET_STRING);
+
+ out.write(0x80);
+
+ //
+ // write out the octet array
+ //
+ if (octs != null)
+ {
+ for (int i = 0; i != octs.size(); i++)
+ {
+ out.writeObject(octs.elementAt(i));
+ }
+ }
+ else
+ {
+ int start = 0;
+ int end = 0;
+
+ while ((end + 1) < string.length)
+ {
+ if (string[end] == 0 && string[end + 1] == 0)
+ {
+ byte[] nStr = new byte[end - start + 1];
+
+ System.arraycopy(string, start, nStr, 0, nStr.length);
+
+ out.writeObject(new DEROctetString(nStr));
+ start = end + 1;
+ }
+ end++;
+ }
+
+ byte[] nStr = new byte[string.length - start];
+
+ System.arraycopy(string, start, nStr, 0, nStr.length);
+
+ out.writeObject(new DEROctetString(nStr));
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERConstructedSequence.java b/src/main/java/com/lowagie/bc/asn1/BERConstructedSequence.java
new file mode 100644
index 0000000..2d5d081
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERConstructedSequence.java
@@ -0,0 +1,34 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class BERConstructedSequence
+ extends DERConstructedSequence
+{
+ /*
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
+ {
+ out.write(SEQUENCE | CONSTRUCTED);
+ out.write(0x80);
+
+ Enumeration e = getObjects();
+ while (e.hasMoreElements())
+ {
+ out.writeObject(e.nextElement());
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERInputStream.java b/src/main/java/com/lowagie/bc/asn1/BERInputStream.java
new file mode 100644
index 0000000..395c1e7
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERInputStream.java
@@ -0,0 +1,197 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+public class BERInputStream
+ extends DERInputStream
+{
+ private DERObject END_OF_STREAM = new DERObject() {
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ throw new IOException("Eeek!");
+ }
+
+ };
+ public BERInputStream(
+ InputStream is)
+ {
+ super(is);
+ }
+
+ /**
+ * read a string of bytes representing an indefinite length object.
+ */
+ private byte[] readIndefiniteLengthFully()
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ int b, b1;
+
+ b1 = read();
+
+ while ((b = read()) >= 0)
+ {
+ if (b1 == 0 && b == 0)
+ {
+ break;
+ }
+
+ bOut.write(b1);
+ b1 = b;
+ }
+
+ return bOut.toByteArray();
+ }
+
+ private BERConstructedOctetString buildConstructedOctetString()
+ throws IOException
+ {
+ Vector octs = new Vector();
+
+ for (;;)
+ {
+ DERObject o = readObject();
+
+ if (o == END_OF_STREAM)
+ {
+ break;
+ }
+
+ octs.addElement(o);
+ }
+
+ return new BERConstructedOctetString(octs);
+ }
+
+ public DERObject readObject()
+ throws IOException
+ {
+ int tag = read();
+ if (tag == -1)
+ {
+ throw new EOFException();
+ }
+
+ int length = readLength();
+
+ if (length < 0) // indefinite length method
+ {
+ switch (tag)
+ {
+ case NULL:
+ return null;
+ case SEQUENCE | CONSTRUCTED:
+ BERConstructedSequence seq = new BERConstructedSequence();
+
+ for (;;)
+ {
+ DERObject obj = readObject();
+
+ if (obj == END_OF_STREAM)
+ {
+ break;
+ }
+
+ seq.addObject(obj);
+ }
+ return seq;
+ case OCTET_STRING | CONSTRUCTED:
+ return buildConstructedOctetString();
+ case SET | CONSTRUCTED:
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ for (;;)
+ {
+ DERObject obj = readObject();
+
+ if (obj == END_OF_STREAM)
+ {
+ break;
+ }
+
+ v.add(obj);
+ }
+ return new BERSet(v);
+ default:
+ //
+ // with tagged object tag number is bottom 5 bits
+ //
+ if ((tag & TAGGED) != 0)
+ {
+ if ((tag & 0x1f) == 0x1f)
+ {
+ throw new IOException("unsupported high tag encountered");
+ }
+
+ //
+ // simple type - implicit... return an octet string
+ //
+ if ((tag & CONSTRUCTED) == 0)
+ {
+ byte[] bytes = readIndefiniteLengthFully();
+
+ return new BERTaggedObject(false, tag & 0x1f, new DEROctetString(bytes));
+ }
+
+ //
+ // either constructed or explicitly tagged
+ //
+ DERObject dObj = readObject();
+
+ if (dObj == END_OF_STREAM) // empty tag!
+ {
+ return new DERTaggedObject(tag & 0x1f);
+ }
+
+ DERObject next = readObject();
+
+ //
+ // explicitly tagged (probably!) - if it isn't we'd have to
+ // tell from the context
+ //
+ if (next == END_OF_STREAM)
+ {
+ return new BERTaggedObject(tag & 0x1f, dObj);
+ }
+
+ //
+ // another implicit object, we'll create a sequence...
+ //
+ seq = new BERConstructedSequence();
+
+ seq.addObject(dObj);
+
+ do
+ {
+ seq.addObject(next);
+ next = readObject();
+ }
+ while (next != END_OF_STREAM);
+
+ return new BERTaggedObject(false, tag & 0x1f, seq);
+ }
+
+ throw new IOException("unknown BER object encountered");
+ }
+ }
+ else
+ {
+ if (tag == 0 && length == 0) // end of contents marker.
+ {
+ return END_OF_STREAM;
+ }
+
+ byte[] bytes = new byte[length];
+
+ readFully(bytes);
+
+ return buildObject(tag, bytes);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERNull.java b/src/main/java/com/lowagie/bc/asn1/BERNull.java
new file mode 100644
index 0000000..868691f
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERNull.java
@@ -0,0 +1,30 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * A BER NULL object.
+ */
+public class BERNull
+ extends DERNull
+{
+ public BERNull()
+ {
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
+ {
+ out.write(NULL);
+ out.write(0);
+ out.write(0);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BEROutputStream.java b/src/main/java/com/lowagie/bc/asn1/BEROutputStream.java
new file mode 100644
index 0000000..9ae1126
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BEROutputStream.java
@@ -0,0 +1,36 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class BEROutputStream
+ extends DEROutputStream
+{
+ public BEROutputStream(
+ OutputStream os)
+ {
+ super(os);
+ }
+
+ public void writeObject(
+ Object obj)
+ throws IOException
+ {
+ if (obj == null)
+ {
+ writeNull();
+ }
+ else if (obj instanceof DERObject)
+ {
+ ((DERObject)obj).encode(this);
+ }
+ else if (obj instanceof DEREncodable)
+ {
+ ((DEREncodable)obj).getDERObject().encode(this);
+ }
+ else
+ {
+ throw new IOException("object not BEREncodable");
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERSequence.java b/src/main/java/com/lowagie/bc/asn1/BERSequence.java
new file mode 100644
index 0000000..5654568
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERSequence.java
@@ -0,0 +1,59 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class BERSequence
+ extends DERSequence
+{
+ /**
+ * create an empty sequence
+ */
+ public BERSequence()
+ {
+ }
+
+ /**
+ * create a sequence containing one object
+ */
+ public BERSequence(
+ DEREncodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ public BERSequence(
+ DEREncodableVector v)
+ {
+ super(v);
+ }
+
+ /*
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
+ {
+ out.write(SEQUENCE | CONSTRUCTED);
+ out.write(0x80);
+
+ Enumeration e = getObjects();
+ while (e.hasMoreElements())
+ {
+ out.writeObject(e.nextElement());
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERSet.java b/src/main/java/com/lowagie/bc/asn1/BERSet.java
new file mode 100644
index 0000000..0f608dc
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERSet.java
@@ -0,0 +1,59 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class BERSet
+ extends DERSet
+{
+ /**
+ * create an empty sequence
+ */
+ public BERSet()
+ {
+ }
+
+ /**
+ * create a set containing one object
+ */
+ public BERSet(
+ DEREncodable obj)
+ {
+ super(obj);
+ }
+
+ /**
+ * create a set containing a vector of objects.
+ */
+ public BERSet(
+ DEREncodableVector v)
+ {
+ super(v);
+ }
+
+ /*
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
+ {
+ out.write(SET | CONSTRUCTED);
+ out.write(0x80);
+
+ Enumeration e = getObjects();
+ while (e.hasMoreElements())
+ {
+ out.writeObject(e.nextElement());
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/BERTaggedObject.java b/src/main/java/com/lowagie/bc/asn1/BERTaggedObject.java
new file mode 100644
index 0000000..203bc22
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/BERTaggedObject.java
@@ -0,0 +1,119 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * BER TaggedObject - in ASN.1 nottation this is any object proceeded by
+ * a [n] where n is some number - these are assume to follow the construction
+ * rules (as with sequences).
+ */
+public class BERTaggedObject
+ extends DERTaggedObject
+{
+ /**
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public BERTaggedObject(
+ int tagNo,
+ DEREncodable obj)
+ {
+ super(tagNo, obj);
+ }
+
+ /**
+ * @param explicit true if an explicitly tagged object.
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public BERTaggedObject(
+ boolean explicit,
+ int tagNo,
+ DEREncodable obj)
+ {
+ super(explicit, tagNo, obj);
+ }
+
+ /**
+ * create an implicitly tagged object that contains a zero
+ * length sequence.
+ */
+ public BERTaggedObject(
+ int tagNo)
+ {
+ super(false, tagNo, new BERConstructedSequence());
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (out instanceof ASN1OutputStream || out instanceof BEROutputStream)
+ {
+ out.write(CONSTRUCTED | TAGGED | tagNo);
+ out.write(0x80);
+
+ if (!empty)
+ {
+ if (!explicit)
+ {
+ if (obj instanceof ASN1OctetString)
+ {
+ Enumeration e;
+
+ if (obj instanceof BERConstructedOctetString)
+ {
+ e = ((BERConstructedOctetString)obj).getObjects();
+ }
+ else
+ {
+ ASN1OctetString octs = (ASN1OctetString)obj;
+ BERConstructedOctetString berO = new BERConstructedOctetString(octs.getOctets());
+
+ e = berO.getObjects();
+ }
+
+ while (e.hasMoreElements())
+ {
+ out.writeObject(e.nextElement());
+ }
+ }
+ else if (obj instanceof ASN1Sequence)
+ {
+ Enumeration e = ((ASN1Sequence)obj).getObjects();
+
+ while (e.hasMoreElements())
+ {
+ out.writeObject(e.nextElement());
+ }
+ }
+ else if (obj instanceof ASN1Set)
+ {
+ Enumeration e = ((ASN1Set)obj).getObjects();
+
+ while (e.hasMoreElements())
+ {
+ out.writeObject(e.nextElement());
+ }
+ }
+ else
+ {
+ throw new RuntimeException("not implemented: " + obj.getClass().getName());
+ }
+ }
+ else
+ {
+ out.writeObject(obj);
+ }
+ }
+
+ out.write(0x00);
+ out.write(0x00);
+ }
+ else
+ {
+ super.encode(out);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERApplicationSpecific.java b/src/main/java/com/lowagie/bc/asn1/DERApplicationSpecific.java
new file mode 100644
index 0000000..2ef11d4
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERApplicationSpecific.java
@@ -0,0 +1,67 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Base class for an application specific object
+ */
+public class DERApplicationSpecific
+ extends DERObject
+{
+ private int tag;
+ private byte[] octets;
+
+ public DERApplicationSpecific(
+ int tag,
+ byte[] octets)
+ {
+ this.tag = tag;
+ this.octets = octets;
+ }
+
+ public DERApplicationSpecific(
+ int tag,
+ DEREncodable object)
+ throws IOException
+ {
+ this.tag = tag | DERTags.CONSTRUCTED;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DEROutputStream dos = new DEROutputStream(baos);
+
+ dos.writeObject(object);
+
+ this.octets = baos.toByteArray();
+ }
+
+ public boolean isConstructed()
+ {
+ return (tag & DERTags.CONSTRUCTED) != 0;
+ }
+
+ public byte[] getContents()
+ {
+ return octets;
+ }
+
+ public int getApplicationTag()
+ {
+ return tag & 0x1F;
+ }
+
+ public DERObject getObject()
+ throws IOException
+ {
+ return new ASN1InputStream(new ByteArrayInputStream(getContents())).readObject();
+ }
+
+ /* (non-Javadoc)
+ * @see org.bouncycastle.asn1.DERObject#encode(org.bouncycastle.asn1.DEROutputStream)
+ */
+ void encode(DEROutputStream out) throws IOException
+ {
+ out.writeEncoded(DERTags.APPLICATION | tag, octets);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERBMPString.java b/src/main/java/com/lowagie/bc/asn1/DERBMPString.java
new file mode 100644
index 0000000..2e712e8
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERBMPString.java
@@ -0,0 +1,121 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER BMPString object.
+ */
+public class DERBMPString
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a BMP String from the given object.
+ *
+ * @param obj the object we want converted.
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERBMPString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERBMPString)
+ {
+ return (DERBMPString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERBMPString(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a BMP String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERBMPString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ public DERBMPString(
+ byte[] string)
+ {
+ char[] cs = new char[string.length / 2];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff));
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERBMPString(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DERBMPString))
+ {
+ return false;
+ }
+
+ DERBMPString s = (DERBMPString)o;
+
+ return this.getString().equals(s.getString());
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ char[] c = string.toCharArray();
+ byte[] b = new byte[c.length * 2];
+
+ for (int i = 0; i != c.length; i++)
+ {
+ b[2 * i] = (byte)(c[i] >> 8);
+ b[2 * i + 1] = (byte)c[i];
+ }
+
+ out.writeEncoded(BMP_STRING, b);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERBitString.java b/src/main/java/com/lowagie/bc/asn1/DERBitString.java
new file mode 100644
index 0000000..a5acd39
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERBitString.java
@@ -0,0 +1,268 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class DERBitString
+ extends DERObject
+ implements DERString
+{
+ private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ protected byte[] data;
+ protected int padBits;
+
+ /**
+ * return the correct number of pad bits for a bit string defined in
+ * a 32 bit constant
+ */
+ static protected int getPadBits(
+ int bitString)
+ {
+ int val = 0;
+ for (int i = 3; i >= 0; i--)
+ {
+ //
+ // this may look a little odd, but if it isn't done like this pre jdk1.2
+ // JVM's break!
+ //
+ if (i != 0)
+ {
+ if ((bitString >> (i * 8)) != 0)
+ {
+ val = (bitString >> (i * 8)) & 0xFF;
+ break;
+ }
+ }
+ else
+ {
+ if (bitString != 0)
+ {
+ val = bitString & 0xFF;
+ break;
+ }
+ }
+ }
+
+ if (val == 0)
+ {
+ return 7;
+ }
+
+
+ int bits = 1;
+
+ while (((val <<= 1) & 0xFF) != 0)
+ {
+ bits++;
+ }
+
+ return 8 - bits;
+ }
+
+ /**
+ * return the correct number of bytes for a bit string defined in
+ * a 32 bit constant
+ */
+ static protected byte[] getBytes(int bitString)
+ {
+ int bytes = 4;
+ for (int i = 3; i >= 1; i--)
+ {
+ if ((bitString & (0xFF << (i * 8))) != 0)
+ {
+ break;
+ }
+ bytes--;
+ }
+
+ byte[] result = new byte[bytes];
+ for (int i = 0; i < bytes; i++)
+ {
+ result[i] = (byte) ((bitString >> (i * 8)) & 0xFF);
+ }
+
+ return result;
+ }
+
+ /**
+ * return a Bit String from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERBitString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERBitString)
+ {
+ return (DERBitString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ byte[] bytes = ((ASN1OctetString)obj).getOctets();
+ int padBits = bytes[0];
+ byte[] data = new byte[bytes.length - 1];
+
+ System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
+
+ return new DERBitString(data, padBits);
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Bit String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERBitString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ protected DERBitString(
+ byte data,
+ int padBits)
+ {
+ this.data = new byte[1];
+ this.data[0] = data;
+ this.padBits = padBits;
+ }
+
+ /**
+ * @param data the octets making up the bit string.
+ * @param padBits the number of extra bits at the end of the string.
+ */
+ public DERBitString(
+ byte[] data,
+ int padBits)
+ {
+ this.data = data;
+ this.padBits = padBits;
+ }
+
+ public DERBitString(
+ byte[] data)
+ {
+ this(data, 0);
+ }
+
+ public DERBitString(
+ DEREncodable obj)
+ {
+ try
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+
+ dOut.writeObject(obj);
+ dOut.close();
+
+ this.data = bOut.toByteArray();
+ this.padBits = 0;
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException("Error processing object : " + e.toString());
+ }
+ }
+
+ public byte[] getBytes()
+ {
+ return data;
+ }
+
+ public int getPadBits()
+ {
+ return padBits;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ byte[] bytes = new byte[getBytes().length + 1];
+
+ bytes[0] = (byte)getPadBits();
+ System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1);
+
+ out.writeEncoded(BIT_STRING, bytes);
+ }
+
+ public int hashCode()
+ {
+ int value = 0;
+
+ for (int i = 0; i != data.length; i++)
+ {
+ value ^= (data[i] & 0xff) << (i % 4);
+ }
+
+ return value;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof DERBitString))
+ {
+ return false;
+ }
+
+ DERBitString other = (DERBitString)o;
+
+ if (data.length != other.data.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != data.length; i++)
+ {
+ if (data[i] != other.data[i])
+ {
+ return false;
+ }
+ }
+
+ return (padBits == other.padBits);
+ }
+
+ public String getString()
+ {
+ StringBuffer buf = new StringBuffer("#");
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ try
+ {
+ aOut.writeObject(this);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("internal error encoding BitString");
+ }
+
+ byte[] string = bOut.toByteArray();
+
+ for (int i = 0; i != string.length; i++)
+ {
+ buf.append(table[(string[i] >>> 4) % 0xf]);
+ buf.append(table[string[i] & 0xf]);
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERBoolean.java b/src/main/java/com/lowagie/bc/asn1/DERBoolean.java
new file mode 100644
index 0000000..5b6d1bf
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERBoolean.java
@@ -0,0 +1,103 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+public class DERBoolean
+ extends DERObject
+{
+ byte value;
+
+ public static final DERBoolean FALSE = new DERBoolean(false);
+ public static final DERBoolean TRUE = new DERBoolean(true);
+
+ /**
+ * return a boolean from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERBoolean getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERBoolean)
+ {
+ return (DERBoolean)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERBoolean(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a DERBoolean from the passed in boolean.
+ */
+ public static DERBoolean getInstance(
+ boolean value)
+ {
+ return (value ? TRUE : FALSE);
+ }
+
+ /**
+ * return a Boolean from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERBoolean getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ public DERBoolean(
+ byte[] value)
+ {
+ this.value = value[0];
+ }
+
+ public DERBoolean(
+ boolean value)
+ {
+ this.value = (value) ? (byte)0xff : (byte)0;
+ }
+
+ public boolean isTrue()
+ {
+ return (value != 0);
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ byte[] bytes = new byte[1];
+
+ bytes[0] = value;
+
+ out.writeEncoded(BOOLEAN, bytes);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERBoolean))
+ {
+ return false;
+ }
+
+ return (value == ((DERBoolean)o).value);
+ }
+
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERConstructedSequence.java b/src/main/java/com/lowagie/bc/asn1/DERConstructedSequence.java
new file mode 100644
index 0000000..607e53b
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERConstructedSequence.java
@@ -0,0 +1,53 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * //@deprecated use DERSequence.
+ */
+public class DERConstructedSequence
+ extends ASN1Sequence
+{
+ public void addObject(
+ DEREncodable obj)
+ {
+ super.addObject(obj);
+ }
+
+ public int getSize()
+ {
+ return size();
+ }
+
+ /*
+ * A note on the implementation:
+ *
+ * As DER requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputing SEQUENCE,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ Enumeration e = this.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject(obj);
+ }
+
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ out.writeEncoded(SEQUENCE | CONSTRUCTED, bytes);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERConstructedSet.java b/src/main/java/com/lowagie/bc/asn1/DERConstructedSet.java
new file mode 100644
index 0000000..7f52ca1
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERConstructedSet.java
@@ -0,0 +1,75 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class DERConstructedSet
+ extends ASN1Set
+{
+ public DERConstructedSet()
+ {
+ }
+
+ /**
+ * @param obj - a single object that makes up the set.
+ */
+ public DERConstructedSet(
+ DEREncodable obj)
+ {
+ this.addObject(obj);
+ }
+
+ /**
+ * @param v - a vector of objects making up the set.
+ */
+ public DERConstructedSet(
+ DEREncodableVector v)
+ {
+ for (int i = 0; i != v.size(); i++)
+ {
+ this.addObject(v.get(i));
+ }
+ }
+
+ public void addObject(
+ DEREncodable obj)
+ {
+ super.addObject(obj);
+ }
+
+ public int getSize()
+ {
+ return size();
+ }
+
+ /*
+ * A note on the implementation:
+ *
+ * As DER requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputing SET,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ Enumeration e = this.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject(obj);
+ }
+
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ out.writeEncoded(SET | CONSTRUCTED, bytes);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DEREncodable.java b/src/main/java/com/lowagie/bc/asn1/DEREncodable.java
new file mode 100644
index 0000000..322ffc0
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DEREncodable.java
@@ -0,0 +1,6 @@
+package com.lowagie.bc.asn1;
+
+public interface DEREncodable
+{
+ public DERObject getDERObject();
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DEREncodableVector.java b/src/main/java/com/lowagie/bc/asn1/DEREncodableVector.java
new file mode 100644
index 0000000..24d4c51
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DEREncodableVector.java
@@ -0,0 +1,30 @@
+package com.lowagie.bc.asn1;
+
+import java.util.Vector;
+
+/**
+ * a general class for building up a vector of DER encodable objects -
+ * this will eventually be superceded by ASN1EncodableVector so you should
+ * use that class in preference.
+ */
+public class DEREncodableVector
+{
+ private Vector v = new Vector();
+
+ public void add(
+ DEREncodable obj)
+ {
+ v.addElement(obj);
+ }
+
+ public DEREncodable get(
+ int i)
+ {
+ return (DEREncodable)v.elementAt(i);
+ }
+
+ public int size()
+ {
+ return v.size();
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DEREnumerated.java b/src/main/java/com/lowagie/bc/asn1/DEREnumerated.java
new file mode 100644
index 0000000..36b3f98
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DEREnumerated.java
@@ -0,0 +1,108 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+public class DEREnumerated
+ extends DERObject
+{
+ byte[] bytes;
+
+ /**
+ * return an integer from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DEREnumerated getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DEREnumerated)
+ {
+ return (DEREnumerated)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DEREnumerated(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Enumerated from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DEREnumerated getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ public DEREnumerated(
+ int value)
+ {
+ bytes = BigInteger.valueOf(value).toByteArray();
+ }
+
+ public DEREnumerated(
+ BigInteger value)
+ {
+ bytes = value.toByteArray();
+ }
+
+ public DEREnumerated(
+ byte[] bytes)
+ {
+ this.bytes = bytes;
+ }
+
+ public BigInteger getValue()
+ {
+ return new BigInteger(bytes);
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(ENUMERATED, bytes);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof DEREnumerated))
+ {
+ return false;
+ }
+
+ DEREnumerated other = (DEREnumerated)o;
+
+ if (bytes.length != other.bytes.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ if (bytes[i] != other.bytes[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERGeneralString.java b/src/main/java/com/lowagie/bc/asn1/DERGeneralString.java
new file mode 100644
index 0000000..641620b
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERGeneralString.java
@@ -0,0 +1,86 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+public class DERGeneralString
+ extends DERObject implements DERString
+{
+ private String string;
+
+ public static DERGeneralString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERGeneralString)
+ {
+ return (DERGeneralString) obj;
+ }
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERGeneralString(((ASN1OctetString) obj).getOctets());
+ }
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject) obj).getObject());
+ }
+ throw new IllegalArgumentException("illegal object in getInstance: "
+ + obj.getClass().getName());
+ }
+
+ public static DERGeneralString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ public DERGeneralString(byte[] string)
+ {
+ char[] cs = new char[string.length];
+ for (int i = 0; i != cs.length; i++) {
+ cs[i] = (char) (string[i] & 0xff);
+ }
+ this.string = new String(cs);
+ }
+
+ public DERGeneralString(String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public byte[] getOctets()
+ {
+ char[] cs = string.toCharArray();
+ byte[] bs = new byte[cs.length];
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte) cs[i];
+ }
+ return bs;
+ }
+
+ void encode(DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(GENERAL_STRING, this.getOctets());
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof DERGeneralString))
+ {
+ return false;
+ }
+ DERGeneralString s = (DERGeneralString) o;
+ return this.getString().equals(s.getString());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERGeneralizedTime.java b/src/main/java/com/lowagie/bc/asn1/DERGeneralizedTime.java
new file mode 100644
index 0000000..28cab51
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERGeneralizedTime.java
@@ -0,0 +1,188 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+
+/**
+ * Generalized time object.
+ */
+public class DERGeneralizedTime
+ extends DERObject
+{
+ String time;
+
+ /**
+ * return a generalized time from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERGeneralizedTime getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERGeneralizedTime)
+ {
+ return (DERGeneralizedTime)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERGeneralizedTime(((ASN1OctetString)obj).getOctets());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Generalized Time object from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERGeneralizedTime getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * The correct format for this is YYYYMMDDHHMMSSZ, or without the Z
+ * for local time, or Z+-HHMM on the end, for difference between local
+ * time and UTC time.
+ *
+ *
+ * @param time the time string.
+ */
+ public DERGeneralizedTime(
+ String time)
+ {
+ this.time = time;
+ }
+
+ /**
+ * base constructer from a java.util.date object
+ */
+ public DERGeneralizedTime(
+ Date time)
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+
+ dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
+
+ this.time = dateF.format(time);
+ }
+
+ DERGeneralizedTime(
+ byte[] bytes)
+ {
+ //
+ // explicitly convert to characters
+ //
+ char[] dateC = new char[bytes.length];
+
+ for (int i = 0; i != dateC.length; i++)
+ {
+ dateC[i] = (char)(bytes[i] & 0xff);
+ }
+
+ this.time = new String(dateC);
+ }
+
+ /**
+ * return the time - always in the form of
+ * YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm).
+ *
+ * Normally in a certificate we would expect "Z" rather than "GMT",
+ * however adding the "GMT" means we can just use:
+ *
+ * As DER requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputing SEQUENCE,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ Enumeration e = this.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject(obj);
+ }
+
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ out.writeEncoded(SEQUENCE | CONSTRUCTED, bytes);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERSet.java b/src/main/java/com/lowagie/bc/asn1/DERSet.java
new file mode 100644
index 0000000..fe4978c
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERSet.java
@@ -0,0 +1,70 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+
+/**
+ * A DER encoded set object
+ */
+public class DERSet
+ extends ASN1Set
+{
+ /**
+ * create an empty set
+ */
+ public DERSet()
+ {
+ }
+
+ /**
+ * @param obj - a single object that makes up the set.
+ */
+ public DERSet(
+ DEREncodable obj)
+ {
+ this.addObject(obj);
+ }
+
+ /**
+ * @param v - a vector of objects making up the set.
+ */
+ public DERSet(
+ DEREncodableVector v)
+ {
+ for (int i = 0; i != v.size(); i++)
+ {
+ this.addObject(v.get(i));
+ }
+ }
+
+ /*
+ * A note on the implementation:
+ *
+ * As DER requires the constructed, definite-length model to
+ * be used for structured types, this varies slightly from the
+ * ASN.1 descriptions given. Rather than just outputing SET,
+ * we also have to specify CONSTRUCTED, and the objects length.
+ */
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+ Enumeration e = this.getObjects();
+
+ while (e.hasMoreElements())
+ {
+ Object obj = e.nextElement();
+
+ dOut.writeObject(obj);
+ }
+
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ out.writeEncoded(SET | CONSTRUCTED, bytes);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERString.java b/src/main/java/com/lowagie/bc/asn1/DERString.java
new file mode 100644
index 0000000..44409b2
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERString.java
@@ -0,0 +1,9 @@
+package com.lowagie.bc.asn1;
+
+/**
+ * basic interface for DER string objects.
+ */
+public interface DERString
+{
+ public String getString();
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERT61String.java b/src/main/java/com/lowagie/bc/asn1/DERT61String.java
new file mode 100644
index 0000000..228fd49
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERT61String.java
@@ -0,0 +1,116 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER T61String (also the teletex string)
+ */
+public class DERT61String
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a T61 string from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERT61String getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERT61String)
+ {
+ return (DERT61String)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERT61String(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an T61 String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERT61String getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - with bytes.
+ */
+ public DERT61String(
+ byte[] string)
+ {
+ char[] cs = new char[string.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(string[i] & 0xff);
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor - with string.
+ */
+ public DERT61String(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(T61_STRING, this.getOctets());
+ }
+
+ public byte[] getOctets()
+ {
+ char[] cs = string.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERT61String))
+ {
+ return false;
+ }
+
+ return this.getString().equals(((DERT61String)o).getString());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERTaggedObject.java b/src/main/java/com/lowagie/bc/asn1/DERTaggedObject.java
new file mode 100644
index 0000000..dbc9e36
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERTaggedObject.java
@@ -0,0 +1,88 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * DER TaggedObject - in ASN.1 nottation this is any object proceeded by
+ * a [n] where n is some number - these are assume to follow the construction
+ * rules (as with sequences).
+ */
+public class DERTaggedObject
+ extends ASN1TaggedObject
+{
+ /**
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public DERTaggedObject(
+ int tagNo,
+ DEREncodable obj)
+ {
+ super(tagNo, obj);
+ }
+
+ /**
+ * @param explicit true if an explicitly tagged object.
+ * @param tagNo the tag number for this object.
+ * @param obj the tagged object.
+ */
+ public DERTaggedObject(
+ boolean explicit,
+ int tagNo,
+ DEREncodable obj)
+ {
+ super(explicit, tagNo, obj);
+ }
+
+ /**
+ * create an implicitly tagged object that contains a zero
+ * length sequence.
+ */
+ public DERTaggedObject(
+ int tagNo)
+ {
+ super(false, tagNo, new DERSequence());
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ if (!empty)
+ {
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+
+ dOut.writeObject(obj);
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ if (explicit)
+ {
+ out.writeEncoded(CONSTRUCTED | TAGGED | tagNo, bytes);
+ }
+ else
+ {
+ //
+ // need to mark constructed types...
+ //
+ if ((bytes[0] & CONSTRUCTED) != 0)
+ {
+ bytes[0] = (byte)(CONSTRUCTED | TAGGED | tagNo);
+ }
+ else
+ {
+ bytes[0] = (byte)(TAGGED | tagNo);
+ }
+
+ out.write(bytes);
+ }
+ }
+ else
+ {
+ out.writeEncoded(CONSTRUCTED | TAGGED | tagNo, new byte[0]);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERTags.java b/src/main/java/com/lowagie/bc/asn1/DERTags.java
new file mode 100644
index 0000000..7b5ddec
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERTags.java
@@ -0,0 +1,36 @@
+package com.lowagie.bc.asn1;
+
+public interface DERTags
+{
+ public static final int BOOLEAN = 0x01;
+ public static final int INTEGER = 0x02;
+ public static final int BIT_STRING = 0x03;
+ public static final int OCTET_STRING = 0x04;
+ public static final int NULL = 0x05;
+ public static final int OBJECT_IDENTIFIER = 0x06;
+ public static final int EXTERNAL = 0x08;
+ public static final int ENUMERATED = 0x0a;
+ public static final int SEQUENCE = 0x10;
+ public static final int SEQUENCE_OF = 0x10; // for completeness
+ public static final int SET = 0x11;
+ public static final int SET_OF = 0x11; // for completeness
+
+
+ public static final int NUMERIC_STRING = 0x12;
+ public static final int PRINTABLE_STRING = 0x13;
+ public static final int T61_STRING = 0x14;
+ public static final int VIDEOTEX_STRING = 0x15;
+ public static final int IA5_STRING = 0x16;
+ public static final int UTC_TIME = 0x17;
+ public static final int GENERALIZED_TIME = 0x18;
+ public static final int GRAPHIC_STRING = 0x19;
+ public static final int VISIBLE_STRING = 0x1a;
+ public static final int GENERAL_STRING = 0x1b;
+ public static final int UNIVERSAL_STRING = 0x1c;
+ public static final int BMP_STRING = 0x1e;
+ public static final int UTF8_STRING = 0x0c;
+
+ public static final int CONSTRUCTED = 0x20;
+ public static final int APPLICATION = 0x40;
+ public static final int TAGGED = 0x80;
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERUTCTime.java b/src/main/java/com/lowagie/bc/asn1/DERUTCTime.java
new file mode 100644
index 0000000..200b23e
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERUTCTime.java
@@ -0,0 +1,183 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+
+/**
+ * UTC time object.
+ */
+public class DERUTCTime
+ extends DERObject
+{
+ String time;
+
+ /**
+ * return an UTC Time from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERUTCTime getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERUTCTime)
+ {
+ return (DERUTCTime)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERUTCTime(((ASN1OctetString)obj).getOctets());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an UTC Time from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERUTCTime getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
+ * never encoded. When you're creating one of these objects from scratch, that's
+ * what you want to use, otherwise we'll try to deal with whatever gets read from
+ * the input stream... (this is why the input format is different from the getTime()
+ * method output).
+ *
+ *
+ * @param time the time string.
+ */
+ public DERUTCTime(
+ String time)
+ {
+ this.time = time;
+ }
+
+ /**
+ * base constructer from a java.util.date object
+ */
+ public DERUTCTime(
+ Date time)
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'");
+
+ dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
+
+ this.time = dateF.format(time);
+ }
+
+ DERUTCTime(
+ byte[] bytes)
+ {
+ //
+ // explicitly convert to characters
+ //
+ char[] dateC = new char[bytes.length];
+
+ for (int i = 0; i != dateC.length; i++)
+ {
+ dateC[i] = (char)(bytes[i] & 0xff);
+ }
+
+ this.time = new String(dateC);
+ }
+
+ /**
+ * return the time - always in the form of
+ * YYMMDDhhmmssGMT(+hh:mm|-hh:mm).
+ *
+ * Normally in a certificate we would expect "Z" rather than "GMT",
+ * however adding the "GMT" means we can just use:
+ *
+ * Note: In some cases, due to the local date processing, this
+ * may lead to unexpected results. If you want to stick the normal
+ * convention of 1950 to 2049 use the getAdjustedTime() method.
+ */
+ public String getTime()
+ {
+ //
+ // standardise the format.
+ //
+ if (time.length() == 11)
+ {
+ return time.substring(0, 10) + "00GMT+00:00";
+ }
+ else if (time.length() == 13)
+ {
+ return time.substring(0, 12) + "GMT+00:00";
+ }
+ else if (time.length() == 17)
+ {
+ return time.substring(0, 12) + "GMT" + time.substring(12, 15) + ":" + time.substring(15, 17);
+ }
+
+ return time;
+ }
+
+ /**
+ * return the time as an adjusted date with a 4 digit year. This goes
+ * in the range of 1950 - 2049.
+ */
+ public String getAdjustedTime()
+ {
+ String d = this.getTime();
+
+ if (d.charAt(0) < '5')
+ {
+ return "20" + d;
+ }
+ else
+ {
+ return "19" + d;
+ }
+ }
+
+ private byte[] getOctets()
+ {
+ char[] cs = time.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(UTC_TIME, this.getOctets());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERUTCTime))
+ {
+ return false;
+ }
+
+ return time.equals(((DERUTCTime)o).time);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERUTF8String.java b/src/main/java/com/lowagie/bc/asn1/DERUTF8String.java
new file mode 100644
index 0000000..13f992b
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERUTF8String.java
@@ -0,0 +1,177 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * DER UTF8String object.
+ */
+public class DERUTF8String
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return an UTF8 string from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERUTF8String getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERUTF8String)
+ {
+ return (DERUTF8String)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERUTF8String(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an UTF8 String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERUTF8String getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ DERUTF8String(
+ byte[] string)
+ {
+ int i = 0;
+ int length = 0;
+
+ while (i < string.length)
+ {
+ length++;
+ if ((string[i] & 0xe0) == 0xe0)
+ {
+ i += 3;
+ }
+ else if ((string[i] & 0xc0) == 0xc0)
+ {
+ i += 2;
+ }
+ else
+ {
+ i += 1;
+ }
+ }
+
+ char[] cs = new char[length];
+
+ i = 0;
+ length = 0;
+
+ while (i < string.length)
+ {
+ char ch;
+
+ if ((string[i] & 0xe0) == 0xe0)
+ {
+ ch = (char)(((string[i] & 0x1f) << 12)
+ | ((string[i + 1] & 0x3f) << 6) | (string[i + 2] & 0x3f));
+ i += 3;
+ }
+ else if ((string[i] & 0xc0) == 0xc0)
+ {
+ ch = (char)(((string[i] & 0x3f) << 6) | (string[i + 1] & 0x3f));
+ i += 2;
+ }
+ else
+ {
+ ch = (char)(string[i] & 0xff);
+ i += 1;
+ }
+
+ cs[length++] = ch;
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERUTF8String(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DERUTF8String))
+ {
+ return false;
+ }
+
+ DERUTF8String s = (DERUTF8String)o;
+
+ return this.getString().equals(s.getString());
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ char[] c = string.toCharArray();
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ for (int i = 0; i != c.length; i++)
+ {
+ char ch = c[i];
+
+ if (ch < 0x0080)
+ {
+ bOut.write(ch);
+ }
+ else if (ch < 0x0800)
+ {
+ bOut.write(0xc0 | (ch >> 6));
+ bOut.write(0x80 | (ch & 0x3f));
+ }
+ else
+ {
+ bOut.write(0xe0 | (ch >> 12));
+ bOut.write(0x80 | ((ch >> 6) & 0x3F));
+ bOut.write(0x80 | (ch & 0x3F));
+ }
+ }
+
+ out.writeEncoded(UTF8_STRING, bOut.toByteArray());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERUniversalString.java b/src/main/java/com/lowagie/bc/asn1/DERUniversalString.java
new file mode 100644
index 0000000..eed5f08
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERUniversalString.java
@@ -0,0 +1,110 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * DER UniversalString object.
+ */
+public class DERUniversalString
+ extends DERObject
+ implements DERString
+{
+ private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ private byte[] string;
+
+ /**
+ * return a Universal String from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERUniversalString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERUniversalString)
+ {
+ return (DERUniversalString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERUniversalString(((ASN1OctetString)obj).getOctets());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Universal String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERUniversalString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ public DERUniversalString(
+ byte[] string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ StringBuffer buf = new StringBuffer("#");
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream aOut = new ASN1OutputStream(bOut);
+
+ try
+ {
+ aOut.writeObject(this);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("internal error encoding BitString");
+ }
+
+ byte[] string = bOut.toByteArray();
+
+ for (int i = 0; i != string.length; i++)
+ {
+ buf.append(table[(string[i] >>> 4) % 0xf]);
+ buf.append(table[string[i] & 0xf]);
+ }
+
+ return buf.toString();
+ }
+
+ public byte[] getOctets()
+ {
+ return string;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(UNIVERSAL_STRING, this.getOctets());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERUniversalString))
+ {
+ return false;
+ }
+
+ return this.getString().equals(((DERUniversalString)o).getString());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERUnknownTag.java b/src/main/java/com/lowagie/bc/asn1/DERUnknownTag.java
new file mode 100644
index 0000000..fb653ba
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERUnknownTag.java
@@ -0,0 +1,73 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * We insert one of these when we find a tag we don't recognise.
+ */
+public class DERUnknownTag
+ extends DERObject
+{
+ int tag;
+ byte[] data;
+
+ /**
+ * @param tag the tag value.
+ * @param data the octets making up the time.
+ */
+ public DERUnknownTag(
+ int tag,
+ byte[] data)
+ {
+ this.tag = tag;
+ this.data = data;
+ }
+
+ public int getTag()
+ {
+ return tag;
+ }
+
+ public byte[] getData()
+ {
+ return data;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(tag, data);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERUnknownTag))
+ {
+ return false;
+ }
+
+ DERUnknownTag other = (DERUnknownTag)o;
+
+ if(tag != other.tag)
+ {
+ return false;
+ }
+
+ if(data.length != other.data.length)
+ {
+ return false;
+ }
+
+ for(int i = 0; i < data.length; i++)
+ {
+ if(data[i] != other.data[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERVisibleString.java b/src/main/java/com/lowagie/bc/asn1/DERVisibleString.java
new file mode 100644
index 0000000..629f677
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERVisibleString.java
@@ -0,0 +1,116 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER VisibleString object.
+ */
+public class DERVisibleString
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a Visible String from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERVisibleString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERVisibleString)
+ {
+ return (DERVisibleString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERVisibleString(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Visible String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERVisibleString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ public DERVisibleString(
+ byte[] string)
+ {
+ char[] cs = new char[string.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(string[i] & 0xff);
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERVisibleString(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public byte[] getOctets()
+ {
+ char[] cs = string.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(VISIBLE_STRING, this.getOctets());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERVisibleString))
+ {
+ return false;
+ }
+
+ return this.getString().equals(((DERVisibleString)o).getString());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/OIDTokenizer.java b/src/main/java/com/lowagie/bc/asn1/OIDTokenizer.java
new file mode 100644
index 0000000..16d3445
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/OIDTokenizer.java
@@ -0,0 +1,48 @@
+package com.lowagie.bc.asn1;
+
+/**
+ * class for breaking up an OID into it's component tokens, ala
+ * java.util.StringTokenizer. We need this class as some of the
+ * lightweight Java environment don't support classes like
+ * StringTokenizer.
+ */
+public class OIDTokenizer
+{
+ private String oid;
+ private int index;
+
+ public OIDTokenizer(
+ String oid)
+ {
+ this.oid = oid;
+ this.index = 0;
+ }
+
+ public boolean hasMoreTokens()
+ {
+ return (index != -1);
+ }
+
+ public String nextToken()
+ {
+ if (index == -1)
+ {
+ return null;
+ }
+
+ String token;
+ int end = oid.indexOf('.', index);
+
+ if (end == -1)
+ {
+ token = oid.substring(index);
+ index = -1;
+ return token;
+ }
+
+ token = oid.substring(index, end);
+
+ index = end + 1;
+ return token;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/Anchor.java b/src/main/java/com/lowagie/text/Anchor.java
new file mode 100644
index 0000000..33e6011
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Anchor.java
@@ -0,0 +1,365 @@
+/*
+ * $Id: Anchor.java,v 1.84 2005/05/03 13:03:49 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Properties;
+
+import com.lowagie.text.markup.MarkupTags;
+import com.lowagie.text.markup.MarkupParser;
+
+/**
+ * An
+ * An
+ * Example:
+ *
+ * A
+ * Example:
+ *
+ * The
+ * if the element is a
+ * Remark: you can't add
+ * A chapter number has to be created using a
+ * Example:
+ *
+ * Most elements can be divided in one or more
+ * Example:
+ * It can be used to implement sub/superscript.
+ *
+ * @param rise
+ * the displacement in points
+ * @return this
+ * There must be a local destination matching the name.
+ *
+ * @param name
+ * the name of the destination to go to
+ * @return this
+ * The text for this tag can be retrieved with
+ * It may be null.
+ *
+ * @return the attributes for this
+ * The outputstream of every writer implementing
+ *
+ * This method should be overriden in the specific
+ * This method should be overriden in the specific
+ * This does nothing. Has to be overridden if needed.
+ *
+ * @param marginLeft the margin on the left
+ * @param marginRight the margin on the right
+ * @param marginTop the margin on the top
+ * @param marginBottom the margin on the bottom
+ * @return
+ * This does nothing. Has to be overridden if needed.
+ *
+ * @return
+ * This method should be overriden in the specific
+ * This method should be overriden in the specific
+ * This method should be overriden in the specific
+ * This method should be overriden in the specific
+ * This method should be overriden in the specific
+ * This method should be overriden in the specific
+ * All kinds of Text-elements can be added to a
+ * Remark:
+ *
+ * Once the document is opened, you can't write any Header- or
+ * Meta-information anymore. You have to open the document before you can
+ * begin to add content to the body of the document.
+ */
+
+ public void open() {
+ if (!close) {
+ open = true;
+ }
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.setPageSize(pageSize);
+ listener.setMargins(marginLeft, marginRight, marginTop,
+ marginBottom);
+ listener.open();
+ }
+ }
+
+ /**
+ * Sets the pagesize.
+ *
+ * @param pageSize
+ * the new pagesize
+ * @return a
+ * Once all the content has been written in the body, you have to close the
+ * body. After that nothing can be written to the body anymore.
+ */
+
+ public void close() {
+ if (!close) {
+ open = false;
+ close = true;
+ }
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.close();
+ }
+ }
+
+ // methods concerning the header or some meta information
+
+ /**
+ * Adds a user defined header to the document.
+ *
+ * @param name
+ * the name of the header
+ * @param content
+ * the content of the header
+ * @return
+ * Note: it will not work with {@link Table}.
+ *
+ * @param marginMirroring
+ *
+ * Remark: I looked at the interface javax.swing.text.Element, but I decided to
+ * write my own text-classes for two reasons:
+ *
+ * Example:
+ * If so, the standard should be used.
+ *
+ * @return a
+ * If you want to use this
+ * Example:
+ *
+ * Example:
+ *
+ * Remark: this only makes sense for Images of the type
+ * Remark: this only makes sense for Images of the type
+ * Remark: this only makes sense for Images of the type
+ * Remark: this only makes sense for Images of the type
+ * This method makes the conversion of this library from the JAVA 2 platform
+ * to a JDK1.1.x-version easier.
+ *
+ * @param filename
+ * a given filename
+ * @return a valid URL
+ * @throws MalformedURLException
+ */
+
+ public static URL toURL(String filename) throws MalformedURLException {
+ if (filename.startsWith("file:/") || filename.startsWith("http://")
+ || filename.startsWith("https://")
+ || filename.startsWith("jar:")) {
+ return new URL(filename);
+ }
+ File f = new File(filename);
+ String path = f.getAbsolutePath();
+ if (File.separatorChar != '/') {
+ path = path.replace(File.separatorChar, '/');
+ }
+ if (!path.startsWith("/")) {
+ path = "/" + path;
+ }
+ if (!path.endsWith("/") && f.isDirectory()) {
+ path = path + "/";
+ }
+ char[] t = path.toCharArray();
+ StringBuffer sb = new StringBuffer();
+ for (int k = 0; k < t.length; ++k) {
+ char c = t[k];
+ int a = excUri.indexOf(c);
+ if (a >= 0)
+ sb.append(excUriEsc[a]);
+ else
+ sb.append(c);
+ }
+ return new URL("file", "", sb.toString());
+ }
+
+ /**
+ * Unescapes an URL. All the "%xx" are replaced by the 'xx' hex char value.
+ * @param src the url to unescape
+ * @return the eunescaped value
+ */
+ public static String unEscapeURL(String src) {
+ StringBuffer bf = new StringBuffer();
+ char[] s = src.toCharArray();
+ for (int k = 0; k < s.length; ++k) {
+ char c = s[k];
+ if (c == '%') {
+ if (k + 2 >= s.length) {
+ bf.append(c);
+ continue;
+ }
+ int a0 = PRTokeniser.getHex((int)s[k + 1]);
+ int a1 = PRTokeniser.getHex((int)s[k + 2]);
+ if (a0 < 0 || a1 < 0) {
+ bf.append(c);
+ continue;
+ }
+ bf.append((char)(a0 * 16 + a1));
+ k += 2;
+ }
+ else
+ bf.append(c);
+ }
+ return bf.toString();
+ }
+
+ /**
+ * Returns the transparency.
+ *
+ * @return the transparency values
+ */
+
+ public int[] getTransparency() {
+ return transparency;
+ }
+
+ /**
+ * Sets the transparency values
+ *
+ * @param transparency
+ * the transparency values
+ */
+ public void setTransparency(int transparency[]) {
+ this.transparency = transparency;
+ }
+
+ /**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag
+ * the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.IMAGE.equals(tag);
+ }
+
+ /**
+ * Gets the plain width of the image.
+ *
+ * @return a value
+ */
+
+ public float plainWidth() {
+ return plainWidth;
+ }
+
+ /**
+ * Gets the plain height of the image.
+ *
+ * @return a value
+ */
+
+ public float plainHeight() {
+ return plainHeight;
+ }
+
+ static protected synchronized Long getSerialId() {
+ ++serialId;
+ return new Long(serialId);
+ }
+
+ /**
+ * Returns a serial id for the Image (reuse the same image more than once)
+ *
+ * @return a serialId
+ */
+ public Long getMySerialId() {
+ return mySerialId;
+ }
+
+ /**
+ * Gets the dots-per-inch in the X direction. Returns 0 if not available.
+ *
+ * @return the dots-per-inch in the X direction
+ */
+ public int getDpiX() {
+ return dpiX;
+ }
+
+ /**
+ * Gets the dots-per-inch in the Y direction. Returns 0 if not available.
+ *
+ * @return the dots-per-inch in the Y direction
+ */
+ public int getDpiY() {
+ return dpiY;
+ }
+
+ /**
+ * Sets the dots per inch value
+ *
+ * @param dpiX
+ * dpi for x coordinates
+ * @param dpiY
+ * dpi for y coordinates
+ */
+ public void setDpi(int dpiX, int dpiY) {
+ this.dpiX = dpiX;
+ this.dpiY = dpiY;
+ }
+
+ /**
+ * Returns
+ * Example 1:
+ *
+ * Remark: the parameter symbolIndent is important for instance when
+ * generating PDF-documents; it indicates the indentation of the listsymbol.
+ * It is not important for HTML-documents.
+ *
+ * @param numbered a boolean
+ * @param symbolIndent the indentation that has to be used for the listsymbol
+ */
+
+ public List(boolean numbered, float symbolIndent) {
+ this.numbered = numbered;
+ this.lettered = false;
+ this.symbolIndent = symbolIndent;
+ }
+
+ /**
+ * Creates a list
+ * @param numbered has the list to be numbered?
+ * @param lettered has the list to be 'numbered' with letters
+ * @param symbolIndent the indentation of the symbol
+ */
+ public List(boolean numbered, boolean lettered, float symbolIndent ) {
+ this.numbered = numbered;
+ this.lettered = lettered;
+ this.symbolIndent = symbolIndent;
+ }
+
+ /**
+ * Returns a
+ * This is a shortcut for
+ * Example 1:
+ *
+ * An object of type
+ * A
+ * A
+ * Example:
+ *
+ * This method is a hack to solve a problem I had with phrases that were split between chunks
+ * in the wrong place.
+ * @param chunk a Chunk to add to the Phrase
+ * @return true if adding the Chunk succeeded
+ */
+
+ private synchronized boolean addChunk(Chunk chunk) {
+ if (!font.isStandardFont()) {
+ chunk.setFont(font.difference(chunk.font()));
+ }
+ if (size() > 0 && !chunk.hasAttributes()) {
+ try {
+ Chunk previous = (Chunk) get(size() - 1);
+ if (!previous.hasAttributes() && previous.font().compareTo(chunk.font()) == 0 && !"".equals(previous.content().trim()) && !"".equals(chunk.content().trim())) {
+ previous.append(chunk.content());
+ return true;
+ }
+ }
+ catch(ClassCastException cce) {
+ }
+ }
+ return super.add(chunk);
+ }
+
+/**
+ * Adds a collection of
+ * All
+ * Since a
+ * Remark: you can not construct a
+ * Example:
+ *
+ * If the numberdepth is 0, the sections will not be numbered. If the numberdepth
+ * is 1, the section will be numbered with their own number. If the numberdepth is
+ * higher (for instance x > 1), the numbers of x - 1 parents will be shown.
+ *
+ * @param numberDepth the new numberDepth
+ */
+
+ public void setNumberDepth(int numberDepth) {
+ this.numberDepth = numberDepth;
+ }
+
+/**
+ * Sets the indentation of this
+ * When you construct a
+ * The default implementation is:
+ *
+ *
+ * Tables that span multiple pages are cut into different parts automatically.
+ * If you want a table header to be repeated on every page, you may not forget to
+ * mark the end of the header section by using the method
+ * The matrix of a table is not necessarily an m x n-matrix. It can contain holes
+ * or cells that are bigger than the unit. Believe me or not, but it took some serious
+ * thinking to make this as userfriendly as possible. I hope you wil find the result
+ * quite simple (I love simple solutions, especially for complex problems).
+ * I didn't want it to be something as complex as the Java
+ * Example:
+ *
+ * When a table doesn't fit a page, it is split in two parts.
+ * If you want to avoid this, you should set the tableFitsPage value to true.
+ *
+ * @param fitPage enter true if you don't want to split cells
+ */
+
+ public void setTableFitsPage(boolean fitPage) {
+ this.tableFitsPage = fitPage;
+ if (fitPage) setCellsFitPage(true);
+ }
+
+ /**
+ * Allows you to control when a page break occurs.
+ *
+ * When a cell doesn't fit a page, it is split in two parts.
+ * If you want to avoid this, you should set the cellsFitPage value to true.
+ *
+ * @param fitPage enter true if you don't want to split cells
+ */
+
+ public void setCellsFitPage(boolean fitPage) {
+ this.cellsFitPage = fitPage;
+ }
+
+ /**
+ * Checks if this
+ * This is a shortcut for
+ * This is a shortcut for
+ * This is a shortcut for
+ * This is a shortcut for
+ * You can give up relative values of borderwidths.
+ * The sum of these values will be considered 100%.
+ * The values will be recalculated as percentages of this sum.
+ *
+ * example:
+ *
+ * You can give up relative values of borderwidths.
+ * The sum of these values will be considered 100%.
+ * The values will be recalculated as percentages of this sum.
+ *
+ * @param widths an array with values
+ * @throws DocumentException
+ */
+
+ public void setWidths(int[] widths) throws DocumentException {
+ float tb[] = new float[widths.length];
+ for (int k = 0; k < widths.length; ++k)
+ tb[k] = widths[k];
+ setWidths(tb);
+ }
+ // methods to retrieve the membervariables
+
+ /**
+ * Gets the number of columns.
+ *
+ * @return a value
+ */
+
+ public int columns() {
+ return columns;
+ }
+
+ /**
+ * Gets the number of rows in this
+ *
+ * This method translates the widths expressed in percentages into the
+ * x-coordinate of the borders of the columns on a real document.
+ *
+ * @param left this is the position of the first border at the left (cellpadding not included)
+ * @param totalWidth this is the space between the first border at the left
+ * and the last border at the right (cellpadding not included)
+ * @return an array with borderpositions
+ */
+
+ public float[] getWidths(float left, float totalWidth) {
+ // for x columns, there are x+1 borders
+ float[] w = new float[columns + 1];
+ // the border at the left is calculated
+ switch(alignment) {
+ case Element.ALIGN_LEFT:
+ w[0] = left;
+ break;
+ case Element.ALIGN_RIGHT:
+ w[0] = left + (totalWidth * (100 - widthPercentage)) / 100;
+ break;
+ case Element.ALIGN_CENTER:
+ default:
+ w[0] = left + (totalWidth * (100 - widthPercentage)) / 200;
+ }
+ // the total available width is changed
+ totalWidth = (totalWidth * widthPercentage) / 100;
+ // the inner borders are calculated
+ for (int i = 1; i < columns; i++) {
+ w[i] = w[i - 1] + (widths[i - 1] * totalWidth / 100);
+ }
+ // the border at the right is calculated
+ w[columns] = w[0] + totalWidth;
+ return w;
+ }
+
+ /**
+ * Sets current col/row to valid(empty) pos after addCell/Table
+ * @param aLocation a location in the Table
+ */
+ private void setCurrentLocationToNextValidPosition(Point aLocation) {
+ // set latest location to next valid position
+ int i, j;
+ i = aLocation.x;
+ j = aLocation.y;
+ do {
+ if ( (j + 1) == columns ) { // goto next row
+ i++;
+ j = 0;
+ }
+ else {
+ j++;
+ }
+ }
+ while (
+ (i < rows.size()) && (j < columns) && (((Row) rows.get(i)).isReserved(j) == true)
+ );
+ curPosition = new Point(i, j);
+ }
+
+
+
+ /**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.TABLE.equals(tag);
+ }
+
+ /**
+ * Allows clients to set up alternating attributes for each Row in the Table.
+ *
+ * This code was contributed by Matt Benson.
+ *
+ * @param name the name of the attribute
+ * @param value0 the value of the attribute for even rows
+ * @param value1 the value of the attribute for odd rows
+ */
+ public void setAlternatingRowAttribute(String name, String value0, String value1) {
+ if (value0 == null || value1 == null) {
+ throw new NullPointerException("MarkupTable#setAlternatingRowAttribute(): null values are not permitted.");
+ }
+ if (alternatingRowAttributes == null) alternatingRowAttributes = new Hashtable();
+
+ // we could always use new Arrays but this is big enough
+ String[] value = (String[])(alternatingRowAttributes.get(name));
+ if (value == null) value = new String[2];
+ value[0] = value0;
+ value[1] = value1;
+ alternatingRowAttributes.put(name, value);
+ }
+
+ /**
+ * This method throws an
+ * To convert the
+ * Example:
+ *
+ * for more info: see O'Reilly; "HTML: The Definitive Guide" (page 164)
+ *
+ * @author mario.maccarini@rug.ac.be
+ */
+
+public class HtmlEncoder {
+
+ // membervariables
+
+/** List with the HTML translation of all the characters. */
+ private static final String[] htmlCode = new String[256];
+
+ static {
+ for (int i = 0; i < 10; i++) {
+ htmlCode[i] = "" + i + ";";
+ }
+
+ for (int i = 10; i < 32; i++) {
+ htmlCode[i] = "" + i + ";";
+ }
+
+ for (int i = 32; i < 128; i++) {
+ htmlCode[i] = String.valueOf((char)i);
+ }
+
+ // Special characters
+ htmlCode['\t'] = "\t";
+ htmlCode['\n'] = "<" + HtmlTags.NEWLINE + " />\n";
+ htmlCode['\"'] = """; // double quote
+ htmlCode['&'] = "&"; // ampersand
+ htmlCode['<'] = "<"; // lower than
+ htmlCode['>'] = ">"; // greater than
+
+ for (int i = 128; i < 256; i++) {
+ htmlCode[i] = "" + i + ";";
+ }
+ }
+
+
+ // constructors
+
+/**
+ * This class will never be constructed.
+ *
+ * HtmlEncoder only contains static methods.
+ */
+
+ private HtmlEncoder () { }
+
+ // methods
+
+/**
+ * Converts a
+ * An
+ * Example:
+ *
+ * The
+ * This method writes some comment.
+ *
+ * @param comment the comment that has to be written
+ * @throws IOException
+ */
+
+ protected void writeComment(String comment) throws IOException {
+ addTabs(2);
+ os.write(BEGINCOMMENT);
+ write(comment);
+ os.write(ENDCOMMENT);
+ }
+
+ // public methods
+
+/**
+ * Changes the standardfont.
+ *
+ * @param standardfont The font
+ */
+
+ public void setStandardFont(Font standardfont) {
+ this.standardfont = standardfont;
+ }
+
+/**
+ * Checks if a given font is the same as the font that was last used.
+ *
+ * @param font the font of an object
+ * @return true if the font differs
+ */
+
+ public boolean isOtherFont(Font font) {
+ try {
+ Font cFont = (Font) currentfont.peek();
+ if (cFont.compareTo(font) == 0) return false;
+ return true;
+ }
+ catch(EmptyStackException ese) {
+ if (standardfont.compareTo(font) == 0) return false;
+ return true;
+ }
+ }
+
+/**
+ * Sets the basepath for images.
+ *
+ * This is especially useful if you add images using a file,
+ * rather than an URL. In PDF there is no problem, since
+ * the images are added inline, but in HTML it is sometimes
+ * necessary to use a relative path or a special path to some
+ * images directory.
+ *
+ * @param imagepath the new imagepath
+ */
+
+ public void setImagepath(String imagepath) {
+ this.imagepath = imagepath;
+ }
+
+/**
+ * Resets the imagepath.
+ */
+
+ public void resetImagepath() {
+ imagepath = null;
+ }
+
+/**
+ * Changes the header of this document.
+ *
+ * @param header the new header
+ */
+
+ public void setHeader(HeaderFooter header) {
+ this.header = header;
+ }
+
+/**
+ * Changes the footer of this document.
+ *
+ * @param footer the new footer
+ */
+
+ public void setFooter(HeaderFooter footer) {
+ this.footer = footer;
+ }
+
+/**
+ * Signals that a
+ * An example:
+ *
+ *
+ * If the field does not exist or is invalid it returns
+ *
+ *
+ *
+ *
+ *
+ *
+ * The bars and text are written in the following colors:
+ * Result bars and text painted with current fill color bars and text painted with bars painted with current color bars painted with
+ * The code types allowed are:
+ * The bars and text are written in the following colors:
+ * Result bars and text painted with current fill color bars and text painted with bars painted with current color bars painted with
+ *
+ * The bars and text are written in the following colors:
+ * Result bars and text painted with current fill color bars and text painted with bars painted with current color bars painted with
+ * The bars and text are written in the following colors:
+ * Result bars and text painted with current fill color bars and text painted with bars painted with current color bars painted with
+ * The bars and text are written in the following colors:
+ * Result bars and text painted with current fill color bars and text painted with bars painted with current color bars painted with
+ * The default parameters are:
+ *
+ * The bars and text are written in the following colors:
+ * Result bars and text painted with current fill color bars and text painted with bars painted with current color bars painted with
+ * The bars and text are written in the following colors:
+ * Result bars and text painted with current fill color bars and text painted with bars painted with current color bars painted with
+ * The bars and text are written in the following colors:
+ * Result bars and text painted with current fill color bars and text painted with bars painted with current color bars painted with
+ * The fonts are cached and if they already exist they are extracted from the cache,
+ * not parsed again.
+ *
+ * This method calls:
+ * The fonts may or may not be cached depending on the flag
+ * This implementation is not optimized for performance. It is intended
+ * as a reference implementation that closely follows the specification
+ * of the Bidirectional Algorithm in The Unicode Standard version 3.0.
+ *
+ * Input: Output:
+ * As the algorithm is defined to operate on a single paragraph at a time,
+ * this implementation is written to handle single paragraphs. Thus
+ * rule P1 is presumed by this implementation-- the data provided to the
+ * implementation is assumed to be a single paragraph, and either contains no
+ * 'B' codes, or a single 'B' code at the end of the input. 'B' is allowed
+ * as input to illustrate how the algorithm assigns it a level.
+ *
+ * Also note that rules L3 and L4 depend on the rendering engine that uses
+ * the result of the bidi algorithm. This implementation assumes that the
+ * rendering engine expects combining marks in visual order (e.g. to the
+ * left of their base character in RTL runs) and that it adjust the glyphs
+ * used to render mirrored characters that are in RTL runs so that they
+ * render appropriately.
+ *
+ * @author Doug Felt
+ */
+
+public final class BidiOrder {
+ private byte[] initialTypes;
+ private byte[] embeddings; // generated from processing format codes
+ private byte paragraphEmbeddingLevel = -1; // undefined
+
+ private int textLength; // for convenience
+ private byte[] resultTypes; // for paragraph, not lines
+ private byte[] resultLevels; // for paragraph, not lines
+
+ // The bidi types
+
+ /** Left-to-right*/
+ public static final byte L = 0;
+
+ /** Left-to-Right Embedding */
+ public static final byte LRE = 1;
+
+ /** Left-to-Right Override */
+ public static final byte LRO = 2;
+
+ /** Right-to-Left */
+ public static final byte R = 3;
+
+ /** Right-to-Left Arabic */
+ public static final byte AL = 4;
+
+ /** Right-to-Left Embedding */
+ public static final byte RLE = 5;
+
+ /** Right-to-Left Override */
+ public static final byte RLO = 6;
+
+ /** Pop Directional Format */
+ public static final byte PDF = 7;
+
+ /** European Number */
+ public static final byte EN = 8;
+
+ /** European Number Separator */
+ public static final byte ES = 9;
+
+ /** European Number Terminator */
+ public static final byte ET = 10;
+
+ /** Arabic Number */
+ public static final byte AN = 11;
+
+ /** Common Number Separator */
+ public static final byte CS = 12;
+
+ /** Non-Spacing Mark */
+ public static final byte NSM = 13;
+
+ /** Boundary Neutral */
+ public static final byte BN = 14;
+
+ /** Paragraph Separator */
+ public static final byte B = 15;
+
+ /** Segment Separator */
+ public static final byte S = 16;
+
+ /** Whitespace */
+ public static final byte WS = 17;
+
+ /** Other Neutrals */
+ public static final byte ON = 18;
+
+ /** Minimum bidi type value. */
+ public static final byte TYPE_MIN = 0;
+
+ /** Maximum bidi type value. */
+ public static final byte TYPE_MAX = 18;
+
+ //
+ // Input
+ //
+
+ /**
+ * Initialize using an array of direction types. Types range from TYPE_MIN to TYPE_MAX inclusive
+ * and represent the direction codes of the characters in the text.
+ *
+ * @param types the types array
+ */
+ public BidiOrder(byte[] types) {
+ validateTypes(types);
+
+ this.initialTypes = (byte[])types.clone(); // client type array remains unchanged
+
+ runAlgorithm();
+ }
+
+ /**
+ * Initialize using an array of direction types and an externally supplied paragraph embedding level.
+ * The embedding level may be -1, 0, or 1. -1 means to apply the default algorithm (rules P2 and P3),
+ * 0 is for LTR paragraphs, and 1 is for RTL paragraphs.
+ *
+ * @param types the types array
+ * @param paragraphEmbeddingLevel the externally supplied paragraph embedding level.
+ */
+ public BidiOrder(byte[] types, byte paragraphEmbeddingLevel) {
+ validateTypes(types);
+ validateParagraphEmbeddingLevel(paragraphEmbeddingLevel);
+
+ this.initialTypes = (byte[])types.clone(); // client type array remains unchanged
+ this.paragraphEmbeddingLevel = paragraphEmbeddingLevel;
+
+ runAlgorithm();
+ }
+
+ public BidiOrder(char text[], int offset, int length, byte paragraphEmbeddingLevel) {
+ initialTypes = new byte[length];
+ for (int k = 0; k < length; ++k) {
+ initialTypes[k] = rtypes[text[offset + k]];
+ }
+ validateParagraphEmbeddingLevel(paragraphEmbeddingLevel);
+
+ this.paragraphEmbeddingLevel = paragraphEmbeddingLevel;
+
+ runAlgorithm();
+ }
+
+ public final static byte getDirection(char c) {
+ return rtypes[c];
+ }
+
+ /**
+ * The algorithm.
+ * Does not include line-based processing (Rules L1, L2).
+ * These are applied later in the line-based phase of the algorithm.
+ */
+ private void runAlgorithm() {
+ textLength = initialTypes.length;
+
+ // Initialize output types.
+ // Result types initialized to input types.
+ resultTypes = (byte[])initialTypes.clone();
+
+
+ // 1) determining the paragraph level
+ // Rule P1 is the requirement for entering this algorithm.
+ // Rules P2, P3.
+ // If no externally supplied paragraph embedding level, use default.
+ if (paragraphEmbeddingLevel == -1) {
+ determineParagraphEmbeddingLevel();
+ }
+
+ // Initialize result levels to paragraph embedding level.
+ resultLevels = new byte[textLength];
+ setLevels(0, textLength, paragraphEmbeddingLevel);
+
+ // 2) Explicit levels and directions
+ // Rules X1-X8.
+ determineExplicitEmbeddingLevels();
+
+ // Rule X9.
+ textLength = removeExplicitCodes();
+
+ // Rule X10.
+ // Run remainder of algorithm one level run at a time
+ byte prevLevel = paragraphEmbeddingLevel;
+ int start = 0;
+ while (start < textLength) {
+ byte level = resultLevels[start];
+ byte prevType = typeForLevel(Math.max(prevLevel, level));
+
+ int limit = start + 1;
+ while (limit < textLength && resultLevels[limit] == level) {
+ ++limit;
+ }
+
+ byte succLevel = limit < textLength ? resultLevels[limit] : paragraphEmbeddingLevel;
+ byte succType = typeForLevel(Math.max(succLevel, level));
+
+ // 3) resolving weak types
+ // Rules W1-W7.
+ resolveWeakTypes(start, limit, level, prevType, succType);
+
+ // 4) resolving neutral types
+ // Rules N1-N3.
+ resolveNeutralTypes(start, limit, level, prevType, succType);
+
+ // 5) resolving implicit embedding levels
+ // Rules I1, I2.
+ resolveImplicitLevels(start, limit, level, prevType, succType);
+
+ prevLevel = level;
+ start = limit;
+ }
+
+ // Reinsert explicit codes and assign appropriate levels to 'hide' them.
+ // This is for convenience, so the resulting level array maps 1-1
+ // with the initial array.
+ // See the implementation suggestions section of TR#9 for guidelines on
+ // how to implement the algorithm without removing and reinserting the codes.
+ textLength = reinsertExplicitCodes(textLength);
+ }
+
+ /**
+ * 1) determining the paragraph level.
+ *
+ * Rules P2, P3.
+ *
+ * At the end of this function, the member variable paragraphEmbeddingLevel is set to either 0 or 1.
+ */
+ private void determineParagraphEmbeddingLevel() {
+ byte strongType = -1; // unknown
+
+ // Rule P2.
+ for (int i = 0; i < textLength; ++i) {
+ byte t = resultTypes[i];
+ if (t == L || t == AL || t == R) {
+ strongType = t;
+ break;
+ }
+ }
+
+ // Rule P3.
+ if (strongType == -1) { // none found
+ // default embedding level when no strong types found is 0.
+ paragraphEmbeddingLevel = 0;
+ } else if (strongType == L) {
+ paragraphEmbeddingLevel = 0;
+ } else { // AL, R
+ paragraphEmbeddingLevel = 1;
+ }
+ }
+
+ /**
+ * Process embedding format codes.
+ *
+ * Calls processEmbeddings to generate an embedding array from the explicit format codes. The
+ * embedding overrides in the array are then applied to the result types, and the result levels are
+ * initialized.
+ * @see #processEmbeddings
+ */
+ private void determineExplicitEmbeddingLevels() {
+ embeddings = processEmbeddings(resultTypes, paragraphEmbeddingLevel);
+
+ for (int i = 0; i < textLength; ++i) {
+ byte level = embeddings[i];
+ if ((level & 0x80) != 0) {
+ level &= 0x7f;
+ resultTypes[i] = typeForLevel(level);
+ }
+ resultLevels[i] = level;
+ }
+ }
+
+ /**
+ * Rules X9.
+ * Remove explicit codes so that they may be ignored during the remainder
+ * of the main portion of the algorithm. The length of the resulting text
+ * is returned.
+ * @return the length of the data excluding explicit codes and BN.
+ */
+ private int removeExplicitCodes() {
+ int w = 0;
+ for (int i = 0; i < textLength; ++i) {
+ byte t = initialTypes[i];
+ if (!(t == LRE || t == RLE || t == LRO || t == RLO || t == PDF || t == BN)) {
+ embeddings[w] = embeddings[i];
+ resultTypes[w] = resultTypes[i];
+ resultLevels[w] = resultLevels[i];
+ w++;
+ }
+ }
+ return w; // new textLength while explicit levels are removed
+ }
+
+ /**
+ * Reinsert levels information for explicit codes.
+ * This is for ease of relating the level information
+ * to the original input data. Note that the levels
+ * assigned to these codes are arbitrary, they're
+ * chosen so as to avoid breaking level runs.
+ * @param textLength the length of the data after compression
+ * @return the length of the data (original length of
+ * types array supplied to constructor)
+ */
+ private int reinsertExplicitCodes(int textLength) {
+ for (int i = initialTypes.length; --i >= 0;) {
+ byte t = initialTypes[i];
+ if (t == LRE || t == RLE || t == LRO || t == RLO || t == PDF || t == BN) {
+ embeddings[i] = 0;
+ resultTypes[i] = t;
+ resultLevels[i] = -1;
+ } else {
+ --textLength;
+ embeddings[i] = embeddings[textLength];
+ resultTypes[i] = resultTypes[textLength];
+ resultLevels[i] = resultLevels[textLength];
+ }
+ }
+
+ // now propagate forward the levels information (could have
+ // propagated backward, the main thing is not to introduce a level
+ // break where one doesn't already exist).
+
+ if (resultLevels[0] == -1) {
+ resultLevels[0] = paragraphEmbeddingLevel;
+ }
+ for (int i = 1; i < initialTypes.length; ++i) {
+ if (resultLevels[i] == -1) {
+ resultLevels[i] = resultLevels[i-1];
+ }
+ }
+
+ // Embedding information is for informational purposes only
+ // so need not be adjusted.
+
+ return initialTypes.length;
+ }
+
+ /**
+ * 2) determining explicit levels
+ * Rules X1 - X8
+ *
+ * The interaction of these rules makes handling them a bit complex.
+ * This examines resultTypes but does not modify it. It returns embedding and
+ * override information in the result array. The low 7 bits are the level, the high
+ * bit is set if the level is an override, and clear if it is an embedding.
+ */
+ private static byte[] processEmbeddings(byte[] resultTypes, byte paragraphEmbeddingLevel) {
+ final int EXPLICIT_LEVEL_LIMIT = 62;
+
+ int textLength = resultTypes.length;
+ byte[] embeddings = new byte[textLength];
+
+ // This stack will store the embedding levels and override status in a single byte
+ // as described above.
+ byte[] embeddingValueStack = new byte[EXPLICIT_LEVEL_LIMIT];
+ int stackCounter = 0;
+
+ // An LRE or LRO at level 60 is invalid, since the new level 62 is invalid. But
+ // an RLE at level 60 is valid, since the new level 61 is valid. The current wording
+ // of the rules requires that the RLE remain valid even if a previous LRE is invalid.
+ // This keeps track of ignored LRE or LRO codes at level 60, so that the matching PDFs
+ // will not try to pop the stack.
+ int overflowAlmostCounter = 0;
+
+ // This keeps track of ignored pushes at level 61 or higher, so that matching PDFs will
+ // not try to pop the stack.
+ int overflowCounter = 0;
+
+ // Rule X1.
+
+ // Keep the level separate from the value (level | override status flag) for ease of access.
+ byte currentEmbeddingLevel = paragraphEmbeddingLevel;
+ byte currentEmbeddingValue = paragraphEmbeddingLevel;
+
+ // Loop through types, handling all remaining rules
+ for (int i = 0; i < textLength; ++i) {
+
+ embeddings[i] = currentEmbeddingValue;
+
+ byte t = resultTypes[i];
+
+ // Rules X2, X3, X4, X5
+ switch (t) {
+ case RLE:
+ case LRE:
+ case RLO:
+ case LRO:
+ // Only need to compute new level if current level is valid
+ if (overflowCounter == 0) {
+ byte newLevel;
+ if (t == RLE || t == RLO) {
+ newLevel = (byte)((currentEmbeddingLevel + 1) | 1); // least greater odd
+ } else { // t == LRE || t == LRO
+ newLevel = (byte)((currentEmbeddingLevel + 2) & ~1); // least greater even
+ }
+
+ // If the new level is valid, push old embedding level and override status
+ // No check for valid stack counter, since the level check suffices.
+ if (newLevel < EXPLICIT_LEVEL_LIMIT) {
+ embeddingValueStack[stackCounter] = currentEmbeddingValue;
+ stackCounter++;
+
+ currentEmbeddingLevel = newLevel;
+ if (t == LRO || t == RLO) { // override
+ currentEmbeddingValue = (byte)(newLevel | 0x80);
+ } else {
+ currentEmbeddingValue = newLevel;
+ }
+
+ // Adjust level of format mark (for expositional purposes only, this gets
+ // removed later).
+ embeddings[i] = currentEmbeddingValue;
+ break;
+ }
+
+ // Otherwise new level is invalid, but a valid level can still be achieved if this
+ // level is 60 and we encounter an RLE or RLO further on. So record that we
+ // 'almost' overflowed.
+ if (currentEmbeddingLevel == 60) {
+ overflowAlmostCounter++;
+ break;
+ }
+ }
+
+ // Otherwise old or new level is invalid.
+ overflowCounter++;
+ break;
+
+ case PDF:
+ // The only case where this did not actually overflow but may have almost overflowed
+ // is when there was an RLE or RLO on level 60, which would result in level 61. So we
+ // only test the almost overflow condition in that case.
+ //
+ // Also note that there may be a PDF without any pushes at all.
+
+ if (overflowCounter > 0) {
+ --overflowCounter;
+ } else if (overflowAlmostCounter > 0 && currentEmbeddingLevel != 61) {
+ --overflowAlmostCounter;
+ } else if (stackCounter > 0) {
+ --stackCounter;
+ currentEmbeddingValue = embeddingValueStack[stackCounter];
+ currentEmbeddingLevel = (byte)(currentEmbeddingValue & 0x7f);
+ }
+ break;
+
+ case B:
+ // Rule X8.
+
+ // These values are reset for clarity, in this implementation B can only
+ // occur as the last code in the array.
+ stackCounter = 0;
+ overflowCounter = 0;
+ overflowAlmostCounter = 0;
+ currentEmbeddingLevel = paragraphEmbeddingLevel;
+ currentEmbeddingValue = paragraphEmbeddingLevel;
+
+ embeddings[i] = paragraphEmbeddingLevel;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return embeddings;
+ }
+
+
+ /**
+ * 3) resolving weak types
+ * Rules W1-W7.
+ *
+ * Note that some weak types (EN, AN) remain after this processing is complete.
+ */
+ private void resolveWeakTypes(int start, int limit, byte level, byte sor, byte eor) {
+
+ // Rule W1.
+ // Changes all NSMs.
+ byte preceedingCharacterType = sor;
+ for (int i = start; i < limit; ++i) {
+ byte t = resultTypes[i];
+ if (t == NSM) {
+ resultTypes[i] = preceedingCharacterType;
+ } else {
+ preceedingCharacterType = t;
+ }
+ }
+
+ // Rule W2.
+ // EN does not change at the start of the run, because sor != AL.
+ for (int i = start; i < limit; ++i) {
+ if (resultTypes[i] == EN) {
+ for (int j = i - 1; j >= start; --j) {
+ byte t = resultTypes[j];
+ if (t == L || t == R || t == AL) {
+ if (t == AL) {
+ resultTypes[i] = AN;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Rule W3.
+ for (int i = start; i < limit; ++i) {
+ if (resultTypes[i] == AL) {
+ resultTypes[i] = R;
+ }
+ }
+
+ // Rule W4.
+ // Since there must be values on both sides for this rule to have an
+ // effect, the scan skips the first and last value.
+ //
+ // Although the scan proceeds left to right, and changes the type values
+ // in a way that would appear to affect the computations later in the scan,
+ // there is actually no problem. A change in the current value can only
+ // affect the value to its immediate right, and only affect it if it is
+ // ES or CS. But the current value can only change if the value to its
+ // right is not ES or CS. Thus either the current value will not change,
+ // or its change will have no effect on the remainder of the analysis.
+
+ for (int i = start + 1; i < limit - 1; ++i) {
+ if (resultTypes[i] == ES || resultTypes[i] == CS) {
+ byte prevSepType = resultTypes[i-1];
+ byte succSepType = resultTypes[i+1];
+ if (prevSepType == EN && succSepType == EN) {
+ resultTypes[i] = EN;
+ } else if (resultTypes[i] == CS && prevSepType == AN && succSepType == AN) {
+ resultTypes[i] = AN;
+ }
+ }
+ }
+
+ // Rule W5.
+ for (int i = start; i < limit; ++i) {
+ if (resultTypes[i] == ET) {
+ // locate end of sequence
+ int runstart = i;
+ int runlimit = findRunLimit(runstart, limit, new byte[] { ET });
+
+ // check values at ends of sequence
+ byte t = runstart == start ? sor : resultTypes[runstart - 1];
+
+ if (t != EN) {
+ t = runlimit == limit ? eor : resultTypes[runlimit];
+ }
+
+ if (t == EN) {
+ setTypes(runstart, runlimit, EN);
+ }
+
+ // continue at end of sequence
+ i = runlimit;
+ }
+ }
+
+ // Rule W6.
+ for (int i = start; i < limit; ++i) {
+ byte t = resultTypes[i];
+ if (t == ES || t == ET || t == CS) {
+ resultTypes[i] = ON;
+ }
+ }
+
+ // Rule W7.
+ for (int i = start; i < limit; ++i) {
+ if (resultTypes[i] == EN) {
+ // set default if we reach start of run
+ byte prevStrongType = sor;
+ for (int j = i - 1; j >= start; --j) {
+ byte t = resultTypes[j];
+ if (t == L || t == R) { // AL's have been removed
+ prevStrongType = t;
+ break;
+ }
+ }
+ if (prevStrongType == L) {
+ resultTypes[i] = L;
+ }
+ }
+ }
+ }
+
+ /**
+ * 6) resolving neutral types
+ * Rules N1-N2.
+ */
+ private void resolveNeutralTypes(int start, int limit, byte level, byte sor, byte eor) {
+
+ for (int i = start; i < limit; ++i) {
+ byte t = resultTypes[i];
+ if (t == WS || t == ON || t == B || t == S) {
+ // find bounds of run of neutrals
+ int runstart = i;
+ int runlimit = findRunLimit(runstart, limit, new byte[] {B, S, WS, ON});
+
+ // determine effective types at ends of run
+ byte leadingType;
+ byte trailingType;
+
+ if (runstart == start) {
+ leadingType = sor;
+ } else {
+ leadingType = resultTypes[runstart - 1];
+ if (leadingType == L || leadingType == R) {
+ // found the strong type
+ } else if (leadingType == AN) {
+ leadingType = R;
+ } else if (leadingType == EN) {
+ // Since EN's with previous strong L types have been changed
+ // to L in W7, the leadingType must be R.
+ leadingType = R;
+ }
+ }
+
+ if (runlimit == limit) {
+ trailingType = eor;
+ } else {
+ trailingType = resultTypes[runlimit];
+ if (trailingType == L || trailingType == R) {
+ // found the strong type
+ } else if (trailingType == AN) {
+ trailingType = R;
+ } else if (trailingType == EN) {
+ trailingType = R;
+ }
+ }
+
+ byte resolvedType;
+ if (leadingType == trailingType) {
+ // Rule N1.
+ resolvedType = leadingType;
+ } else {
+ // Rule N2.
+ // Notice the embedding level of the run is used, not
+ // the paragraph embedding level.
+ resolvedType = typeForLevel(level);
+ }
+
+ setTypes(runstart, runlimit, resolvedType);
+
+ // skip over run of (former) neutrals
+ i = runlimit;
+ }
+ }
+ }
+
+ /**
+ * 7) resolving implicit embedding levels
+ * Rules I1, I2.
+ */
+ private void resolveImplicitLevels(int start, int limit, byte level, byte sor, byte eor) {
+ if ((level & 1) == 0) { // even level
+ for (int i = start; i < limit; ++i) {
+ byte t = resultTypes[i];
+ // Rule I1.
+ if (t == L ) {
+ // no change
+ } else if (t == R) {
+ resultLevels[i] += 1;
+ } else { // t == AN || t == EN
+ resultLevels[i] += 2;
+ }
+ }
+ } else { // odd level
+ for (int i = start; i < limit; ++i) {
+ byte t = resultTypes[i];
+ // Rule I2.
+ if (t == R) {
+ // no change
+ } else { // t == L || t == AN || t == EN
+ resultLevels[i] += 1;
+ }
+ }
+ }
+ }
+
+ //
+ // Output
+ //
+
+ public byte[] getLevels() {
+ return getLevels(new int[]{textLength});
+ }
+
+ /**
+ * Return levels array breaking lines at offsets in linebreaks.
+ * The returned levels array contains the resolved level for each
+ * bidi code passed to the constructor.
+ *
+ * The linebreaks array must include at least one value.
+ * The values must be in strictly increasing order (no duplicates)
+ * between 1 and the length of the text, inclusive. The last value
+ * must be the length of the text.
+ *
+ * @param linebreaks the offsets at which to break the paragraph
+ * @return the resolved levels of the text
+ */
+ public byte[] getLevels(int[] linebreaks) {
+
+ // Note that since the previous processing has removed all
+ // P, S, and WS values from resultTypes, the values referred to
+ // in these rules are the initial types, before any processing
+ // has been applied (including processing of overrides).
+ //
+ // This example implementation has reinserted explicit format codes
+ // and BN, in order that the levels array correspond to the
+ // initial text. Their final placement is not normative.
+ // These codes are treated like WS in this implementation,
+ // so they don't interrupt sequences of WS.
+
+ validateLineBreaks(linebreaks, textLength);
+
+ byte[] result = (byte[])resultLevels.clone(); // will be returned to caller
+
+ // don't worry about linebreaks since if there is a break within
+ // a series of WS values preceeding S, the linebreak itself
+ // causes the reset.
+ for (int i = 0; i < result.length; ++i) {
+ byte t = initialTypes[i];
+ if (t == B || t == S) {
+ // Rule L1, clauses one and two.
+ result[i] = paragraphEmbeddingLevel;
+
+ // Rule L1, clause three.
+ for (int j = i - 1; j >= 0; --j) {
+ if (isWhitespace(initialTypes[j])) { // including format codes
+ result[j] = paragraphEmbeddingLevel;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ // Rule L1, clause four.
+ int start = 0;
+ for (int i = 0; i < linebreaks.length; ++i) {
+ int limit = linebreaks[i];
+ for (int j = limit - 1; j >= start; --j) {
+ if (isWhitespace(initialTypes[j])) { // including format codes
+ result[j] = paragraphEmbeddingLevel;
+ } else {
+ break;
+ }
+ }
+
+ start = limit;
+ }
+
+ return result;
+ }
+
+ /**
+ * Return reordering array breaking lines at offsets in linebreaks.
+ *
+ * The reordering array maps from a visual index to a logical index.
+ * Lines are concatenated from left to right. So for example, the
+ * fifth character from the left on the third line is
+ *
+ * The linebreaks array must include at least one value.
+ * The values must be in strictly increasing order (no duplicates)
+ * between 1 and the length of the text, inclusive. The last value
+ * must be the length of the text.
+ *
+ * @param linebreaks the offsets at which to break the paragraph.
+ */
+ public int[] getReordering(int[] linebreaks) {
+ validateLineBreaks(linebreaks, textLength);
+
+ byte[] levels = getLevels(linebreaks);
+
+ return computeMultilineReordering(levels, linebreaks);
+ }
+
+ /**
+ * Return multiline reordering array for a given level array.
+ * Reordering does not occur across a line break.
+ */
+ private static int[] computeMultilineReordering(byte[] levels, int[] linebreaks) {
+ int[] result = new int[levels.length];
+
+ int start = 0;
+ for (int i = 0; i < linebreaks.length; ++i) {
+ int limit = linebreaks[i];
+
+ byte[] templevels = new byte[limit - start];
+ System.arraycopy(levels, start, templevels, 0, templevels.length);
+
+ int[] temporder = computeReordering(templevels);
+ for (int j = 0; j < temporder.length; ++j) {
+ result[start + j] = temporder[j] + start;
+ }
+
+ start = limit;
+ }
+
+ return result;
+ }
+
+ /**
+ * Return reordering array for a given level array. This reorders a single line.
+ * The reordering is a visual to logical map. For example,
+ * the leftmost char is string.charAt(order[0]).
+ * Rule L2.
+ */
+ private static int[] computeReordering(byte[] levels) {
+ int lineLength = levels.length;
+
+ int[] result = new int[lineLength];
+
+ // initialize order
+ for (int i = 0; i < lineLength; ++i) {
+ result[i] = i;
+ }
+
+ // locate highest level found on line.
+ // Note the rules say text, but no reordering across line bounds is performed,
+ // so this is sufficient.
+ byte highestLevel = 0;
+ byte lowestOddLevel = 63;
+ for (int i = 0; i < lineLength; ++i) {
+ byte level = levels[i];
+ if (level > highestLevel) {
+ highestLevel = level;
+ }
+ if (((level & 1) != 0) && level < lowestOddLevel) {
+ lowestOddLevel = level;
+ }
+ }
+
+ for (int level = highestLevel; level >= lowestOddLevel; --level) {
+ for (int i = 0; i < lineLength; ++i) {
+ if (levels[i] >= level) {
+ // find range of text at or above this level
+ int start = i;
+ int limit = i + 1;
+ while (limit < lineLength && levels[limit] >= level) {
+ ++limit;
+ }
+
+ // reverse run
+ for (int j = start, k = limit - 1; j < k; ++j, --k) {
+ int temp = result[j];
+ result[j] = result[k];
+ result[k] = temp;
+ }
+
+ // skip to end of level run
+ i = limit;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Return the base level of the paragraph.
+ */
+ public byte getBaseLevel() {
+ return paragraphEmbeddingLevel;
+ }
+
+ // --- internal utilities -------------------------------------------------
+
+ /**
+ * Return true if the type is considered a whitespace type for the line break rules.
+ */
+ private static boolean isWhitespace(byte biditype) {
+ switch (biditype) {
+ case LRE:
+ case RLE:
+ case LRO:
+ case RLO:
+ case PDF:
+ case BN:
+ case WS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Return the strong type (L or R) corresponding to the level.
+ */
+ private static byte typeForLevel(int level) {
+ return ((level & 0x1) == 0) ? L : R;
+ }
+
+ /**
+ * Return the limit of the run starting at index that includes only resultTypes in validSet.
+ * This checks the value at index, and will return index if that value is not in validSet.
+ */
+ private int findRunLimit(int index, int limit, byte[] validSet) {
+ --index;
+ loop:
+ while (++index < limit) {
+ byte t = resultTypes[index];
+ for (int i = 0; i < validSet.length; ++i) {
+ if (t == validSet[i]) {
+ continue loop;
+ }
+ }
+ // didn't find a match in validSet
+ return index;
+ }
+ return limit;
+ }
+
+ /**
+ * Return the start of the run including index that includes only resultTypes in validSet.
+ * This assumes the value at index is valid, and does not check it.
+ */
+ private int findRunStart(int index, byte[] validSet) {
+ loop:
+ while (--index >= 0) {
+ byte t = resultTypes[index];
+ for (int i = 0; i < validSet.length; ++i) {
+ if (t == validSet[i]) {
+ continue loop;
+ }
+ }
+ return index + 1;
+ }
+ return 0;
+ }
+
+ /**
+ * Set resultTypes from start up to (but not including) limit to newType.
+ */
+ private void setTypes(int start, int limit, byte newType) {
+ for (int i = start; i < limit; ++i) {
+ resultTypes[i] = newType;
+ }
+ }
+
+ /**
+ * Set resultLevels from start up to (but not including) limit to newLevel.
+ */
+ private void setLevels(int start, int limit, byte newLevel) {
+ for (int i = start; i < limit; ++i) {
+ resultLevels[i] = newLevel;
+ }
+ }
+
+ // --- input validation ---------------------------------------------------
+
+ /**
+ * Throw exception if type array is invalid.
+ */
+ private static void validateTypes(byte[] types) {
+ if (types == null) {
+ throw new IllegalArgumentException("types is null");
+ }
+ for (int i = 0; i < types.length; ++i) {
+ if (types[i] < TYPE_MIN || types[i] > TYPE_MAX) {
+ throw new IllegalArgumentException("illegal type value at " + i + ": " + types[i]);
+ }
+ }
+ for (int i = 0; i < types.length - 1; ++i) {
+ if (types[i] == B) {
+ throw new IllegalArgumentException("B type before end of paragraph at index: " + i);
+ }
+ }
+ }
+
+ /**
+ * Throw exception if paragraph embedding level is invalid. Special allowance for -1 so that
+ * default processing can still be performed when using this API.
+ */
+ private static void validateParagraphEmbeddingLevel(byte paragraphEmbeddingLevel) {
+ if (paragraphEmbeddingLevel != -1 &&
+ paragraphEmbeddingLevel != 0 &&
+ paragraphEmbeddingLevel != 1) {
+ throw new IllegalArgumentException("illegal paragraph embedding level: " + paragraphEmbeddingLevel);
+ }
+ }
+
+ /**
+ * Throw exception if line breaks array is invalid.
+ */
+ private static void validateLineBreaks(int[] linebreaks, int textLength) {
+ int prev = 0;
+ for (int i = 0; i < linebreaks.length; ++i) {
+ int next = linebreaks[i];
+ if (next <= prev) {
+ throw new IllegalArgumentException("bad linebreak: " + next + " at index: " + i);
+ }
+ prev = next;
+ }
+ if (prev != textLength) {
+ throw new IllegalArgumentException("last linebreak must be at " + textLength);
+ }
+ }
+
+ private static final byte rtypes[] = new byte[0x10000];
+
+ private static char baseTypes[] = {
+ 0, 8, (char)BN, 9, 9, (char)S, 10, 10, (char)B, 11, 11, (char)S, 12, 12, (char)WS, 13, 13, (char)B,
+ 14, 27, (char)BN, 28, 30, (char)B, 31, 31, (char)S, 32, 32, (char)WS, 33, 34, (char)ON, 35, 37, (char)ET,
+ 38, 42, (char)ON, 43, 43, (char)ET, 44, 44, (char)CS, 45, 45, (char)ET, 46, 46, (char)CS, 47, 47, (char)ES,
+ 48, 57, (char)EN, 58, 58, (char)CS, 59, 64, (char)ON, 65, 90, (char)L, 91, 96, (char)ON, 97, 122, (char)L,
+ 123, 126, (char)ON, 127, 132, (char)BN, 133, 133, (char)B, 134, 159, (char)BN, 160, 160, (char)CS,
+ 161, 161, (char)ON, 162, 165, (char)ET, 166, 169, (char)ON, 170, 170, (char)L, 171, 175, (char)ON,
+ 176, 177, (char)ET, 178, 179, (char)EN, 180, 180, (char)ON, 181, 181, (char)L, 182, 184, (char)ON,
+ 185, 185, (char)EN, 186, 186, (char)L, 187, 191, (char)ON, 192, 214, (char)L, 215, 215, (char)ON,
+ 216, 246, (char)L, 247, 247, (char)ON, 248, 696, (char)L, 697, 698, (char)ON, 699, 705, (char)L,
+ 706, 719, (char)ON, 720, 721, (char)L, 722, 735, (char)ON, 736, 740, (char)L, 741, 749, (char)ON,
+ 750, 750, (char)L, 751, 767, (char)ON, 768, 855, (char)NSM, 856, 860, (char)L, 861, 879, (char)NSM,
+ 880, 883, (char)L, 884, 885, (char)ON, 886, 893, (char)L, 894, 894, (char)ON, 895, 899, (char)L,
+ 900, 901, (char)ON, 902, 902, (char)L, 903, 903, (char)ON, 904, 1013, (char)L, 1014, 1014, (char)ON,
+ 1015, 1154, (char)L, 1155, 1158, (char)NSM, 1159, 1159, (char)L, 1160, 1161, (char)NSM,
+ 1162, 1417, (char)L, 1418, 1418, (char)ON, 1419, 1424, (char)L, 1425, 1441, (char)NSM,
+ 1442, 1442, (char)L, 1443, 1465, (char)NSM, 1466, 1466, (char)L, 1467, 1469, (char)NSM,
+ 1470, 1470, (char)R, 1471, 1471, (char)NSM, 1472, 1472, (char)R, 1473, 1474, (char)NSM,
+ 1475, 1475, (char)R, 1476, 1476, (char)NSM, 1477, 1487, (char)L, 1488, 1514, (char)R,
+ 1515, 1519, (char)L, 1520, 1524, (char)R, 1525, 1535, (char)L, 1536, 1539, (char)AL,
+ 1540, 1547, (char)L, 1548, 1548, (char)CS, 1549, 1549, (char)AL, 1550, 1551, (char)ON,
+ 1552, 1557, (char)NSM, 1558, 1562, (char)L, 1563, 1563, (char)AL, 1564, 1566, (char)L,
+ 1567, 1567, (char)AL, 1568, 1568, (char)L, 1569, 1594, (char)AL, 1595, 1599, (char)L,
+ 1600, 1610, (char)AL, 1611, 1624, (char)NSM, 1625, 1631, (char)L, 1632, 1641, (char)AN,
+ 1642, 1642, (char)ET, 1643, 1644, (char)AN, 1645, 1647, (char)AL, 1648, 1648, (char)NSM,
+ 1649, 1749, (char)AL, 1750, 1756, (char)NSM, 1757, 1757, (char)AL, 1758, 1764, (char)NSM,
+ 1765, 1766, (char)AL, 1767, 1768, (char)NSM, 1769, 1769, (char)ON, 1770, 1773, (char)NSM,
+ 1774, 1775, (char)AL, 1776, 1785, (char)EN, 1786, 1805, (char)AL, 1806, 1806, (char)L,
+ 1807, 1807, (char)BN, 1808, 1808, (char)AL, 1809, 1809, (char)NSM, 1810, 1839, (char)AL,
+ 1840, 1866, (char)NSM, 1867, 1868, (char)L, 1869, 1871, (char)AL, 1872, 1919, (char)L,
+ 1920, 1957, (char)AL, 1958, 1968, (char)NSM, 1969, 1969, (char)AL, 1970, 2304, (char)L,
+ 2305, 2306, (char)NSM, 2307, 2363, (char)L, 2364, 2364, (char)NSM, 2365, 2368, (char)L,
+ 2369, 2376, (char)NSM, 2377, 2380, (char)L, 2381, 2381, (char)NSM, 2382, 2384, (char)L,
+ 2385, 2388, (char)NSM, 2389, 2401, (char)L, 2402, 2403, (char)NSM, 2404, 2432, (char)L,
+ 2433, 2433, (char)NSM, 2434, 2491, (char)L, 2492, 2492, (char)NSM, 2493, 2496, (char)L,
+ 2497, 2500, (char)NSM, 2501, 2508, (char)L, 2509, 2509, (char)NSM, 2510, 2529, (char)L,
+ 2530, 2531, (char)NSM, 2532, 2545, (char)L, 2546, 2547, (char)ET, 2548, 2560, (char)L,
+ 2561, 2562, (char)NSM, 2563, 2619, (char)L, 2620, 2620, (char)NSM, 2621, 2624, (char)L,
+ 2625, 2626, (char)NSM, 2627, 2630, (char)L, 2631, 2632, (char)NSM, 2633, 2634, (char)L,
+ 2635, 2637, (char)NSM, 2638, 2671, (char)L, 2672, 2673, (char)NSM, 2674, 2688, (char)L,
+ 2689, 2690, (char)NSM, 2691, 2747, (char)L, 2748, 2748, (char)NSM, 2749, 2752, (char)L,
+ 2753, 2757, (char)NSM, 2758, 2758, (char)L, 2759, 2760, (char)NSM, 2761, 2764, (char)L,
+ 2765, 2765, (char)NSM, 2766, 2785, (char)L, 2786, 2787, (char)NSM, 2788, 2800, (char)L,
+ 2801, 2801, (char)ET, 2802, 2816, (char)L, 2817, 2817, (char)NSM, 2818, 2875, (char)L,
+ 2876, 2876, (char)NSM, 2877, 2878, (char)L, 2879, 2879, (char)NSM, 2880, 2880, (char)L,
+ 2881, 2883, (char)NSM, 2884, 2892, (char)L, 2893, 2893, (char)NSM, 2894, 2901, (char)L,
+ 2902, 2902, (char)NSM, 2903, 2945, (char)L, 2946, 2946, (char)NSM, 2947, 3007, (char)L,
+ 3008, 3008, (char)NSM, 3009, 3020, (char)L, 3021, 3021, (char)NSM, 3022, 3058, (char)L,
+ 3059, 3064, (char)ON, 3065, 3065, (char)ET, 3066, 3066, (char)ON, 3067, 3133, (char)L,
+ 3134, 3136, (char)NSM, 3137, 3141, (char)L, 3142, 3144, (char)NSM, 3145, 3145, (char)L,
+ 3146, 3149, (char)NSM, 3150, 3156, (char)L, 3157, 3158, (char)NSM, 3159, 3259, (char)L,
+ 3260, 3260, (char)NSM, 3261, 3275, (char)L, 3276, 3277, (char)NSM, 3278, 3392, (char)L,
+ 3393, 3395, (char)NSM, 3396, 3404, (char)L, 3405, 3405, (char)NSM, 3406, 3529, (char)L,
+ 3530, 3530, (char)NSM, 3531, 3537, (char)L, 3538, 3540, (char)NSM, 3541, 3541, (char)L,
+ 3542, 3542, (char)NSM, 3543, 3632, (char)L, 3633, 3633, (char)NSM, 3634, 3635, (char)L,
+ 3636, 3642, (char)NSM, 3643, 3646, (char)L, 3647, 3647, (char)ET, 3648, 3654, (char)L,
+ 3655, 3662, (char)NSM, 3663, 3760, (char)L, 3761, 3761, (char)NSM, 3762, 3763, (char)L,
+ 3764, 3769, (char)NSM, 3770, 3770, (char)L, 3771, 3772, (char)NSM, 3773, 3783, (char)L,
+ 3784, 3789, (char)NSM, 3790, 3863, (char)L, 3864, 3865, (char)NSM, 3866, 3892, (char)L,
+ 3893, 3893, (char)NSM, 3894, 3894, (char)L, 3895, 3895, (char)NSM, 3896, 3896, (char)L,
+ 3897, 3897, (char)NSM, 3898, 3901, (char)ON, 3902, 3952, (char)L, 3953, 3966, (char)NSM,
+ 3967, 3967, (char)L, 3968, 3972, (char)NSM, 3973, 3973, (char)L, 3974, 3975, (char)NSM,
+ 3976, 3983, (char)L, 3984, 3991, (char)NSM, 3992, 3992, (char)L, 3993, 4028, (char)NSM,
+ 4029, 4037, (char)L, 4038, 4038, (char)NSM, 4039, 4140, (char)L, 4141, 4144, (char)NSM,
+ 4145, 4145, (char)L, 4146, 4146, (char)NSM, 4147, 4149, (char)L, 4150, 4151, (char)NSM,
+ 4152, 4152, (char)L, 4153, 4153, (char)NSM, 4154, 4183, (char)L, 4184, 4185, (char)NSM,
+ 4186, 5759, (char)L, 5760, 5760, (char)WS, 5761, 5786, (char)L, 5787, 5788, (char)ON,
+ 5789, 5905, (char)L, 5906, 5908, (char)NSM, 5909, 5937, (char)L, 5938, 5940, (char)NSM,
+ 5941, 5969, (char)L, 5970, 5971, (char)NSM, 5972, 6001, (char)L, 6002, 6003, (char)NSM,
+ 6004, 6070, (char)L, 6071, 6077, (char)NSM, 6078, 6085, (char)L, 6086, 6086, (char)NSM,
+ 6087, 6088, (char)L, 6089, 6099, (char)NSM, 6100, 6106, (char)L, 6107, 6107, (char)ET,
+ 6108, 6108, (char)L, 6109, 6109, (char)NSM, 6110, 6127, (char)L, 6128, 6137, (char)ON,
+ 6138, 6143, (char)L, 6144, 6154, (char)ON, 6155, 6157, (char)NSM, 6158, 6158, (char)WS,
+ 6159, 6312, (char)L, 6313, 6313, (char)NSM, 6314, 6431, (char)L, 6432, 6434, (char)NSM,
+ 6435, 6438, (char)L, 6439, 6443, (char)NSM, 6444, 6449, (char)L, 6450, 6450, (char)NSM,
+ 6451, 6456, (char)L, 6457, 6459, (char)NSM, 6460, 6463, (char)L, 6464, 6464, (char)ON,
+ 6465, 6467, (char)L, 6468, 6469, (char)ON, 6470, 6623, (char)L, 6624, 6655, (char)ON,
+ 6656, 8124, (char)L, 8125, 8125, (char)ON, 8126, 8126, (char)L, 8127, 8129, (char)ON,
+ 8130, 8140, (char)L, 8141, 8143, (char)ON, 8144, 8156, (char)L, 8157, 8159, (char)ON,
+ 8160, 8172, (char)L, 8173, 8175, (char)ON, 8176, 8188, (char)L, 8189, 8190, (char)ON,
+ 8191, 8191, (char)L, 8192, 8202, (char)WS, 8203, 8205, (char)BN, 8206, 8206, (char)L,
+ 8207, 8207, (char)R, 8208, 8231, (char)ON, 8232, 8232, (char)WS, 8233, 8233, (char)B,
+ 8234, 8234, (char)LRE, 8235, 8235, (char)RLE, 8236, 8236, (char)PDF, 8237, 8237, (char)LRO,
+ 8238, 8238, (char)RLO, 8239, 8239, (char)WS, 8240, 8244, (char)ET, 8245, 8276, (char)ON,
+ 8277, 8278, (char)L, 8279, 8279, (char)ON, 8280, 8286, (char)L, 8287, 8287, (char)WS,
+ 8288, 8291, (char)BN, 8292, 8297, (char)L, 8298, 8303, (char)BN, 8304, 8304, (char)EN,
+ 8305, 8307, (char)L, 8308, 8313, (char)EN, 8314, 8315, (char)ET, 8316, 8318, (char)ON,
+ 8319, 8319, (char)L, 8320, 8329, (char)EN, 8330, 8331, (char)ET, 8332, 8334, (char)ON,
+ 8335, 8351, (char)L, 8352, 8369, (char)ET, 8370, 8399, (char)L, 8400, 8426, (char)NSM,
+ 8427, 8447, (char)L, 8448, 8449, (char)ON, 8450, 8450, (char)L, 8451, 8454, (char)ON,
+ 8455, 8455, (char)L, 8456, 8457, (char)ON, 8458, 8467, (char)L, 8468, 8468, (char)ON,
+ 8469, 8469, (char)L, 8470, 8472, (char)ON, 8473, 8477, (char)L, 8478, 8483, (char)ON,
+ 8484, 8484, (char)L, 8485, 8485, (char)ON, 8486, 8486, (char)L, 8487, 8487, (char)ON,
+ 8488, 8488, (char)L, 8489, 8489, (char)ON, 8490, 8493, (char)L, 8494, 8494, (char)ET,
+ 8495, 8497, (char)L, 8498, 8498, (char)ON, 8499, 8505, (char)L, 8506, 8507, (char)ON,
+ 8508, 8511, (char)L, 8512, 8516, (char)ON, 8517, 8521, (char)L, 8522, 8523, (char)ON,
+ 8524, 8530, (char)L, 8531, 8543, (char)ON, 8544, 8591, (char)L, 8592, 8721, (char)ON,
+ 8722, 8723, (char)ET, 8724, 9013, (char)ON, 9014, 9082, (char)L, 9083, 9108, (char)ON,
+ 9109, 9109, (char)L, 9110, 9168, (char)ON, 9169, 9215, (char)L, 9216, 9254, (char)ON,
+ 9255, 9279, (char)L, 9280, 9290, (char)ON, 9291, 9311, (char)L, 9312, 9371, (char)EN,
+ 9372, 9449, (char)L, 9450, 9450, (char)EN, 9451, 9751, (char)ON, 9752, 9752, (char)L,
+ 9753, 9853, (char)ON, 9854, 9855, (char)L, 9856, 9873, (char)ON, 9874, 9887, (char)L,
+ 9888, 9889, (char)ON, 9890, 9984, (char)L, 9985, 9988, (char)ON, 9989, 9989, (char)L,
+ 9990, 9993, (char)ON, 9994, 9995, (char)L, 9996, 10023, (char)ON, 10024, 10024, (char)L,
+ 10025, 10059, (char)ON, 10060, 10060, (char)L, 10061, 10061, (char)ON, 10062, 10062, (char)L,
+ 10063, 10066, (char)ON, 10067, 10069, (char)L, 10070, 10070, (char)ON, 10071, 10071, (char)L,
+ 10072, 10078, (char)ON, 10079, 10080, (char)L, 10081, 10132, (char)ON, 10133, 10135, (char)L,
+ 10136, 10159, (char)ON, 10160, 10160, (char)L, 10161, 10174, (char)ON, 10175, 10191, (char)L,
+ 10192, 10219, (char)ON, 10220, 10223, (char)L, 10224, 11021, (char)ON, 11022, 11903, (char)L,
+ 11904, 11929, (char)ON, 11930, 11930, (char)L, 11931, 12019, (char)ON, 12020, 12031, (char)L,
+ 12032, 12245, (char)ON, 12246, 12271, (char)L, 12272, 12283, (char)ON, 12284, 12287, (char)L,
+ 12288, 12288, (char)WS, 12289, 12292, (char)ON, 12293, 12295, (char)L, 12296, 12320, (char)ON,
+ 12321, 12329, (char)L, 12330, 12335, (char)NSM, 12336, 12336, (char)ON, 12337, 12341, (char)L,
+ 12342, 12343, (char)ON, 12344, 12348, (char)L, 12349, 12351, (char)ON, 12352, 12440, (char)L,
+ 12441, 12442, (char)NSM, 12443, 12444, (char)ON, 12445, 12447, (char)L, 12448, 12448, (char)ON,
+ 12449, 12538, (char)L, 12539, 12539, (char)ON, 12540, 12828, (char)L, 12829, 12830, (char)ON,
+ 12831, 12879, (char)L, 12880, 12895, (char)ON, 12896, 12923, (char)L, 12924, 12925, (char)ON,
+ 12926, 12976, (char)L, 12977, 12991, (char)ON, 12992, 13003, (char)L, 13004, 13007, (char)ON,
+ 13008, 13174, (char)L, 13175, 13178, (char)ON, 13179, 13277, (char)L, 13278, 13279, (char)ON,
+ 13280, 13310, (char)L, 13311, 13311, (char)ON, 13312, 19903, (char)L, 19904, 19967, (char)ON,
+ 19968, 42127, (char)L, 42128, 42182, (char)ON, 42183, 64284, (char)L, 64285, 64285, (char)R,
+ 64286, 64286, (char)NSM, 64287, 64296, (char)R, 64297, 64297, (char)ET, 64298, 64310, (char)R,
+ 64311, 64311, (char)L, 64312, 64316, (char)R, 64317, 64317, (char)L, 64318, 64318, (char)R,
+ 64319, 64319, (char)L, 64320, 64321, (char)R, 64322, 64322, (char)L, 64323, 64324, (char)R,
+ 64325, 64325, (char)L, 64326, 64335, (char)R, 64336, 64433, (char)AL, 64434, 64466, (char)L,
+ 64467, 64829, (char)AL, 64830, 64831, (char)ON, 64832, 64847, (char)L, 64848, 64911, (char)AL,
+ 64912, 64913, (char)L, 64914, 64967, (char)AL, 64968, 65007, (char)L, 65008, 65020, (char)AL,
+ 65021, 65021, (char)ON, 65022, 65023, (char)L, 65024, 65039, (char)NSM, 65040, 65055, (char)L,
+ 65056, 65059, (char)NSM, 65060, 65071, (char)L, 65072, 65103, (char)ON, 65104, 65104, (char)CS,
+ 65105, 65105, (char)ON, 65106, 65106, (char)CS, 65107, 65107, (char)L, 65108, 65108, (char)ON,
+ 65109, 65109, (char)CS, 65110, 65118, (char)ON, 65119, 65119, (char)ET, 65120, 65121, (char)ON,
+ 65122, 65123, (char)ET, 65124, 65126, (char)ON, 65127, 65127, (char)L, 65128, 65128, (char)ON,
+ 65129, 65130, (char)ET, 65131, 65131, (char)ON, 65132, 65135, (char)L, 65136, 65140, (char)AL,
+ 65141, 65141, (char)L, 65142, 65276, (char)AL, 65277, 65278, (char)L, 65279, 65279, (char)BN,
+ 65280, 65280, (char)L, 65281, 65282, (char)ON, 65283, 65285, (char)ET, 65286, 65290, (char)ON,
+ 65291, 65291, (char)ET, 65292, 65292, (char)CS, 65293, 65293, (char)ET, 65294, 65294, (char)CS,
+ 65295, 65295, (char)ES, 65296, 65305, (char)EN, 65306, 65306, (char)CS, 65307, 65312, (char)ON,
+ 65313, 65338, (char)L, 65339, 65344, (char)ON, 65345, 65370, (char)L, 65371, 65381, (char)ON,
+ 65382, 65503, (char)L, 65504, 65505, (char)ET, 65506, 65508, (char)ON, 65509, 65510, (char)ET,
+ 65511, 65511, (char)L, 65512, 65518, (char)ON, 65519, 65528, (char)L, 65529, 65531, (char)BN,
+ 65532, 65533, (char)ON, 65534, 65535, (char)L};
+
+ static {
+ for (int k = 0; k < baseTypes.length; ++k) {
+ int start = baseTypes[k];
+ int end = baseTypes[++k];
+ byte b = (byte)baseTypes[++k];
+ while (start <= end)
+ rtypes[start++] = b;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/ByteBuffer.java b/src/main/java/com/lowagie/text/pdf/ByteBuffer.java
new file mode 100644
index 0000000..695d4c5
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/ByteBuffer.java
@@ -0,0 +1,623 @@
+/*
+ * $Id: ByteBuffer.java,v 1.69 2006/01/31 18:05:22 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2000, 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import java.io.UnsupportedEncodingException;
+import java.io.OutputStream;
+import java.io.IOException;
+import com.lowagie.text.DocWriter;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
+
+/**
+ * Acts like a
+ * This can only be used to increment the size.
+ * If the size that is passed through is smaller than the current size, nothing happens.
+ *
+ * @param size the size of the cache
+ */
+
+ public static void setCacheSize(int size) {
+ if (size > 3276700) size = 3276700;
+ if (size <= byteCacheSize) return;
+ byte[][] tmpCache = new byte[size][];
+ for (int i = 0; i < byteCacheSize; i++) {
+ tmpCache[i] = byteCache[i];
+ }
+ byteCache = tmpCache;
+ byteCacheSize = size;
+ }
+
+ /**
+ * You can fill the cache in advance if you want to.
+ *
+ * @param decimals
+ */
+
+ public static void fillCache(int decimals) {
+ int step = 1;
+ switch(decimals) {
+ case 0:
+ step = 100;
+ break;
+ case 1:
+ step = 10;
+ break;
+ }
+ for (int i = 1; i < byteCacheSize; i += step) {
+ if (byteCache[i] != null) continue;
+ byteCache[i] = convertToBytes(i);
+ }
+ }
+
+ /**
+ * Converts an double (multiplied by 100 and cast to an int) into an array of bytes.
+ *
+ * @param i the int
+ * @return a bytearray
+ */
+
+ private static byte[] convertToBytes(int i) {
+ int size = (int)Math.floor(Math.log(i) / Math.log(10));
+ if (i % 100 != 0) {
+ size += 2;
+ }
+ if (i % 10 != 0) {
+ size++;
+ }
+ if (i < 100) {
+ size++;
+ if (i < 10) {
+ size++;
+ }
+ }
+ size--;
+ byte[] cache = new byte[size];
+ size --;
+ if (i < 100) {
+ cache[0] = (byte)'0';
+ }
+ if (i % 10 != 0) {
+ cache[size--] = bytes[i % 10];
+ }
+ if (i % 100 != 0) {
+ cache[size--] = bytes[(i / 10) % 10];
+ cache[size--] = (byte)'.';
+ }
+ size = (int)Math.floor(Math.log(i) / Math.log(10)) - 1;
+ int add = 0;
+ while (add < size) {
+ cache[add] = bytes[(i / (int)Math.pow(10, size - add + 1)) % 10];
+ add++;
+ }
+ return cache;
+ }
+
+ /**
+ * Appends an
+ * Several parameters can be set like the first paragraph line indent and
+ * extra space between paragraphs.
+ *
+ * A call to the method
+ * I the column ended, a new column definition can be loaded with the method
+ *
+ * If the text ended, more text can be loaded with
+ * Full bidirectional reordering is supported. If the run direction is
+ *
+ * It removes all the text placed with
+ * Each array element will contain a
+ * The built in fonts "Symbol" and "ZapfDingbats", if used, have a special encoding
+ * to allow the characters to be referred by Unicode.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class FontSelector {
+
+ protected ArrayList fonts = new ArrayList();
+
+ /**
+ * Adds a
+// Use just like java.util.Hashtable, except that the keys must be ints.
+// This is much faster than creating a new Integer for each access.
+//
+// Fetch the software.
+// @see java.util.Hashtable
+
+public class IntHashtable implements Cloneable {
+ /// The hash table data.
+ private IntHashtableEntry table[];
+
+ /// The total number of entries in the hash table.
+ private int count;
+
+ /// Rehashes the table when count exceeds this threshold.
+ private int threshold;
+
+ /// The load factor for the hashtable.
+ private float loadFactor;
+
+ /// Constructs a new, empty hashtable with the specified initial
+ // capacity and the specified load factor.
+ // @param initialCapacity the initial number of buckets
+ // @param loadFactor a number between 0.0 and 1.0, it defines
+ // the threshold for rehashing the hashtable into
+ // a bigger one.
+ // @exception IllegalArgumentException If the initial capacity
+ // is less than or equal to zero.
+ // @exception IllegalArgumentException If the load factor is
+ // less than or equal to zero.
+ public IntHashtable( int initialCapacity, float loadFactor ) {
+ if ( initialCapacity <= 0 || loadFactor <= 0.0 )
+ throw new IllegalArgumentException();
+ this.loadFactor = loadFactor;
+ table = new IntHashtableEntry[initialCapacity];
+ threshold = (int) ( initialCapacity * loadFactor );
+ }
+
+ /// Constructs a new, empty hashtable with the specified initial
+ // capacity.
+ // @param initialCapacity the initial number of buckets
+ public IntHashtable( int initialCapacity ) {
+ this( initialCapacity, 0.75f );
+ }
+
+ /// Constructs a new, empty hashtable. A default capacity and load factor
+ // is used. Note that the hashtable will automatically grow when it gets
+ // full.
+ public IntHashtable() {
+ this( 101, 0.75f );
+ }
+
+ /// Returns the number of elements contained in the hashtable.
+ public int size() {
+ return count;
+ }
+
+ /// Returns true if the hashtable contains no elements.
+ public boolean isEmpty() {
+ return count == 0;
+ }
+
+ /// Returns true if the specified object is an element of the hashtable.
+ // This operation is more expensive than the containsKey() method.
+ // @param value the value that we are looking for
+ // @exception NullPointerException If the value being searched
+ // for is equal to null.
+ // @see IntHashtable#containsKey
+ public boolean contains( int value ) {
+ IntHashtableEntry tab[] = table;
+ for ( int i = tab.length ; i-- > 0 ; ) {
+ for ( IntHashtableEntry e = tab[i] ; e != null ; e = e.next ) {
+ if ( e.value == value )
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// Returns true if the collection contains an element for the key.
+ // @param key the key that we are looking for
+ // @see IntHashtable#contains
+ public boolean containsKey( int key ) {
+ IntHashtableEntry tab[] = table;
+ int hash = key;
+ int index = ( hash & 0x7FFFFFFF ) % tab.length;
+ for ( IntHashtableEntry e = tab[index] ; e != null ; e = e.next ) {
+ if ( e.hash == hash && e.key == key )
+ return true;
+ }
+ return false;
+ }
+
+ /// Gets the object associated with the specified key in the
+ // hashtable.
+ // @param key the specified key
+ // @returns the element for the key or null if the key
+ // is not defined in the hash table.
+ // @see IntHashtable#put
+ public int get( int key ) {
+ IntHashtableEntry tab[] = table;
+ int hash = key;
+ int index = ( hash & 0x7FFFFFFF ) % tab.length;
+ for ( IntHashtableEntry e = tab[index] ; e != null ; e = e.next ) {
+ if ( e.hash == hash && e.key == key )
+ return e.value;
+ }
+ return 0;
+ }
+
+ /// Rehashes the content of the table into a bigger table.
+ // This method is called automatically when the hashtable's
+ // size exceeds the threshold.
+ protected void rehash() {
+ int oldCapacity = table.length;
+ IntHashtableEntry oldTable[] = table;
+
+ int newCapacity = oldCapacity * 2 + 1;
+ IntHashtableEntry newTable[] = new IntHashtableEntry[newCapacity];
+
+ threshold = (int) ( newCapacity * loadFactor );
+ table = newTable;
+
+ for ( int i = oldCapacity ; i-- > 0 ; ) {
+ for ( IntHashtableEntry old = oldTable[i] ; old != null ; ) {
+ IntHashtableEntry e = old;
+ old = old.next;
+
+ int index = ( e.hash & 0x7FFFFFFF ) % newCapacity;
+ e.next = newTable[index];
+ newTable[index] = e;
+ }
+ }
+ }
+
+ /// Puts the specified element into the hashtable, using the specified
+ // key. The element may be retrieved by doing a get() with the same key.
+ // The key and the element cannot be null.
+ // @param key the specified key in the hashtable
+ // @param value the specified element
+ // @exception NullPointerException If the value of the element
+ // is equal to null.
+ // @see IntHashtable#get
+ // @return the old value of the key, or null if it did not have one.
+ public int put( int key, int value ) {
+ // Makes sure the key is not already in the hashtable.
+ IntHashtableEntry tab[] = table;
+ int hash = key;
+ int index = ( hash & 0x7FFFFFFF ) % tab.length;
+ for ( IntHashtableEntry e = tab[index] ; e != null ; e = e.next ) {
+ if ( e.hash == hash && e.key == key ) {
+ int old = e.value;
+ e.value = value;
+ return old;
+ }
+ }
+
+ if ( count >= threshold ) {
+ // Rehash the table if the threshold is exceeded.
+ rehash();
+ return put( key, value );
+ }
+
+ // Creates the new entry.
+ IntHashtableEntry e = new IntHashtableEntry();
+ e.hash = hash;
+ e.key = key;
+ e.value = value;
+ e.next = tab[index];
+ tab[index] = e;
+ ++count;
+ return 0;
+ }
+
+ /// Removes the element corresponding to the key. Does nothing if the
+ // key is not present.
+ // @param key the key that needs to be removed
+ // @return the value of key, or null if the key was not found.
+ public int remove( int key ) {
+ IntHashtableEntry tab[] = table;
+ int hash = key;
+ int index = ( hash & 0x7FFFFFFF ) % tab.length;
+ for ( IntHashtableEntry e = tab[index], prev = null ; e != null ; prev = e, e = e.next ) {
+ if ( e.hash == hash && e.key == key ) {
+ if ( prev != null )
+ prev.next = e.next;
+ else
+ tab[index] = e.next;
+ --count;
+ return e.value;
+ }
+ }
+ return 0;
+ }
+
+ /// Clears the hash table so that it has no more elements in it.
+ public void clear() {
+ IntHashtableEntry tab[] = table;
+ for ( int index = tab.length; --index >= 0; )
+ tab[index] = null;
+ count = 0;
+ }
+
+ public Object clone() {
+ try {
+ IntHashtable t = (IntHashtable)super.clone();
+ t.table = new IntHashtableEntry[table.length];
+ for (int i = table.length ; i-- > 0 ; ) {
+ t.table[i] = (table[i] != null)
+ ? (IntHashtableEntry)table[i].clone() : null;
+ }
+ return t;
+ } catch (CloneNotSupportedException e) {
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError();
+ }
+ }
+
+ public int[] toOrderedKeys() {
+ int res[] = getKeys();
+ Arrays.sort(res);
+ return res;
+ }
+
+ public int[] getKeys() {
+ int res[] = new int[count];
+ int ptr = 0;
+ int index = table.length;
+ IntHashtableEntry entry = null;
+ while (true) {
+ if (entry == null)
+ while ((index-- > 0) && ((entry = table[index]) == null));
+ if (entry == null)
+ break;
+ IntHashtableEntry e = entry;
+ entry = e.next;
+ res[ptr++] = e.key;
+ }
+ return res;
+ }
+
+ public int getOneKey() {
+ if (count == 0)
+ return 0;
+ int index = table.length;
+ IntHashtableEntry entry = null;
+ while ((index-- > 0) && ((entry = table[index]) == null));
+ if (entry == null)
+ return 0;
+ return entry.key;
+ }
+
+ static class IntHashtableEntry {
+ int hash;
+ int key;
+ int value;
+ IntHashtableEntry next;
+
+ public int getKey() {
+ return key;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ protected Object clone() {
+ IntHashtableEntry entry = new IntHashtableEntry();
+ entry.hash = hash;
+ entry.key = key;
+ entry.value = value;
+ entry.next = (next != null) ? (IntHashtableEntry)next.clone() : null;
+ return entry;
+ }
+ }
+
+ public Iterator getEntryIterator() {
+ return new IntHashtableIterator(table);
+ }
+
+ static class IntHashtableIterator implements Iterator {
+ // boolean keys;
+ int index;
+ IntHashtableEntry table[];
+ IntHashtableEntry entry;
+
+ IntHashtableIterator(IntHashtableEntry table[] /* , boolean keys */) {
+ this.table = table;
+ // this.keys = keys;
+ this.index = table.length;
+ }
+
+ public boolean hasNext() {
+ if (entry != null) {
+ return true;
+ }
+ while (index-- > 0) {
+ if ((entry = table[index]) != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Object next() {
+ if (entry == null) {
+ while ((index-- > 0) && ((entry = table[index]) == null));
+ }
+ if (entry != null) {
+ IntHashtableEntry e = entry;
+ entry = e.next;
+ return e;
+ }
+ throw new NoSuchElementException("IntHashtableIterator");
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove() not supported.");
+ }
+
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/LZWDecoder.java b/src/main/java/com/lowagie/text/pdf/LZWDecoder.java
new file mode 100644
index 0000000..b8bedcf
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/LZWDecoder.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2001 Sun Microsystems, 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.
+ *
+ * -Redistribution in binary form must reproduct the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
+ * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
+ * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
+ * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
+ * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
+ * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that Software is not designed,licensed or intended for use in
+ * the design, construction, operation or maintenance of any nuclear facility.
+ *
+ * Adapted from the JAI codecs TIFFLZWDecoder.
+ */
+package com.lowagie.text.pdf;
+import java.io.OutputStream;
+import java.io.IOException;
+import com.lowagie.text.ExceptionConverter;
+/**
+ * A class for performing LZW decoding.
+ *
+ *
+ */
+public class LZWDecoder {
+
+ byte stringTable[][];
+ byte data[] = null;
+ OutputStream uncompData;
+ int tableIndex, bitsToGet = 9;
+ int bytePointer, bitPointer;
+ int nextData = 0;
+ int nextBits = 0;
+
+ int andTable[] = {
+ 511,
+ 1023,
+ 2047,
+ 4095
+ };
+
+ public LZWDecoder() {
+ }
+
+ /**
+ * Method to decode LZW compressed data.
+ *
+ * @param data The compressed data.
+ * @param uncompData Array to return the uncompressed data in.
+ */
+ public void decode(byte data[], OutputStream uncompData) {
+
+ if(data[0] == (byte)0x00 && data[1] == (byte)0x01) {
+ throw new RuntimeException("LZW flavour not supported.");
+ }
+
+ initializeStringTable();
+
+ this.data = data;
+ this.uncompData = uncompData;
+
+ // Initialize pointers
+ bytePointer = 0;
+ bitPointer = 0;
+
+ nextData = 0;
+ nextBits = 0;
+
+ int code, oldCode = 0;
+ byte string[];
+
+ while ((code = getNextCode()) != 257) {
+
+ if (code == 256) {
+
+ initializeStringTable();
+ code = getNextCode();
+
+ if (code == 257) {
+ break;
+ }
+
+ writeString(stringTable[code]);
+ oldCode = code;
+
+ } else {
+
+ if (code < tableIndex) {
+
+ string = stringTable[code];
+
+ writeString(string);
+ addStringToTable(stringTable[oldCode], string[0]);
+ oldCode = code;
+
+ } else {
+
+ string = stringTable[oldCode];
+ string = composeString(string, string[0]);
+ writeString(string);
+ addStringToTable(string);
+ oldCode = code;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Initialize the string table.
+ */
+ public void initializeStringTable() {
+
+ stringTable = new byte[8192][];
+
+ for (int i=0; i<256; i++) {
+ stringTable[i] = new byte[1];
+ stringTable[i][0] = (byte)i;
+ }
+
+ tableIndex = 258;
+ bitsToGet = 9;
+ }
+
+ /**
+ * Write out the string just uncompressed.
+ */
+ public void writeString(byte string[]) {
+ try {
+ uncompData.write(string);
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Add a new string to the string table.
+ */
+ public void addStringToTable(byte oldString[], byte newString) {
+ int length = oldString.length;
+ byte string[] = new byte[length + 1];
+ System.arraycopy(oldString, 0, string, 0, length);
+ string[length] = newString;
+
+ // Add this new String to the table
+ stringTable[tableIndex++] = string;
+
+ if (tableIndex == 511) {
+ bitsToGet = 10;
+ } else if (tableIndex == 1023) {
+ bitsToGet = 11;
+ } else if (tableIndex == 2047) {
+ bitsToGet = 12;
+ }
+ }
+
+ /**
+ * Add a new string to the string table.
+ */
+ public void addStringToTable(byte string[]) {
+
+ // Add this new String to the table
+ stringTable[tableIndex++] = string;
+
+ if (tableIndex == 511) {
+ bitsToGet = 10;
+ } else if (tableIndex == 1023) {
+ bitsToGet = 11;
+ } else if (tableIndex == 2047) {
+ bitsToGet = 12;
+ }
+ }
+
+ /**
+ * Append
+ * The
+ * The
+ * Subclasses of
+ * The
+ * If
+ * If
+ * An array is a sequence of PDF objects. An array may contain a mixture of object types.
+ * An array is written as a left square bracket ([), followed by a sequence of objects,
+ * followed by a right square bracket (]).
+ * The newly added object will be the first element in the
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 4.2 (page 37).
+ *
+ * @see PdfObject
+ * @see BadPdfFormatException
+ */
+
+public class PdfBoolean extends PdfObject {
+
+ // static membervariables (possible values of a boolean object)
+ public static final PdfBoolean PDFTRUE = new PdfBoolean(true);
+ public static final PdfBoolean PDFFALSE = new PdfBoolean(false);
+/** A possible value of
+ * A
+ * When using variable borders ({@link com.lowagie.text.Rectangle#isUseVariableBorders isUseVariableBorders()} == true),
+ * the borders are drawn completely inside the cell Rectangle
+ * so that adjacent cell borders will not overlap.
+ * Otherwise, the borders are drawn on top of the edges of the
+ * cell Rectangle and will overlap the borders of adjacent
+ * cells.
+ *
+ * @see com.lowagie.text.Rectangle
+ * @see com.lowagie.text.Cell
+ * @see PdfLine
+ * @see PdfTable
+ */
+
+public class PdfCell extends Rectangle {
+
+ // membervariables
+
+ /**
+ * These are the PdfLines in the Cell.
+ */
+ private ArrayList lines;
+
+ /**
+ * These are the PdfLines in the Cell.
+ */
+ private PdfLine line;
+
+ /**
+ * These are the Images in the Cell.
+ */
+ private ArrayList images;
+
+ /**
+ * This is the leading of the lines.
+ */
+ private float leading;
+
+ /**
+ * This is the number of the row the cell is in.
+ */
+ private int rownumber;
+
+ /**
+ * This is the rowspan of the cell.
+ */
+ private int rowspan;
+
+ /**
+ * This is the cellspacing of the cell.
+ */
+ private float cellspacing;
+
+ /**
+ * This is the cellpadding of the cell.
+ */
+ private float cellpadding;
+
+ /**
+ * Indicates if this cell belongs to the header of a
+ * Remark: all the lines that can be drawn are removed from the object!
+ *
+ * @param top the top of the part of the table that can be drawn
+ * @param bottom the bottom of the part of the table that can be drawn
+ * @return an
+ * Remark: all the lines that can be drawn are removed from the object!
+ *
+ * @param top the top of the part of the table that can be drawn
+ * @param bottom the bottom of the part of the table that can be drawn
+ * @return an
+ * Headers may allways be removed, even if they are drawn only partially:
+ * they will be repeated on each following page anyway!
+ *
+ * @return
+ * A
+ * This attributes require the mesurement of characters widths when rendering
+ * such as underline.
+ */
+ protected HashMap attributes = new HashMap();
+
+/**
+ * Non metric attributes.
+ *
+ * This attributes do not require the mesurement of characters widths when rendering
+ * such as Color.
+ */
+ protected HashMap noStroke = new HashMap();
+
+/**
+ * Returns null if the
+ * Returns null if the
+ * for the moment every character less than or equal to SPACE and the character '-' are 'splitCharacters'.
+ *
+ * @param start start position in the array
+ * @param current current position in the array
+ * @param end end position in the array
+ * @param cc the character array that has to be checked
+ * @param ck chunk array
+ * @return
+ * @param string the
+ * Flatness sets the maximum permitted distance in device pixels between the
+ * mathematically correct path and an approximation constructed from straight line segments.
+ * The line cap style specifies the shape to be used at the end of open subpaths
+ * when they are stroked.
+ * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
+ * It is specified by an array and a phase. The array specifies the length
+ * of the alternating dashes and gaps. The phase specifies the distance into the dash
+ * pattern to start the dash.
+ * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
+ * It is specified by an array and a phase. The array specifies the length
+ * of the alternating dashes and gaps. The phase specifies the distance into the dash
+ * pattern to start the dash.
+ * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
+ * It is specified by an array and a phase. The array specifies the length
+ * of the alternating dashes and gaps. The phase specifies the distance into the dash
+ * pattern to start the dash.
+ * The line dash pattern controls the pattern of dashes and gaps used to stroke paths.
+ * It is specified by an array and a phase. The array specifies the length
+ * of the alternating dashes and gaps. The phase specifies the distance into the dash
+ * pattern to start the dash.
+ * The line join style specifies the shape to be used at the corners of paths
+ * that are stroked.
+ * The line width specifies the thickness of the line used to stroke a path and is measured
+ * in user space units.
+ * When two line segments meet at a sharp angle and mitered joins have been specified as the
+ * line join style, it is possible for the miter to extend far beyond the thickness of the line
+ * stroking path. The miter limit imposes a maximum on the ratio of the miter length to the line
+ * witdh. When the limit is exceeded, the join is converted from a miter to a bevel.
+ * Sets the color space to DeviceGray (or the DefaultGray color space),
+ * and sets the gray tint to use for filling paths.
+ * Sets the color space to DeviceGray (or the DefaultGray color space),
+ * and sets the gray tint to use for stroking paths.
+ * Sets the color space to DeviceRGB (or the DefaultRGB color space),
+ * and sets the color to use for filling paths.
+ * Following the PDF manual, each operand must be a number between 0 (minimum intensity) and
+ * 1 (maximum intensity).
+ * Sets the color space to DeviceRGB (or the DefaultRGB color space),
+ * and sets the color to use for stroking paths.
+ * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
+ * 1 (maximum intensity).
+ *
+ * @param red the intensity of red. A value between 0 and 1
+ * @param green the intensity of green. A value between 0 and 1
+ * @param blue the intensity of blue. A value between 0 and 1
+ */
+
+ public void setRGBColorStrokeF(float red, float green, float blue) {
+ HelperRGB(red, green, blue);
+ content.append(" RG").append_i(separator);
+ }
+
+ /**
+ * Changes the current color for stroking paths to black.
+ *
+ */
+
+ public void resetRGBColorStroke() {
+ content.append("0 G").append_i(separator);
+ }
+
+ /**
+ * Helper to validate and write the CMYK color components.
+ *
+ * @param cyan the intensity of cyan. A value between 0 and 1
+ * @param magenta the intensity of magenta. A value between 0 and 1
+ * @param yellow the intensity of yellow. A value between 0 and 1
+ * @param black the intensity of black. A value between 0 and 1
+ */
+ private void HelperCMYK(float cyan, float magenta, float yellow, float black) {
+ if (cyan < 0)
+ cyan = 0.0f;
+ else if (cyan > 1.0f)
+ cyan = 1.0f;
+ if (magenta < 0)
+ magenta = 0.0f;
+ else if (magenta > 1.0f)
+ magenta = 1.0f;
+ if (yellow < 0)
+ yellow = 0.0f;
+ else if (yellow > 1.0f)
+ yellow = 1.0f;
+ if (black < 0)
+ black = 0.0f;
+ else if (black > 1.0f)
+ black = 1.0f;
+ content.append(cyan).append(' ').append(magenta).append(' ').append(yellow).append(' ').append(black);
+ }
+
+ /**
+ * Changes the current color for filling paths (device dependent colors!).
+ *
+ * Sets the color space to DeviceCMYK (or the DefaultCMYK color space),
+ * and sets the color to use for filling paths.
+ * Following the PDF manual, each operand must be a number between 0 (no ink) and
+ * 1 (maximum ink).
+ * Sets the color space to DeviceCMYK (or the DefaultCMYK color space),
+ * and sets the color to use for stroking paths.
+ * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
+ * 1 (maximum intensity).
+ *
+ * @param cyan the intensity of cyan. A value between 0 and 1
+ * @param magenta the intensity of magenta. A value between 0 and 1
+ * @param yellow the intensity of yellow. A value between 0 and 1
+ * @param black the intensity of black. A value between 0 and 1
+ */
+
+ public void setCMYKColorStrokeF(float cyan, float magenta, float yellow, float black) {
+ HelperCMYK(cyan, magenta, yellow, black);
+ content.append(" K").append_i(separator);
+ }
+
+ /**
+ * Changes the current color for stroking paths to black.
+ *
+ */
+
+ public void resetCMYKColorStroke() {
+ content.append("0 0 0 1 K").append_i(separator);
+ }
+
+ /**
+ * Move the current point (x, y), omitting any connecting line segment.
+ *
+ * @param x new x-coordinate
+ * @param y new y-coordinate
+ */
+
+ public void moveTo(float x, float y) {
+ content.append(x).append(' ').append(y).append(" m").append_i(separator);
+ }
+
+ /**
+ * Appends a straight line segment from the current point (x, y). The new current
+ * point is (x, y).
+ *
+ * @param x new x-coordinate
+ * @param y new y-coordinate
+ */
+
+ public void lineTo(float x, float y) {
+ content.append(x).append(' ').append(y).append(" l").append_i(separator);
+ }
+
+ /**
+ * Appends a Bêzier curve to the path, starting from the current point.
+ *
+ * @param x1 x-coordinate of the first control point
+ * @param y1 y-coordinate of the first control point
+ * @param x2 x-coordinate of the second control point
+ * @param y2 y-coordinate of the second control point
+ * @param x3 x-coordinaat of the ending point (= new current point)
+ * @param y3 y-coordinaat of the ending point (= new current point)
+ */
+
+ public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) {
+ content.append(x1).append(' ').append(y1).append(' ').append(x2).append(' ').append(y2).append(' ').append(x3).append(' ').append(y3).append(" c").append_i(separator);
+ }
+
+ /**
+ * Appends a Bêzier curve to the path, starting from the current point.
+ *
+ * @param x2 x-coordinate of the second control point
+ * @param y2 y-coordinate of the second control point
+ * @param x3 x-coordinaat of the ending point (= new current point)
+ * @param y3 y-coordinaat of the ending point (= new current point)
+ */
+
+ public void curveTo(float x2, float y2, float x3, float y3) {
+ content.append(x2).append(' ').append(y2).append(' ').append(x3).append(' ').append(y3).append(" v").append_i(separator);
+ }
+
+ /**
+ * Appends a Bêzier curve to the path, starting from the current point.
+ *
+ * @param x1 x-coordinate of the first control point
+ * @param y1 y-coordinate of the first control point
+ * @param x3 x-coordinaat of the ending point (= new current point)
+ * @param y3 y-coordinaat of the ending point (= new current point)
+ */
+
+ public void curveFromTo(float x1, float y1, float x3, float y3) {
+ content.append(x1).append(' ').append(y1).append(' ').append(x3).append(' ').append(y3).append(" y").append_i(separator);
+ }
+
+ /** Draws a circle. The endpoint will (x+r, y).
+ *
+ * @param x x center of circle
+ * @param y y center of circle
+ * @param r radius of circle
+ */
+ public void circle(float x, float y, float r) {
+ float b = 0.5523f;
+ moveTo(x + r, y);
+ curveTo(x + r, y + r * b, x + r * b, y + r, x, y + r);
+ curveTo(x - r * b, y + r, x - r, y + r * b, x - r, y);
+ curveTo(x - r, y - r * b, x - r * b, y - r, x, y - r);
+ curveTo(x + r * b, y - r, x + r, y - r * b, x + r, y);
+ }
+
+
+
+ /**
+ * Adds a rectangle to the current path.
+ *
+ * @param x x-coordinate of the starting point
+ * @param y y-coordinate of the starting point
+ * @param w width
+ * @param h height
+ */
+
+ public void rectangle(float x, float y, float w, float h) {
+ content.append(x).append(' ').append(y).append(' ').append(w).append(' ').append(h).append(" re").append_i(separator);
+ }
+
+ private boolean compareColors(Color c1, Color c2) {
+ if (c1 == null && c2 == null)
+ return true;
+ if (c1 == null || c2 == null)
+ return false;
+ if (c1 instanceof ExtendedColor)
+ return c1.equals(c2);
+ return c2.equals(c1);
+ }
+
+ /**
+ * Adds a variable width border to the current path.
+ * Only use if {@link com.lowagie.text.Rectangle#isUseVariableBorders() Rectangle.isUseVariableBorders}
+ * = true.
+ * @param rect a
+ * The leading parameter is measured in text space units. It specifies the vertical distance
+ * between the baselines of adjacent lines of text.
+ * This allows to write text in subscript or superscript mode.
+ * Remark: this operation also initializes the current point position.
+ * Remark: this operation also initializes the current point position.
+ * As a side effect, this sets the leading parameter in the text state.
+ * (x1, y1) and (x2, y2) are the corners of the enclosing rectangle.
+ * Angles, measured in degrees, start with 0 to the right (the positive X
+ * axis) and increase counter-clockwise. The arc extends from startAng
+ * to startAng+extent. I.e. startAng=0 and extent=180 yields an openside-down
+ * semi-circle.
+ *
+ * The resulting coordinates are of the form float[]{x1,y1,x2,y2,x3,y3, x4,y4}
+ * such that the curve goes from (x1, y1) to (x4, y4) with (x2, y2) and
+ * (x3, y3) as their respective Bezier control points.
+ *
+ * Note: this code was taken from ReportLab (www.reportlab.com), an excelent
+ * PDF generator for Python.
+ *
+ * @param x1 a corner of the enclosing rectangle
+ * @param y1 a corner of the enclosing rectangle
+ * @param x2 a corner of the enclosing rectangle
+ * @param y2 a corner of the enclosing rectangle
+ * @param startAng starting angle in degrees
+ * @param extent angle extent in degrees
+ * @return a list of float[] with the bezier curves
+ */
+ public static ArrayList bezierArc(float x1, float y1, float x2, float y2, float startAng, float extent) {
+ float tmp;
+ if (x1 > x2) {
+ tmp = x1;
+ x1 = x2;
+ x2 = tmp;
+ }
+ if (y2 > y1) {
+ tmp = y1;
+ y1 = y2;
+ y2 = tmp;
+ }
+
+ float fragAngle;
+ int Nfrag;
+ if (Math.abs(extent) <= 90f) {
+ fragAngle = extent;
+ Nfrag = 1;
+ }
+ else {
+ Nfrag = (int)(Math.ceil(Math.abs(extent)/90f));
+ fragAngle = extent / Nfrag;
+ }
+ float x_cen = (x1+x2)/2f;
+ float y_cen = (y1+y2)/2f;
+ float rx = (x2-x1)/2f;
+ float ry = (y2-y1)/2f;
+ float halfAng = (float)(fragAngle * Math.PI / 360.);
+ float kappa = (float)(Math.abs(4. / 3. * (1. - Math.cos(halfAng)) / Math.sin(halfAng)));
+ ArrayList pointList = new ArrayList();
+ for (int i = 0; i < Nfrag; ++i) {
+ float theta0 = (float)((startAng + i*fragAngle) * Math.PI / 180.);
+ float theta1 = (float)((startAng + (i+1)*fragAngle) * Math.PI / 180.);
+ float cos0 = (float)Math.cos(theta0);
+ float cos1 = (float)Math.cos(theta1);
+ float sin0 = (float)Math.sin(theta0);
+ float sin1 = (float)Math.sin(theta1);
+ if (fragAngle > 0f) {
+ pointList.add(new float[]{x_cen + rx * cos0,
+ y_cen - ry * sin0,
+ x_cen + rx * (cos0 - kappa * sin0),
+ y_cen - ry * (sin0 + kappa * cos0),
+ x_cen + rx * (cos1 + kappa * sin1),
+ y_cen - ry * (sin1 - kappa * cos1),
+ x_cen + rx * cos1,
+ y_cen - ry * sin1});
+ }
+ else {
+ pointList.add(new float[]{x_cen + rx * cos0,
+ y_cen - ry * sin0,
+ x_cen + rx * (cos0 + kappa * sin0),
+ y_cen - ry * (sin0 - kappa * cos0),
+ x_cen + rx * (cos1 - kappa * sin1),
+ y_cen - ry * (sin1 + kappa * cos1),
+ x_cen + rx * cos1,
+ y_cen - ry * sin1});
+ }
+ }
+ return pointList;
+ }
+
+ /**
+ * Draws a partial ellipse inscribed within the rectangle x1,y1,x2,y2,
+ * starting at startAng degrees and covering extent degrees. Angles
+ * start with 0 to the right (+x) and increase counter-clockwise.
+ *
+ * @param x1 a corner of the enclosing rectangle
+ * @param y1 a corner of the enclosing rectangle
+ * @param x2 a corner of the enclosing rectangle
+ * @param y2 a corner of the enclosing rectangle
+ * @param startAng starting angle in degrees
+ * @param extent angle extent in degrees
+ */
+ public void arc(float x1, float y1, float x2, float y2, float startAng, float extent) {
+ ArrayList ar = bezierArc(x1, y1, x2, y2, startAng, extent);
+ if (ar.size() == 0)
+ return;
+ float pt[] = (float [])ar.get(0);
+ moveTo(pt[0], pt[1]);
+ for (int k = 0; k < ar.size(); ++k) {
+ pt = (float [])ar.get(k);
+ curveTo(pt[2], pt[3], pt[4], pt[5], pt[6], pt[7]);
+ }
+ }
+
+ /**
+ * Draws an ellipse inscribed within the rectangle x1,y1,x2,y2.
+ *
+ * @param x1 a corner of the enclosing rectangle
+ * @param y1 a corner of the enclosing rectangle
+ * @param x2 a corner of the enclosing rectangle
+ * @param y2 a corner of the enclosing rectangle
+ */
+ public void ellipse(float x1, float y1, float x2, float y2) {
+ arc(x1, y1, x2, y2, 0f, 360f);
+ }
+
+ /**
+ * Create a new colored tiling pattern.
+ *
+ * @param width the width of the pattern
+ * @param height the height of the pattern
+ * @param xstep the desired horizontal spacing between pattern cells.
+ * May be either positive or negative, but not zero.
+ * @param ystep the desired vertical spacing between pattern cells.
+ * May be either positive or negative, but not zero.
+ * @return the
+ * Creates a new template that is nothing more than a form XObject. This template can be included
+ * in this
+ * Sets the color space to DeviceCMYK (or the DefaultCMYK color space),
+ * and sets the color to use for filling paths.
+ * This method is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 8.5.2.1 (page 331).
+ * Following the PDF manual, each operand must be a number between 0 (no ink) and
+ * 1 (maximum ink). This method however accepts only integers between 0x00 and 0xFF.
+ * Sets the color space to DeviceCMYK (or the DefaultCMYK color space),
+ * and sets the color to use for stroking paths.
+ * This method is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 8.5.2.1 (page 331).
+ * Sets the color space to DeviceRGB (or the DefaultRGB color space),
+ * and sets the color to use for filling paths.
+ * This method is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 8.5.2.1 (page 331).
+ * Following the PDF manual, each operand must be a number between 0 (miniumum intensity) and
+ * 1 (maximum intensity). This method however accepts only integers between 0x00 and 0xFF.
+ * Sets the color space to DeviceRGB (or the DefaultRGB color space),
+ * and sets the color to use for stroking paths.
+ * This method is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 8.5.2.1 (page 331).
+ * Note that nested layers with {@link PdfLayer#addChild(PdfLayer)} only require a single
+ * call to this method and a single call to {@link #endLayer()}; all the nesting control
+ * is built in.
+ * @param layer the layer
+ */
+ public void beginLayer(PdfOCG layer) {
+ if ((layer instanceof PdfLayer) && ((PdfLayer)layer).getTitle() != null)
+ throw new IllegalArgumentException("A title is not a layer");
+ if (layerDepth == null)
+ layerDepth = new ArrayList();
+ if (layer instanceof PdfLayerMembership) {
+ layerDepth.add(new Integer(1));
+ beginLayer2(layer);
+ return;
+ }
+ int n = 0;
+ PdfLayer la = (PdfLayer)layer;
+ while (la != null) {
+ if (la.getTitle() == null) {
+ beginLayer2(la);
+ ++n;
+ }
+ la = la.getParent();
+ }
+ layerDepth.add(new Integer(n));
+ }
+
+ private void beginLayer2(PdfOCG layer) {
+ PdfName name = (PdfName)writer.addSimpleProperty(layer, layer.getRef())[0];
+ PageResources prs = getPageResources();
+ name = prs.addProperty(name, layer.getRef());
+ content.append("/OC ").append(name.getBytes()).append(" BDC").append_i(separator);
+ }
+
+ /**
+ * Ends a layer controled graphic block. It will end the most recent open block.
+ */
+ public void endLayer() {
+ int n = 1;
+ if (layerDepth != null && layerDepth.size() > 0) {
+ n = ((Integer)layerDepth.get(layerDepth.size() - 1)).intValue();
+ layerDepth.remove(layerDepth.size() - 1);
+ }
+ while (n-- > 0)
+ content.append("EMC").append_i(separator);
+ }
+
+ /** Concatenates a transformation to the current transformation
+ * matrix.
+ * @param af the transformation
+ */
+ public void transform(AffineTransform af) {
+ double arr[] = new double[6];
+ af.getMatrix(arr);
+ content.append(arr[0]).append(' ').append(arr[1]).append(' ').append(arr[2]).append(' ');
+ content.append(arr[3]).append(' ').append(arr[4]).append(' ').append(arr[5]).append(" cm").append_i(separator);
+ }
+
+ void addAnnotation(PdfAnnotation annot) {
+ writer.addAnnotation(annot);
+ }
+
+ /**
+ * Sets the default colorspace.
+ * @param name the name of the colorspace. It can be
+ * The pages-tree is built and written to the outputstream.
+ * A Catalog is constructed, as well as an Info-object,
+ * the referencetable is composed and everything is written
+ * to the outputstream embedded in a Trailer.
+ */
+
+ public synchronized void close() {
+ if (open) {
+ PdfReaderInstance ri = currentPdfReaderInstance;
+ pdf.close();
+ super.close();
+ if (ri != null) {
+ try {
+ ri.getReader().close();
+ ri.getReaderFile().close();
+ }
+ catch (IOException ioe) {
+ // empty on purpose
+ }
+ }
+ }
+ }
+ PdfIndirectReference add(PdfImage pdfImage, PdfIndirectReference fixedRef) throws PdfException { return null; }
+ public PdfIndirectReference add(PdfOutline outline) { return null; }
+ public void addAnnotation(PdfAnnotation annot) { }
+ PdfIndirectReference add(PdfPage page, PdfContents contents) throws PdfException { return null; }
+
+ public void freeReader(PdfReader reader) throws IOException {
+ indirectMap.remove(reader);
+ if (currentPdfReaderInstance != null) {
+ if (currentPdfReaderInstance.getReader() == reader) {
+ try {
+ currentPdfReaderInstance.getReader().close();
+ currentPdfReaderInstance.getReaderFile().close();
+ }
+ catch (IOException ioe) {
+ // empty on purpose
+ }
+ currentPdfReaderInstance = null;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfCopyFields.java b/src/main/java/com/lowagie/text/pdf/PdfCopyFields.java
new file mode 100644
index 0000000..ec08329
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfCopyFields.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.DocWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+/**
+ * Concatenates PDF documents including form fields. The rules for the form field
+ * concatenation are the same as in Acrobat. All the documents are kept in memory unlike
+ * PdfCopy.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfCopyFields {
+
+ private PdfCopyFieldsImp fc;
+
+ /**
+ * Creates a new instance.
+ * @param os the output stream
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public PdfCopyFields(OutputStream os) throws DocumentException, IOException {
+ fc = new PdfCopyFieldsImp(os);
+ }
+
+ /**
+ * Creates a new instance.
+ * @param os the output stream
+ * @param pdfVersion the pdf version the output will have
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public PdfCopyFields(OutputStream os, char pdfVersion) throws DocumentException, IOException {
+ fc = new PdfCopyFieldsImp(os, pdfVersion);
+ }
+
+ /**
+ * Concatenates a PDF document.
+ * @param reader the PDF document
+ * @throws DocumentException on error
+ */
+ public void addDocument(PdfReader reader) throws DocumentException {
+ fc.addDocument(reader);
+ }
+
+ /**
+ * Concatenates a PDF document selecting the pages to keep. The pages are described as a
+ *
+ * If set before opening the document it will also set the pdf version to 1.5.
+ */
+ public void setFullCompression() {
+ fc.setFullCompression();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfCopyFieldsImp.java b/src/main/java/com/lowagie/text/pdf/PdfCopyFieldsImp.java
new file mode 100644
index 0000000..ac9b3d5
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfCopyFieldsImp.java
@@ -0,0 +1,639 @@
+/*
+ * $Id: PdfCopyFieldsImp.java,v 1.13 2006/02/11 09:44:10 psoares33 Exp $
+ * $Name: $
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Document;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ *
+ * @author psoares
+ */
+class PdfCopyFieldsImp extends PdfWriter {
+
+ private static final PdfName iTextTag = new PdfName("_iTextTag_");
+ private static final Integer zero = new Integer(0);
+ ArrayList readers = new ArrayList();
+ HashMap readers2intrefs = new HashMap();
+ HashMap pages2intrefs = new HashMap();
+ HashMap visited = new HashMap();
+ ArrayList fields = new ArrayList();
+ RandomAccessFileOrArray file;
+ HashMap fieldTree = new HashMap();
+ ArrayList pageRefs = new ArrayList();
+ ArrayList pageDics = new ArrayList();
+ PdfDictionary resources = new PdfDictionary();
+ PdfDictionary form;
+ protected List newBookmarks;
+ boolean closing = false;
+ Document nd;
+ private HashMap tabOrder;
+ private ArrayList calculationOrder = new ArrayList();
+ private ArrayList calculationOrderRefs;
+
+ PdfCopyFieldsImp(OutputStream os) throws DocumentException, IOException {
+ this(os, '\0');
+ }
+
+ PdfCopyFieldsImp(OutputStream os, char pdfVersion) throws DocumentException, IOException {
+ super(new PdfDocument(), os);
+ pdf.addWriter(this);
+ if (pdfVersion != 0)
+ super.setPdfVersion(pdfVersion);
+ nd = new Document();
+ nd.addDocListener(pdf);
+ }
+
+ void addDocument(PdfReader reader, List pagesToKeep) throws DocumentException {
+ if (!readers2intrefs.containsKey(reader) && reader.isTampered())
+ throw new DocumentException("The document was reused.");
+ reader = new PdfReader(reader);
+ reader.selectPages(pagesToKeep);
+ if (reader.getNumberOfPages() == 0)
+ return;
+ reader.setTampered(false);
+ addDocument(reader);
+ }
+
+ void addDocument(PdfReader reader) throws DocumentException {
+ openDoc();
+ if (readers2intrefs.containsKey(reader)) {
+ reader = new PdfReader(reader);
+ }
+ else {
+ if (reader.isTampered())
+ throw new DocumentException("The document was reused.");
+ reader.consolidateNamedDestinations();
+ reader.setTampered(true);
+ }
+ reader.shuffleSubsetNames();
+ readers2intrefs.put(reader, new IntHashtable());
+ readers.add(reader);
+ int len = reader.getNumberOfPages();
+ IntHashtable refs = new IntHashtable();
+ for (int p = 1; p <= len; ++p) {
+ refs.put(reader.getPageOrigRef(p).getNumber(), 1);
+ reader.releasePage(p);
+ }
+ pages2intrefs.put(reader, refs);
+ visited.put(reader, new IntHashtable());
+ fields.add(reader.getAcroFields());
+ updateCalculationOrder(reader);
+ }
+
+ private static String getCOName(PdfReader reader, PRIndirectReference ref) {
+ String name = "";
+ while (ref != null) {
+ PdfObject obj = PdfReader.getPdfObject(ref);
+ if (obj == null || obj.type() != PdfObject.DICTIONARY)
+ break;
+ PdfDictionary dic = (PdfDictionary)obj;
+ PdfString t = (PdfString)PdfReader.getPdfObject(dic.get(PdfName.T));
+ if (t != null) {
+ name = t.toUnicodeString()+ "." + name;
+ }
+ ref = (PRIndirectReference)dic.get(PdfName.PARENT);
+ }
+ if (name.endsWith("."))
+ name = name.substring(0, name.length() - 1);
+ return name;
+ }
+
+ private void updateCalculationOrder(PdfReader reader) {
+ PdfDictionary catalog = reader.getCatalog();
+ PdfDictionary acro = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.ACROFORM));
+ if (acro == null)
+ return;
+ PdfArray co = (PdfArray)PdfReader.getPdfObject(acro.get(PdfName.CO));
+ if (co == null || co.size() == 0)
+ return;
+ AcroFields af = reader.getAcroFields();
+ ArrayList coa = co.getArrayList();
+ for (int k = 0; k < coa.size(); ++k) {
+ PdfObject obj = (PdfObject)coa.get(k);
+ if (obj == null || !obj.isIndirect())
+ continue;
+ String name = getCOName(reader, (PRIndirectReference)obj);
+ if (af.getFieldItem(name) == null)
+ continue;
+ name = "." + name;
+ if (calculationOrder.contains(name))
+ continue;
+ calculationOrder.add(name);
+ }
+ }
+
+ void propagate(PdfObject obj, PdfIndirectReference refo, boolean restricted) throws IOException {
+ if (obj == null)
+ return;
+// if (refo != null)
+// addToBody(obj, refo);
+ if (obj instanceof PdfIndirectReference)
+ return;
+ switch (obj.type()) {
+ case PdfObject.DICTIONARY:
+ case PdfObject.STREAM: {
+ PdfDictionary dic = (PdfDictionary)obj;
+ for (Iterator it = dic.getKeys().iterator(); it.hasNext();) {
+ PdfName key = (PdfName)it.next();
+ if (restricted && (key.equals(PdfName.PARENT) || key.equals(PdfName.KIDS)))
+ continue;
+ PdfObject ob = dic.get(key);
+ if (ob != null && ob.isIndirect()) {
+ PRIndirectReference ind = (PRIndirectReference)ob;
+ if (!setVisited(ind) && !isPage(ind)) {
+ PdfIndirectReference ref = getNewReference(ind);
+ propagate(PdfReader.getPdfObjectRelease(ind), ref, restricted);
+ }
+ }
+ else
+ propagate(ob, null, restricted);
+ }
+ break;
+ }
+ case PdfObject.ARRAY: {
+ ArrayList list = ((PdfArray)obj).getArrayList();
+ //PdfArray arr = new PdfArray();
+ for (Iterator it = list.iterator(); it.hasNext();) {
+ PdfObject ob = (PdfObject)it.next();
+ if (ob != null && ob.isIndirect()) {
+ PRIndirectReference ind = (PRIndirectReference)ob;
+ if (!isVisited(ind) && !isPage(ind)) {
+ PdfIndirectReference ref = getNewReference(ind);
+ propagate(PdfReader.getPdfObjectRelease(ind), ref, restricted);
+ }
+ }
+ else
+ propagate(ob, null, restricted);
+ }
+ break;
+ }
+ case PdfObject.INDIRECT: {
+ throw new RuntimeException("Reference pointing to reference.");
+ }
+ }
+ }
+
+ private void adjustTabOrder(PdfArray annots, PdfIndirectReference ind, PdfNumber nn) {
+ int v = nn.intValue();
+ ArrayList t = (ArrayList)tabOrder.get(annots);
+ if (t == null) {
+ t = new ArrayList();
+ int size = annots.size() - 1;
+ for (int k = 0; k < size; ++k) {
+ t.add(zero);
+ }
+ t.add(new Integer(v));
+ tabOrder.put(annots, t);
+ annots.add(ind);
+ }
+ else {
+ int size = t.size() - 1;
+ for (int k = size; k >= 0; --k) {
+ if (((Integer)t.get(k)).intValue() <= v) {
+ t.add(k + 1, new Integer(v));
+ annots.getArrayList().add(k + 1, ind);
+ size = -2;
+ break;
+ }
+ }
+ if (size != -2) {
+ t.add(0, new Integer(v));
+ annots.getArrayList().add(0, ind);
+ }
+ }
+ }
+
+ protected PdfArray branchForm(HashMap level, PdfIndirectReference parent, String fname) throws IOException {
+ PdfArray arr = new PdfArray();
+ for (Iterator it = level.keySet().iterator(); it.hasNext();) {
+ String name = (String)it.next();
+ Object obj = level.get(name);
+ PdfIndirectReference ind = getPdfIndirectReference();
+ PdfDictionary dic = new PdfDictionary();
+ if (parent != null)
+ dic.put(PdfName.PARENT, parent);
+ dic.put(PdfName.T, new PdfString(name, PdfObject.TEXT_UNICODE));
+ String fname2 = fname + "." + name;
+ int coidx = calculationOrder.indexOf(fname2);
+ if (coidx >= 0)
+ calculationOrderRefs.set(coidx, ind);
+ if (obj instanceof HashMap) {
+ dic.put(PdfName.KIDS, branchForm((HashMap)obj, ind, fname2));
+ arr.add(ind);
+ addToBody(dic, ind);
+ }
+ else {
+ ArrayList list = (ArrayList)obj;
+ dic.mergeDifferent((PdfDictionary)list.get(0));
+ if (list.size() == 3) {
+ dic.mergeDifferent((PdfDictionary)list.get(2));
+ int page = ((Integer)list.get(1)).intValue();
+ PdfDictionary pageDic = (PdfDictionary)pageDics.get(page - 1);
+ PdfArray annots = (PdfArray)PdfReader.getPdfObject(pageDic.get(PdfName.ANNOTS));
+ if (annots == null) {
+ annots = new PdfArray();
+ pageDic.put(PdfName.ANNOTS, annots);
+ }
+ PdfNumber nn = (PdfNumber)dic.get(iTextTag);
+ dic.remove(iTextTag);
+ adjustTabOrder(annots, ind, nn);
+ }
+ else {
+ PdfArray kids = new PdfArray();
+ for (int k = 1; k < list.size(); k += 2) {
+ int page = ((Integer)list.get(k)).intValue();
+ PdfDictionary pageDic = (PdfDictionary)pageDics.get(page - 1);
+ PdfArray annots = (PdfArray)PdfReader.getPdfObject(pageDic.get(PdfName.ANNOTS));
+ if (annots == null) {
+ annots = new PdfArray();
+ pageDic.put(PdfName.ANNOTS, annots);
+ }
+ PdfDictionary widget = new PdfDictionary();
+ widget.merge((PdfDictionary)list.get(k + 1));
+ widget.put(PdfName.PARENT, ind);
+ PdfNumber nn = (PdfNumber)widget.get(iTextTag);
+ widget.remove(iTextTag);
+ PdfIndirectReference wref = addToBody(widget).getIndirectReference();
+ adjustTabOrder(annots, wref, nn);
+ kids.add(wref);
+ propagate(widget, null, false);
+ }
+ dic.put(PdfName.KIDS, kids);
+ }
+ arr.add(ind);
+ addToBody(dic, ind);
+ propagate(dic, null, false);
+ }
+ }
+ return arr;
+ }
+
+ protected void createAcroForms() throws IOException {
+ if (fieldTree.size() == 0)
+ return;
+ form = new PdfDictionary();
+ form.put(PdfName.DR, resources);
+ propagate(resources, null, false);
+ form.put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g "));
+ tabOrder = new HashMap();
+ calculationOrderRefs = new ArrayList(calculationOrder);
+ form.put(PdfName.FIELDS, branchForm(fieldTree, null, ""));
+ PdfArray co = new PdfArray();
+ for (int k = 0; k < calculationOrderRefs.size(); ++k) {
+ Object obj = calculationOrderRefs.get(k);
+ if (obj instanceof PdfIndirectReference)
+ co.add((PdfIndirectReference)obj);
+ }
+ if (co.size() > 0)
+ form.put(PdfName.CO, co);
+ }
+
+ public void close() {
+ if (closing) {
+ super.close();
+ return;
+ }
+ closing = true;
+ try {
+ closeIt();
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ protected void closeIt() throws DocumentException, IOException {
+ for (int k = 0; k < readers.size(); ++k) {
+ ((PdfReader)readers.get(k)).removeFields();
+ }
+ for (int r = 0; r < readers.size(); ++r) {
+ PdfReader reader = (PdfReader)readers.get(r);
+ for (int page = 1; page <= reader.getNumberOfPages(); ++page) {
+ pageRefs.add(getNewReference(reader.getPageOrigRef(page)));
+ pageDics.add(reader.getPageN(page));
+ }
+ }
+ mergeFields();
+ createAcroForms();
+ for (int r = 0; r < readers.size(); ++r) {
+ PdfReader reader = (PdfReader)readers.get(r);
+ for (int page = 1; page <= reader.getNumberOfPages(); ++page) {
+ PdfDictionary dic = reader.getPageN(page);
+ PdfIndirectReference pageRef = getNewReference(reader.getPageOrigRef(page));
+ PdfIndirectReference parent = root.addPageRef(pageRef);
+ dic.put(PdfName.PARENT, parent);
+ propagate(dic, pageRef, false);
+ }
+ }
+ for (Iterator it = readers2intrefs.keySet().iterator(); it.hasNext();) {
+ PdfReader reader = (PdfReader)it.next();
+ try {
+ file = reader.getSafeFile();
+ file.reOpen();
+ IntHashtable t = (IntHashtable)readers2intrefs.get(reader);
+ int keys[] = t.toOrderedKeys();
+ for (int k = 0; k < keys.length; ++k) {
+ PRIndirectReference ref = new PRIndirectReference(reader, keys[k]);
+ addToBody(PdfReader.getPdfObjectRelease(ref), t.get(keys[k]));
+ }
+ }
+ finally {
+ try {
+ file.close();
+ reader.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ }
+ pdf.close();
+ }
+
+ void addPageOffsetToField(HashMap fd, int pageOffset) {
+ if (pageOffset == 0)
+ return;
+ for (Iterator it = fd.values().iterator(); it.hasNext();) {
+ ArrayList page = ((AcroFields.Item)it.next()).page;
+ for (int k = 0; k < page.size(); ++k)
+ page.set(k, new Integer(((Integer)page.get(k)).intValue() + pageOffset));
+ }
+ }
+
+ void createWidgets(ArrayList list, AcroFields.Item item) {
+ for (int k = 0; k < item.merged.size(); ++k) {
+ list.add(item.page.get(k));
+ PdfDictionary merged = (PdfDictionary)item.merged.get(k);
+ PdfObject dr = merged.get(PdfName.DR);
+ if (dr != null)
+ PdfFormField.mergeResources(resources, (PdfDictionary)PdfReader.getPdfObject(dr));
+ PdfDictionary widget = new PdfDictionary();
+ for (Iterator it = merged.getKeys().iterator(); it.hasNext();) {
+ PdfName key = (PdfName)it.next();
+ if (widgetKeys.containsKey(key))
+ widget.put(key, merged.get(key));
+ }
+ widget.put(iTextTag, new PdfNumber(((Integer)item.tabOrder.get(k)).intValue() + 1));
+ list.add(widget);
+ }
+ }
+
+ void mergeField(String name, AcroFields.Item item) {
+ HashMap map = fieldTree;
+ StringTokenizer tk = new StringTokenizer(name, ".");
+ if (!tk.hasMoreTokens())
+ return;
+ while (true) {
+ String s = tk.nextToken();
+ Object obj = map.get(s);
+ if (tk.hasMoreTokens()) {
+ if (obj == null) {
+ obj = new HashMap();
+ map.put(s, obj);
+ map = (HashMap)obj;
+ continue;
+ }
+ else if (obj instanceof HashMap)
+ map = (HashMap)obj;
+ else
+ return;
+ }
+ else {
+ if (obj instanceof HashMap)
+ return;
+ PdfDictionary merged = (PdfDictionary)item.merged.get(0);
+ if (obj == null) {
+ PdfDictionary field = new PdfDictionary();
+ for (Iterator it = merged.getKeys().iterator(); it.hasNext();) {
+ PdfName key = (PdfName)it.next();
+ if (fieldKeys.containsKey(key))
+ field.put(key, merged.get(key));
+ }
+ ArrayList list = new ArrayList();
+ list.add(field);
+ createWidgets(list, item);
+ map.put(s, list);
+ }
+ else {
+ ArrayList list = (ArrayList)obj;
+ PdfDictionary field = (PdfDictionary)list.get(0);
+ PdfName type1 = (PdfName)field.get(PdfName.FT);
+ PdfName type2 = (PdfName)merged.get(PdfName.FT);
+ if (type1 == null || !type1.equals(type2))
+ return;
+ int flag1 = 0;
+ PdfObject f1 = field.get(PdfName.FF);
+ if (f1 != null && f1.isNumber())
+ flag1 = ((PdfNumber)f1).intValue();
+ int flag2 = 0;
+ PdfObject f2 = merged.get(PdfName.FF);
+ if (f2 != null && f2.isNumber())
+ flag2 = ((PdfNumber)f2).intValue();
+ if (type1.equals(PdfName.BTN)) {
+ if (((flag1 ^ flag2) & PdfFormField.FF_PUSHBUTTON) != 0)
+ return;
+ if ((flag1 & PdfFormField.FF_PUSHBUTTON) == 0 && ((flag1 ^ flag2) & PdfFormField.FF_RADIO) != 0)
+ return;
+ }
+ else if (type1.equals(PdfName.CH)) {
+ if (((flag1 ^ flag2) & PdfFormField.FF_COMBO) != 0)
+ return;
+ }
+ createWidgets(list, item);
+ }
+ return;
+ }
+ }
+ }
+
+ void mergeWithMaster(HashMap fd) {
+ for (Iterator it = fd.keySet().iterator(); it.hasNext();) {
+ String name = (String)it.next();
+ mergeField(name, (AcroFields.Item)fd.get(name));
+ }
+ }
+
+ void mergeFields() {
+ int pageOffset = 0;
+ for (int k = 0; k < fields.size(); ++k) {
+ HashMap fd = ((AcroFields)fields.get(k)).getFields();
+ addPageOffsetToField(fd, pageOffset);
+ mergeWithMaster(fd);
+ pageOffset += ((PdfReader)readers.get(k)).getNumberOfPages();
+ }
+ }
+
+ public PdfIndirectReference getPageReference(int page) {
+ return (PdfIndirectReference)pageRefs.get(page - 1);
+ }
+
+ protected PdfDictionary getCatalog(PdfIndirectReference rootObj) {
+ try {
+ PdfDictionary cat = ((PdfDocument)document).getCatalog(rootObj);
+ if (form != null) {
+ PdfIndirectReference ref = addToBody(form).getIndirectReference();
+ cat.put(PdfName.ACROFORM, ref);
+ }
+ if (newBookmarks == null || newBookmarks.size() == 0)
+ return cat;
+ PdfDictionary top = new PdfDictionary();
+ PdfIndirectReference topRef = getPdfIndirectReference();
+ Object kids[] = SimpleBookmark.iterateOutlines(this, topRef, newBookmarks, false);
+ top.put(PdfName.FIRST, (PdfIndirectReference)kids[0]);
+ top.put(PdfName.LAST, (PdfIndirectReference)kids[1]);
+ top.put(PdfName.COUNT, new PdfNumber(((Integer)kids[2]).intValue()));
+ addToBody(top, topRef);
+ cat.put(PdfName.OUTLINES, topRef);
+ return cat;
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ protected PdfIndirectReference getNewReference(PRIndirectReference ref) {
+ return new PdfIndirectReference(0, getNewObjectNumber(ref.getReader(), ref.getNumber(), 0));
+ }
+
+ protected int getNewObjectNumber(PdfReader reader, int number, int generation) {
+ IntHashtable refs = (IntHashtable)readers2intrefs.get(reader);
+ int n = refs.get(number);
+ if (n == 0) {
+ n = getIndirectReferenceNumber();
+ refs.put(number, n);
+ }
+ return n;
+ }
+
+ protected boolean isVisited(PdfReader reader, int number, int generation) {
+ IntHashtable refs = (IntHashtable)readers2intrefs.get(reader);
+ return refs.containsKey(number);
+ }
+
+ protected boolean isVisited(PRIndirectReference ref) {
+ IntHashtable refs = (IntHashtable)visited.get(ref.getReader());
+ return refs.containsKey(ref.getNumber());
+ }
+
+ protected boolean setVisited(PRIndirectReference ref) {
+ IntHashtable refs = (IntHashtable)visited.get(ref.getReader());
+ return (refs.put(ref.getNumber(), 1) != 0);
+ }
+
+ protected boolean isPage(PRIndirectReference ref) {
+ IntHashtable refs = (IntHashtable)pages2intrefs.get(ref.getReader());
+ return refs.containsKey(ref.getNumber());
+ }
+
+ RandomAccessFileOrArray getReaderFile(PdfReader reader) {
+ return file;
+ }
+
+ /**
+ * Sets the bookmarks. The list structure is defined in
+ *
+ * PDF defines a standard date format. The PDF date format closely follows the format
+ * defined by the international standard ASN.1 (Abstract Syntax Notation One, defined
+ * in CCITT X.208 or ISO/IEC 8824). A date is a
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 7.2 (page 183-184)
+ *
+ * @see PdfString
+ * @see java.util.GregorianCalendar
+ */
+
+public class PdfDate extends PdfString {
+
+ private static final int dateSpace[] = {Calendar.YEAR, 4, 0, Calendar.MONTH, 2, -1, Calendar.DAY_OF_MONTH, 2, 0,
+ Calendar.HOUR_OF_DAY, 2, 0, Calendar.MINUTE, 2, 0, Calendar.SECOND, 2, 0};
+
+ // constructors
+
+/**
+ * Constructs a
+ * If type equals FITB, the bounding box of a page
+ * will fit the window of the Reader. Otherwise the type will be set to
+ * FIT so that the entire page will fit to the window.
+ *
+ * @param type The destination type
+ */
+
+ public PdfDestination(int type) {
+ super();
+ if (type == FITB) {
+ add(PdfName.FITB);
+ }
+ else {
+ add(PdfName.FIT);
+ }
+ }
+
+/**
+ * Constructs a new
+ * If type equals FITBH / FITBV,
+ * the width / height of the bounding box of a page will fit the window
+ * of the Reader. The parameter will specify the y / x coordinate of the
+ * top / left edge of the window. If the type equals FITH
+ * or FITV the width / height of the entire page will fit
+ * the window and the parameter will specify the y / x coordinate of the
+ * top / left edge. In all other cases the type will be set to FITH.
+ *
+ * @param type the destination type
+ * @param parameter a parameter to combined with the destination type
+ */
+
+ public PdfDestination(int type, float parameter) {
+ super(new PdfNumber(parameter));
+ switch(type) {
+ default:
+ addFirst(PdfName.FITH);
+ break;
+ case FITV:
+ addFirst(PdfName.FITV);
+ break;
+ case FITBH:
+ addFirst(PdfName.FITBH);
+ break;
+ case FITBV:
+ addFirst(PdfName.FITBV);
+ }
+ }
+
+/** Constructs a new
+ * Display the page, with the coordinates (left, top) positioned
+ * at the top-left corner of the window and the contents of the page magnified
+ * by the factor zoom. A negative value for any of the parameters left or top, or a
+ * zoom value of 0 specifies that the current value of that parameter is to be retained unchanged.
+ * @param type must be a PdfDestination.XYZ
+ * @param left the left value. Negative to place a null
+ * @param top the top value. Negative to place a null
+ * @param zoom The zoom factor. A value of 0 keeps the current value
+ */
+
+ public PdfDestination(int type, float left, float top, float zoom) {
+ super(PdfName.XYZ);
+ if (left < 0)
+ add(PdfNull.PDFNULL);
+ else
+ add(new PdfNumber(left));
+ if (top < 0)
+ add(PdfNull.PDFNULL);
+ else
+ add(new PdfNumber(top));
+ add(new PdfNumber(zoom));
+ }
+
+/** Constructs a new
+ * Display the page, with its contents magnified just enough
+ * to fit the rectangle specified by the coordinates left, bottom, right, and top
+ * entirely within the window both horizontally and vertically. If the required
+ * horizontal and vertical magnification factors are different, use the smaller of
+ * the two, centering the rectangle within the window in the other dimension.
+ *
+ * @param type must be PdfDestination.FITR
+ * @param left a parameter
+ * @param bottom a parameter
+ * @param right a parameter
+ * @param top a parameter
+ * @since iText0.38
+ */
+
+ public PdfDestination(int type, float left, float bottom, float right, float top) {
+ super(PdfName.FITR);
+ add(new PdfNumber(left));
+ add(new PdfNumber(bottom));
+ add(new PdfNumber(right));
+ add(new PdfNumber(top));
+ }
+
+ // methods
+
+/**
+ * Checks if an indirect reference to a page has been added.
+ *
+ * @return
+ * A dictionary is an associative table containing pairs of objects. The first element
+ * of each pair is called the key and the second element is called the value.
+ * Unlike dictionaries in the PostScript language, a key must be a
+ *
+ * @see PdfObject
+ * @see PdfName
+ * @see BadPdfFormatException
+ */
+
+public class PdfDictionary extends PdfObject {
+
+ // static membervariables (types of dictionary's)
+
+/** This is a possible type of dictionary */
+ public static final PdfName FONT = PdfName.FONT;
+
+/** This is a possible type of dictionary */
+ public static final PdfName OUTLINES = PdfName.OUTLINES;
+
+/** This is a possible type of dictionary */
+ public static final PdfName PAGE = PdfName.PAGE;
+
+/** This is a possible type of dictionary */
+ public static final PdfName PAGES = PdfName.PAGES;
+
+/** This is a possible type of dictionary */
+ public static final PdfName CATALOG = PdfName.CATALOG;
+
+ // membervariables
+
+/** This is the type of this dictionary */
+ private PdfName dictionaryType = null;
+
+/** This is the hashmap that contains all the values and keys of the dictionary */
+ protected HashMap hashMap;
+
+ /**
+ * This list preserves the order of the elements.
+ */
+ protected List list;
+
+ // constructors
+
+/**
+ * Constructs an empty
+ * A
+ * A document's trailer may contain a reference to an Info dictionary that provides information
+ * about the document. This optional dictionary may contain one or more keys, whose values
+ * should be strings.
+ * The Catalog is a dictionary that is the root node of the document. It contains a reference
+ * to the tree of pages contained in the document, a reference to the tree of objects representing
+ * the document's outline, a reference to the document's article threads, and the list of named
+ * destinations. In addition, the Catalog indicates whether the document's outline or thumbnail
+ * page images should be displayed automatically when the document is viewed and wether some location
+ * other than the first page should be shown when the document is opened.
+ * You have to open the document before you can begin to add content
+ * to the body of the document.
+ */
+
+ public void open() {
+ if (!open) {
+ super.open();
+ writer.open();
+ rootOutline = new PdfOutline(writer);
+ currentOutline = rootOutline;
+ }
+ try {
+ initPage();
+ }
+ catch(DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ void outlineTree(PdfOutline outline) throws IOException {
+ outline.setIndirectReference(writer.getPdfIndirectReference());
+ if (outline.parent() != null)
+ outline.put(PdfName.PARENT, outline.parent().indirectReference());
+ ArrayList kids = outline.getKids();
+ int size = kids.size();
+ for (int k = 0; k < size; ++k)
+ outlineTree((PdfOutline)kids.get(k));
+ for (int k = 0; k < size; ++k) {
+ if (k > 0)
+ ((PdfOutline)kids.get(k)).put(PdfName.PREV, ((PdfOutline)kids.get(k - 1)).indirectReference());
+ if (k < size - 1)
+ ((PdfOutline)kids.get(k)).put(PdfName.NEXT, ((PdfOutline)kids.get(k + 1)).indirectReference());
+ }
+ if (size > 0) {
+ outline.put(PdfName.FIRST, ((PdfOutline)kids.get(0)).indirectReference());
+ outline.put(PdfName.LAST, ((PdfOutline)kids.get(size - 1)).indirectReference());
+ }
+ for (int k = 0; k < size; ++k) {
+ PdfOutline kid = (PdfOutline)kids.get(k);
+ writer.addToBody(kid, kid.indirectReference());
+ }
+ }
+
+ void writeOutlines() throws IOException {
+ if (rootOutline.getKids().size() == 0)
+ return;
+ outlineTree(rootOutline);
+ writer.addToBody(rootOutline, rootOutline.indirectReference());
+ }
+
+ void traverseOutlineCount(PdfOutline outline) {
+ ArrayList kids = outline.getKids();
+ PdfOutline parent = outline.parent();
+ if (kids.size() == 0) {
+ if (parent != null) {
+ parent.setCount(parent.getCount() + 1);
+ }
+ }
+ else {
+ for (int k = 0; k < kids.size(); ++k) {
+ traverseOutlineCount((PdfOutline)kids.get(k));
+ }
+ if (parent != null) {
+ if (outline.isOpen()) {
+ parent.setCount(outline.getCount() + parent.getCount() + 1);
+ }
+ else {
+ parent.setCount(parent.getCount() + 1);
+ outline.setCount(-outline.getCount());
+ }
+ }
+ }
+ }
+
+ void calculateOutlineCount() {
+ if (rootOutline.getKids().size() == 0)
+ return;
+ traverseOutlineCount(rootOutline);
+ }
+ /**
+ * Closes the document.
+ *
+ * Once all the content has been written in the body, you have to close
+ * the body. After that nothing can be written to the body anymore.
+ */
+
+ public void close() {
+ if (close) {
+ return;
+ }
+ try {
+ boolean wasImage = (imageWait != null);
+ newPage();
+ if (imageWait != null || wasImage) newPage();
+ if (annotations.size() > 0)
+ throw new RuntimeException(annotations.size() + " annotations had invalid placement pages.");
+ PdfPageEvent pageEvent = writer.getPageEvent();
+ if (pageEvent != null)
+ pageEvent.onCloseDocument(writer, this);
+ super.close();
+
+ writer.addLocalDestinations(localDestinations);
+ calculateOutlineCount();
+ writeOutlines();
+ }
+ catch(Exception e) {
+ throw new ExceptionConverter(e);
+ }
+
+ writer.close();
+ }
+
+ PageResources getPageResources() {
+ return pageResources;
+ }
+
+ /** Adds a
+ * If the footer/header is set, it is printed.
+ * @throws DocumentException on error
+ */
+
+ private void initPage() throws DocumentException {
+
+ // initialisation of some page objects
+ markPoint = 0;
+ annotations = delayedAnnotations;
+ delayedAnnotations = new ArrayList();
+ pageResources = new PageResources();
+ writer.resetContent();
+
+ // the pagenumber is incremented
+ pageN++;
+
+ // graphics and text are initialized
+ float oldleading = leading;
+ int oldAlignment = alignment;
+
+ if (marginMirroring && (getPageNumber() & 1) == 0) {
+ marginRight = nextMarginLeft;
+ marginLeft = nextMarginRight;
+ }
+ else {
+ marginLeft = nextMarginLeft;
+ marginRight = nextMarginRight;
+ }
+ marginTop = nextMarginTop;
+ marginBottom = nextMarginBottom;
+ imageEnd = -1;
+ imageIndentRight = 0;
+ imageIndentLeft = 0;
+ graphics = new PdfContentByte(writer);
+ text = new PdfContentByte(writer);
+ text.beginText();
+ text.moveText(left(), top());
+ textEmptySize = text.size();
+ text.reset();
+ text.beginText();
+ leading = 16;
+ indentBottom = 0;
+ indentTop = 0;
+ currentHeight = 0;
+
+ // backgroundcolors, etc...
+ pageSize = nextPageSize;
+ thisBoxSize = new HashMap(boxSize);
+ if (pageSize.backgroundColor() != null
+ || pageSize.hasBorders()
+ || pageSize.borderColor() != null) {
+ add(pageSize);
+ }
+
+ // if there is a watermark, the watermark is added
+ if (watermark != null) {
+ float mt[] = watermark.matrix();
+ graphics.addImage(watermark, mt[0], mt[1], mt[2], mt[3], watermark.offsetX() - mt[4], watermark.offsetY() - mt[5]);
+ }
+
+ // if there is a footer, the footer is added
+ if (footer != null) {
+ /*
+ Added by Edgar Leonardo Prieto Perilla
+ */
+ // Avoid footer identation
+ float tmpIndentLeft = indentLeft;
+ float tmpIndentRight = indentRight;
+ // Begin added: Bonf (Marc Schneider) 2003-07-29
+ float tmpListIndentLeft = listIndentLeft;
+ float tmpImageIndentLeft = imageIndentLeft;
+ float tmpImageIndentRight = imageIndentRight;
+ // End added: Bonf (Marc Schneider) 2003-07-29
+
+ indentLeft = indentRight = 0;
+ // Begin added: Bonf (Marc Schneider) 2003-07-29
+ listIndentLeft = 0;
+ imageIndentLeft = 0;
+ imageIndentRight = 0;
+ // End added: Bonf (Marc Schneider) 2003-07-29
+ /*
+ End Added by Edgar Leonardo Prieto Perilla
+ */
+
+ footer.setPageNumber(pageN);
+ leading = footer.paragraph().leading();
+ add(footer.paragraph());
+ // adding the footer limits the height
+ indentBottom = currentHeight;
+ text.moveText(left(), indentBottom());
+ flushLines();
+ text.moveText(-left(), -bottom());
+ footer.setTop(bottom(currentHeight));
+ footer.setBottom(bottom() - (0.75f * leading));
+ footer.setLeft(left());
+ footer.setRight(right());
+ graphics.rectangle(footer);
+ indentBottom = currentHeight + leading * 2;
+ currentHeight = 0;
+
+ /*
+ Added by Edgar Leonardo Prieto Perilla
+ */
+ indentLeft = tmpIndentLeft;
+ indentRight = tmpIndentRight;
+ // Begin added: Bonf (Marc Schneider) 2003-07-29
+ listIndentLeft = tmpListIndentLeft;
+ imageIndentLeft = tmpImageIndentLeft;
+ imageIndentRight = tmpImageIndentRight;
+ // End added: Bonf (Marc Schneider) 2003-07-29
+ /*
+ End Added by Edgar Leonardo Prieto Perilla
+ */
+ }
+
+ // we move to the left/top position of the page
+ text.moveText(left(), top());
+
+ // if there is a header, the header = added
+ if (header != null) {
+ /*
+ Added by Edgar Leonardo Prieto Perilla
+ */
+ // Avoid header identation
+ float tmpIndentLeft = indentLeft;
+ float tmpIndentRight = indentRight;
+ // Begin added: Bonf (Marc Schneider) 2003-07-29
+ float tmpListIndentLeft = listIndentLeft;
+ float tmpImageIndentLeft = imageIndentLeft;
+ float tmpImageIndentRight = imageIndentRight;
+ // End added: Bonf (Marc Schneider) 2003-07-29
+
+ indentLeft = indentRight = 0;
+ // Added: Bonf
+ listIndentLeft = 0;
+ imageIndentLeft = 0;
+ imageIndentRight = 0;
+ // End added: Bonf
+ /*
+ End Added by Edgar Leonardo Prieto Perilla
+ */
+
+ header.setPageNumber(pageN);
+ leading = header.paragraph().leading();
+ text.moveText(0, leading);
+ add(header.paragraph());
+ newLine();
+ indentTop = currentHeight - leading;
+ header.setTop(top() + leading);
+ header.setBottom(indentTop() + leading * 2 / 3);
+ header.setLeft(left());
+ header.setRight(right());
+ graphics.rectangle(header);
+ flushLines();
+ currentHeight = 0;
+
+ /*
+ Added by Edgar Leonardo Prieto Perilla
+ */
+ // Restore identation
+ indentLeft = tmpIndentLeft;
+ indentRight = tmpIndentRight;
+ // Begin added: Bonf (Marc Schneider) 2003-07-29
+ listIndentLeft = tmpListIndentLeft;
+ imageIndentLeft = tmpImageIndentLeft;
+ imageIndentRight = tmpImageIndentRight;
+ // End added: Bonf (Marc Schneider) 2003-07-29
+ /*
+ End Added by Edgar Leonardo Prieto Perilla
+ */
+ }
+
+ pageEmpty = true;
+
+ // if there is an image waiting to be drawn, draw it
+ try {
+ if (imageWait != null) {
+ add(imageWait);
+ imageWait = null;
+ }
+ }
+ catch(Exception e) {
+ throw new ExceptionConverter(e);
+ }
+
+ leading = oldleading;
+ alignment = oldAlignment;
+ carriageReturn();
+ PdfPageEvent pageEvent = writer.getPageEvent();
+ if (pageEvent != null) {
+ if (firstPageEvent) {
+ pageEvent.onOpenDocument(writer, this);
+ }
+ pageEvent.onStartPage(writer, this);
+ }
+ firstPageEvent = false;
+ }
+
+ /**
+ * If the current line is not empty or null, it is added to the arraylist
+ * of lines and a new empty line is added.
+ * @throws DocumentException on error
+ */
+
+ private void carriageReturn() throws DocumentException {
+ // the arraylist with lines may not be null
+ if (lines == null) {
+ lines = new ArrayList();
+ }
+ // If the current line is not null
+ if (line != null) {
+ // we check if the end of the page is reached (bugfix by Francois Gravel)
+ if (currentHeight + line.height() + leading < indentTop() - indentBottom()) {
+ // if so nonempty lines are added and the heigt is augmented
+ if (line.size() > 0) {
+ currentHeight += line.height();
+ lines.add(line);
+ pageEmpty = false;
+ }
+ }
+ // if the end of the line is reached, we start a new page
+ else {
+ newPage();
+ }
+ }
+ if (imageEnd > -1 && currentHeight > imageEnd) {
+ imageEnd = -1;
+ imageIndentRight = 0;
+ imageIndentLeft = 0;
+ }
+ // a new current line is constructed
+ line = new PdfLine(indentLeft(), indentRight(), alignment, leading);
+ }
+
+ /**
+ * Adds the current line to the list of lines and also adds an empty line.
+ * @throws DocumentException on error
+ */
+
+ private void newLine() throws DocumentException {
+ lastElementType = -1;
+ carriageReturn();
+ if (lines != null && lines.size() > 0) {
+ lines.add(line);
+ currentHeight += line.height();
+ }
+ line = new PdfLine(indentLeft(), indentRight(), alignment, leading);
+ }
+
+ /**
+ * Writes all the lines to the text-object.
+ *
+ * @return the displacement that was caused
+ * @throws DocumentException on error
+ */
+
+ private float flushLines() throws DocumentException {
+
+ // checks if the ArrayList with the lines is not null
+ if (lines == null) {
+ return 0;
+ }
+
+ //add by Jin-Hsia Yang
+ boolean newline=false;
+ //end add by Jin-Hsia Yang
+
+ // checks if a new Line has to be made.
+ if (line != null && line.size() > 0) {
+ lines.add(line);
+ line = new PdfLine(indentLeft(), indentRight(), alignment, leading);
+
+ //add by Jin-Hsia Yang
+ newline=true;
+ //end add by Jin-Hsia Yang
+
+ }
+
+ // checks if the ArrayList with the lines is empty
+ if (lines.size() == 0) {
+ return 0;
+ }
+
+ // initialisation of some parameters
+ Object currentValues[] = new Object[2];
+ PdfFont currentFont = null;
+ float displacement = 0;
+ PdfLine l;
+ Float lastBaseFactor = new Float(0);
+ currentValues[1] = lastBaseFactor;
+ // looping over all the lines
+ for (Iterator i = lines.iterator(); i.hasNext(); ) {
+
+ // this is a line in the loop
+ l = (PdfLine) i.next();
+
+ if(isNewpage && newline) { // fix Ken@PDI
+ newline=false;
+ text.moveText(l.indentLeft() - indentLeft() + listIndentLeft + paraIndent,-l.height());
+ }
+ else {
+ text.moveText(l.indentLeft() - indentLeft() + listIndentLeft, -l.height());
+ }
+
+ // is the line preceeded by a symbol?
+ if (l.listSymbol() != null) {
+ ColumnText.showTextAligned(graphics, Element.ALIGN_LEFT, new Phrase(l.listSymbol()), text.getXTLM() - l.listIndent(), text.getYTLM(), 0);
+ }
+
+ currentValues[0] = currentFont;
+
+ writeLineToContent(l, text, graphics, currentValues, writer.getSpaceCharRatio());
+
+ currentFont = (PdfFont)currentValues[0];
+
+ displacement += l.height();
+ if (indentLeft() - listIndentLeft != l.indentLeft()) {
+ text.moveText(indentLeft() - l.indentLeft() - listIndentLeft, 0);
+ }
+
+ }
+ lines = new ArrayList();
+ return displacement;
+ }
+
+ // methods to retrieve information
+
+ /**
+ * Gets the
+ * Before entering the line position must have been established and the
+ *
+ * dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+ *
+ * To read in the time and get a date which is compatible with our local
+ * time zone.
+ */
+ public String getTime()
+ {
+ //
+ // standardise the format.
+ //
+ if (time.charAt(time.length() - 1) == 'Z')
+ {
+ return time.substring(0, time.length() - 1) + "GMT+00:00";
+ }
+ else
+ {
+ int signPos = time.length() - 5;
+ char sign = time.charAt(signPos);
+ if (sign == '-' || sign == '+')
+ {
+ return time.substring(0, signPos)
+ + "GMT"
+ + time.substring(signPos, signPos + 3)
+ + ":"
+ + time.substring(signPos + 3);
+ }
+ else
+ {
+ signPos = time.length() - 3;
+ sign = time.charAt(signPos);
+ if (sign == '-' || sign == '+')
+ {
+ return time.substring(0, signPos)
+ + "GMT"
+ + time.substring(signPos)
+ + ":00";
+ }
+ }
+ }
+
+ return time;
+ }
+
+ public Date getDate()
+ throws ParseException
+ {
+ SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
+
+ dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
+
+ return dateF.parse(time);
+ }
+
+ private byte[] getOctets()
+ {
+ char[] cs = time.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(GENERALIZED_TIME, this.getOctets());
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERGeneralizedTime))
+ {
+ return false;
+ }
+
+ return time.equals(((DERGeneralizedTime)o).time);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERIA5String.java b/src/main/java/com/lowagie/bc/asn1/DERIA5String.java
new file mode 100644
index 0000000..d60cdc6
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERIA5String.java
@@ -0,0 +1,123 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER IA5String object - this is an ascii string.
+ */
+public class DERIA5String
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a IA5 string from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERIA5String getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERIA5String)
+ {
+ return (DERIA5String)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERIA5String(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an IA5 String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERIA5String getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - with bytes.
+ */
+ public DERIA5String(
+ byte[] string)
+ {
+ char[] cs = new char[string.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(string[i] & 0xff);
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor - with string.
+ */
+ public DERIA5String(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public byte[] getOctets()
+ {
+ char[] cs = string.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(IA5_STRING, this.getOctets());
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DERIA5String))
+ {
+ return false;
+ }
+
+ DERIA5String s = (DERIA5String)o;
+
+ return this.getString().equals(s.getString());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERInputStream.java b/src/main/java/com/lowagie/bc/asn1/DERInputStream.java
new file mode 100644
index 0000000..3148a3f
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERInputStream.java
@@ -0,0 +1,258 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Don't use this class. It will eventually disappear, use ASN1InputStream.
+ *
+ * This class is scheduled for removal.
+ */
+public class DERInputStream
+ extends FilterInputStream implements DERTags
+{
+ public DERInputStream(
+ InputStream is)
+ {
+ super(is);
+ }
+
+ protected int readLength()
+ throws IOException
+ {
+ int length = read();
+ if (length < 0)
+ {
+ throw new IOException("EOF found when length expected");
+ }
+
+ if (length == 0x80)
+ {
+ return -1; // indefinite-length encoding
+ }
+
+ if (length > 127)
+ {
+ int size = length & 0x7f;
+
+ length = 0;
+ for (int i = 0; i < size; i++)
+ {
+ int next = read();
+
+ if (next < 0)
+ {
+ throw new IOException("EOF found reading length");
+ }
+
+ length = (length << 8) + next;
+ }
+ }
+
+ return length;
+ }
+
+ protected void readFully(
+ byte[] bytes)
+ throws IOException
+ {
+ int left = bytes.length;
+
+ if (left == 0)
+ {
+ return;
+ }
+
+ while (left > 0)
+ {
+ int l = read(bytes, bytes.length - left, left);
+
+ if (l < 0)
+ {
+ throw new EOFException("unexpected end of stream");
+ }
+
+ left -= l;
+ }
+ }
+
+ /**
+ * build an object given its tag and a byte stream to construct it
+ * from.
+ */
+ protected DERObject buildObject(
+ int tag,
+ byte[] bytes)
+ throws IOException
+ {
+ switch (tag)
+ {
+ case NULL:
+ return null;
+ case SEQUENCE | CONSTRUCTED:
+ ByteArrayInputStream bIn = new ByteArrayInputStream(bytes);
+ BERInputStream dIn = new BERInputStream(bIn);
+ DERConstructedSequence seq = new DERConstructedSequence();
+
+ try
+ {
+ for (;;)
+ {
+ DERObject obj = dIn.readObject();
+
+ seq.addObject(obj);
+ }
+ }
+ catch (EOFException ex)
+ {
+ return seq;
+ }
+ case SET | CONSTRUCTED:
+ bIn = new ByteArrayInputStream(bytes);
+ dIn = new BERInputStream(bIn);
+
+ ASN1EncodableVector v = new ASN1EncodableVector();
+
+ try
+ {
+ for (;;)
+ {
+ DERObject obj = dIn.readObject();
+
+ v.add(obj);
+ }
+ }
+ catch (EOFException ex)
+ {
+ return new DERConstructedSet(v);
+ }
+ case BOOLEAN:
+ return new DERBoolean(bytes);
+ case INTEGER:
+ return new DERInteger(bytes);
+ case ENUMERATED:
+ return new DEREnumerated(bytes);
+ case OBJECT_IDENTIFIER:
+ return new DERObjectIdentifier(bytes);
+ case BIT_STRING:
+ int padBits = bytes[0];
+ byte[] data = new byte[bytes.length - 1];
+
+ System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
+
+ return new DERBitString(data, padBits);
+ case UTF8_STRING:
+ return new DERUTF8String(bytes);
+ case PRINTABLE_STRING:
+ return new DERPrintableString(bytes);
+ case IA5_STRING:
+ return new DERIA5String(bytes);
+ case T61_STRING:
+ return new DERT61String(bytes);
+ case VISIBLE_STRING:
+ return new DERVisibleString(bytes);
+ case UNIVERSAL_STRING:
+ return new DERUniversalString(bytes);
+ case GENERAL_STRING:
+ return new DERGeneralString(bytes);
+ case BMP_STRING:
+ return new DERBMPString(bytes);
+ case OCTET_STRING:
+ return new DEROctetString(bytes);
+ case UTC_TIME:
+ return new DERUTCTime(bytes);
+ case GENERALIZED_TIME:
+ return new DERGeneralizedTime(bytes);
+ default:
+ //
+ // with tagged object tag number is bottom 5 bits
+ //
+ if ((tag & TAGGED) != 0)
+ {
+ if ((tag & 0x1f) == 0x1f)
+ {
+ throw new IOException("unsupported high tag encountered");
+ }
+
+ if (bytes.length == 0) // empty tag!
+ {
+ if ((tag & CONSTRUCTED) == 0)
+ {
+ return new DERTaggedObject(false, tag & 0x1f, new DERNull());
+ }
+ else
+ {
+ return new DERTaggedObject(false, tag & 0x1f, new DERConstructedSequence());
+ }
+ }
+
+ //
+ // simple type - implicit... return an octet string
+ //
+ if ((tag & CONSTRUCTED) == 0)
+ {
+ return new DERTaggedObject(false, tag & 0x1f, new DEROctetString(bytes));
+ }
+
+ bIn = new ByteArrayInputStream(bytes);
+ dIn = new BERInputStream(bIn);
+
+ DEREncodable dObj = dIn.readObject();
+
+ //
+ // explicitly tagged (probably!) - if it isn't we'd have to
+ // tell from the context
+ //
+ if (dIn.available() == 0)
+ {
+ return new DERTaggedObject(tag & 0x1f, dObj);
+ }
+
+ //
+ // another implicit object, we'll create a sequence...
+ //
+ seq = new DERConstructedSequence();
+
+ seq.addObject(dObj);
+
+ try
+ {
+ for (;;)
+ {
+ dObj = dIn.readObject();
+
+ seq.addObject(dObj);
+ }
+ }
+ catch (EOFException ex)
+ {
+ // ignore --
+ }
+
+ return new DERTaggedObject(false, tag & 0x1f, seq);
+ }
+
+ return new DERUnknownTag(tag, bytes);
+ }
+ }
+
+ public DERObject readObject()
+ throws IOException
+ {
+ int tag = read();
+ if (tag == -1)
+ {
+ throw new EOFException();
+ }
+
+ int length = readLength();
+ byte[] bytes = new byte[length];
+
+ readFully(bytes);
+
+ return buildObject(tag, bytes);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERInteger.java b/src/main/java/com/lowagie/bc/asn1/DERInteger.java
new file mode 100644
index 0000000..9df0d62
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERInteger.java
@@ -0,0 +1,129 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+public class DERInteger
+ extends DERObject
+{
+ byte[] bytes;
+
+ /**
+ * return an integer from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERInteger getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERInteger)
+ {
+ return (DERInteger)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERInteger(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Integer from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERInteger getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ public DERInteger(
+ int value)
+ {
+ bytes = BigInteger.valueOf(value).toByteArray();
+ }
+
+ public DERInteger(
+ BigInteger value)
+ {
+ bytes = value.toByteArray();
+ }
+
+ public DERInteger(
+ byte[] bytes)
+ {
+ this.bytes = bytes;
+ }
+
+ public BigInteger getValue()
+ {
+ return new BigInteger(bytes);
+ }
+
+ /**
+ * in some cases positive values get crammed into a space,
+ * that's not quite big enough...
+ */
+ public BigInteger getPositiveValue()
+ {
+ return new BigInteger(1, bytes);
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(INTEGER, bytes);
+ }
+
+ public int hashCode()
+ {
+ int value = 0;
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ value ^= (bytes[i] & 0xff) << (i % 4);
+ }
+
+ return value;
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (o == null || !(o instanceof DERInteger))
+ {
+ return false;
+ }
+
+ DERInteger other = (DERInteger)o;
+
+ if (bytes.length != other.bytes.length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ if (bytes[i] != other.bytes[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERNull.java b/src/main/java/com/lowagie/bc/asn1/DERNull.java
new file mode 100644
index 0000000..3ce810a
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERNull.java
@@ -0,0 +1,34 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * A NULL object.
+ */
+public class DERNull
+ extends ASN1Null
+{
+ byte[] zeroBytes = new byte[0];
+
+ public DERNull()
+ {
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(NULL, zeroBytes);
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERNull))
+ {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERNumericString.java b/src/main/java/com/lowagie/bc/asn1/DERNumericString.java
new file mode 100644
index 0000000..6d58c6c
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERNumericString.java
@@ -0,0 +1,123 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }.
+ */
+public class DERNumericString
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a Numeric string from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERNumericString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERNumericString)
+ {
+ return (DERNumericString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERNumericString(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Numeric String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERNumericString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - with bytes.
+ */
+ public DERNumericString(
+ byte[] string)
+ {
+ char[] cs = new char[string.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(string[i] & 0xff);
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor - with string.
+ */
+ public DERNumericString(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public byte[] getOctets()
+ {
+ char[] cs = string.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(NUMERIC_STRING, this.getOctets());
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DERNumericString))
+ {
+ return false;
+ }
+
+ DERNumericString s = (DERNumericString)o;
+
+ return this.getString().equals(s.getString());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERObject.java b/src/main/java/com/lowagie/bc/asn1/DERObject.java
new file mode 100644
index 0000000..abb86d0
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERObject.java
@@ -0,0 +1,15 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+public abstract class DERObject
+ implements DERTags, DEREncodable
+{
+ public DERObject getDERObject()
+ {
+ return this;
+ }
+
+ abstract void encode(DEROutputStream out)
+ throws IOException;
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERObjectIdentifier.java b/src/main/java/com/lowagie/bc/asn1/DERObjectIdentifier.java
new file mode 100644
index 0000000..04c2d86
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERObjectIdentifier.java
@@ -0,0 +1,170 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class DERObjectIdentifier
+ extends DERObject
+{
+ String identifier;
+
+ /**
+ * return an OID from the passed in object
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERObjectIdentifier getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERObjectIdentifier)
+ {
+ return (DERObjectIdentifier)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERObjectIdentifier(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return an Object Identifier from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERObjectIdentifier getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+
+ DERObjectIdentifier(
+ byte[] bytes)
+ {
+ StringBuffer objId = new StringBuffer();
+ int value = 0;
+ boolean first = true;
+
+ for (int i = 0; i != bytes.length; i++)
+ {
+ int b = bytes[i] & 0xff;
+
+ value = value * 128 + (b & 0x7f);
+ if ((b & 0x80) == 0) // end of number reached
+ {
+ if (first)
+ {
+ switch (value / 40)
+ {
+ case 0:
+ objId.append('0');
+ break;
+ case 1:
+ objId.append('1');
+ value -= 40;
+ break;
+ default:
+ objId.append('2');
+ value -= 80;
+ }
+ first = false;
+ }
+
+ objId.append('.');
+ objId.append(Integer.toString(value));
+ value = 0;
+ }
+ }
+
+ this.identifier = objId.toString();
+ }
+
+ public DERObjectIdentifier(
+ String identifier)
+ {
+ this.identifier = identifier;
+ }
+
+ public String getId()
+ {
+ return identifier;
+ }
+
+ private void writeField(
+ OutputStream out,
+ int fieldValue)
+ throws IOException
+ {
+ if (fieldValue >= (1 << 7))
+ {
+ if (fieldValue >= (1 << 14))
+ {
+ if (fieldValue >= (1 << 21))
+ {
+ if (fieldValue >= (1 << 28))
+ {
+ out.write((fieldValue >> 28) | 0x80);
+ }
+ out.write((fieldValue >> 21) | 0x80);
+ }
+ out.write((fieldValue >> 14) | 0x80);
+ }
+ out.write((fieldValue >> 7) | 0x80);
+ }
+ out.write(fieldValue & 0x7f);
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ OIDTokenizer tok = new OIDTokenizer(identifier);
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ DEROutputStream dOut = new DEROutputStream(bOut);
+
+ writeField(bOut,
+ Integer.parseInt(tok.nextToken()) * 40
+ + Integer.parseInt(tok.nextToken()));
+
+ while (tok.hasMoreTokens())
+ {
+ writeField(bOut, Integer.parseInt(tok.nextToken()));
+ }
+
+ dOut.close();
+
+ byte[] bytes = bOut.toByteArray();
+
+ out.writeEncoded(OBJECT_IDENTIFIER, bytes);
+ }
+
+ public int hashCode()
+ {
+ return identifier.hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if ((o == null) || !(o instanceof DERObjectIdentifier))
+ {
+ return false;
+ }
+
+ return identifier.equals(((DERObjectIdentifier)o).identifier);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DEROctetString.java b/src/main/java/com/lowagie/bc/asn1/DEROctetString.java
new file mode 100644
index 0000000..46ab32d
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DEROctetString.java
@@ -0,0 +1,29 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+public class DEROctetString
+ extends ASN1OctetString
+{
+ /**
+ * @param string the octets making up the octet string.
+ */
+ public DEROctetString(
+ byte[] string)
+ {
+ super(string);
+ }
+
+ public DEROctetString(
+ DEREncodable obj)
+ {
+ super(obj);
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(OCTET_STRING, string);
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DEROutputStream.java b/src/main/java/com/lowagie/bc/asn1/DEROutputStream.java
new file mode 100644
index 0000000..457cae4
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DEROutputStream.java
@@ -0,0 +1,81 @@
+package com.lowagie.bc.asn1;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class DEROutputStream
+ extends FilterOutputStream implements DERTags
+{
+ public DEROutputStream(
+ OutputStream os)
+ {
+ super(os);
+ }
+
+ private void writeLength(
+ int length)
+ throws IOException
+ {
+ if (length > 127)
+ {
+ int size = 1;
+ int val = length;
+
+ while ((val >>>= 8) != 0)
+ {
+ size++;
+ }
+
+ write((byte)(size | 0x80));
+
+ for (int i = (size - 1) * 8; i >= 0; i -= 8)
+ {
+ write((byte)(length >> i));
+ }
+ }
+ else
+ {
+ write((byte)length);
+ }
+ }
+
+ void writeEncoded(
+ int tag,
+ byte[] bytes)
+ throws IOException
+ {
+ write(tag);
+ writeLength(bytes.length);
+ write(bytes);
+ }
+
+ protected void writeNull()
+ throws IOException
+ {
+ write(NULL);
+ write(0x00);
+ }
+
+ public void writeObject(
+ Object obj)
+ throws IOException
+ {
+ if (obj == null)
+ {
+ writeNull();
+ }
+ else if (obj instanceof DERObject)
+ {
+ ((DERObject)obj).encode(this);
+ }
+ else if (obj instanceof DEREncodable)
+ {
+ ((DEREncodable)obj).getDERObject().encode(this);
+ }
+ else
+ {
+ throw new IOException("object not DEREncodable");
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERPrintableString.java b/src/main/java/com/lowagie/bc/asn1/DERPrintableString.java
new file mode 100644
index 0000000..ac3f588
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERPrintableString.java
@@ -0,0 +1,123 @@
+package com.lowagie.bc.asn1;
+
+import java.io.IOException;
+
+/**
+ * DER PrintableString object.
+ */
+public class DERPrintableString
+ extends DERObject
+ implements DERString
+{
+ String string;
+
+ /**
+ * return a printable string from the passed in object.
+ *
+ * @exception IllegalArgumentException if the object cannot be converted.
+ */
+ public static DERPrintableString getInstance(
+ Object obj)
+ {
+ if (obj == null || obj instanceof DERPrintableString)
+ {
+ return (DERPrintableString)obj;
+ }
+
+ if (obj instanceof ASN1OctetString)
+ {
+ return new DERPrintableString(((ASN1OctetString)obj).getOctets());
+ }
+
+ if (obj instanceof ASN1TaggedObject)
+ {
+ return getInstance(((ASN1TaggedObject)obj).getObject());
+ }
+
+ throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
+ }
+
+ /**
+ * return a Printable String from a tagged object.
+ *
+ * @param obj the tagged object holding the object we want
+ * @param explicit true if the object is meant to be explicitly
+ * tagged false otherwise.
+ * @exception IllegalArgumentException if the tagged object cannot
+ * be converted.
+ */
+ public static DERPrintableString getInstance(
+ ASN1TaggedObject obj,
+ boolean explicit)
+ {
+ return getInstance(obj.getObject());
+ }
+
+ /**
+ * basic constructor - byte encoded string.
+ */
+ public DERPrintableString(
+ byte[] string)
+ {
+ char[] cs = new char[string.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ cs[i] = (char)(string[i] & 0xff);
+ }
+
+ this.string = new String(cs);
+ }
+
+ /**
+ * basic constructor
+ */
+ public DERPrintableString(
+ String string)
+ {
+ this.string = string;
+ }
+
+ public String getString()
+ {
+ return string;
+ }
+
+ public byte[] getOctets()
+ {
+ char[] cs = string.toCharArray();
+ byte[] bs = new byte[cs.length];
+
+ for (int i = 0; i != cs.length; i++)
+ {
+ bs[i] = (byte)cs[i];
+ }
+
+ return bs;
+ }
+
+ void encode(
+ DEROutputStream out)
+ throws IOException
+ {
+ out.writeEncoded(PRINTABLE_STRING, this.getOctets());
+ }
+
+ public int hashCode()
+ {
+ return this.getString().hashCode();
+ }
+
+ public boolean equals(
+ Object o)
+ {
+ if (!(o instanceof DERPrintableString))
+ {
+ return false;
+ }
+
+ DERPrintableString s = (DERPrintableString)o;
+
+ return this.getString().equals(s.getString());
+ }
+}
diff --git a/src/main/java/com/lowagie/bc/asn1/DERSequence.java b/src/main/java/com/lowagie/bc/asn1/DERSequence.java
new file mode 100644
index 0000000..5ffd2dd
--- /dev/null
+++ b/src/main/java/com/lowagie/bc/asn1/DERSequence.java
@@ -0,0 +1,67 @@
+package com.lowagie.bc.asn1;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+
+public class DERSequence
+ extends ASN1Sequence
+{
+ /**
+ * create an empty sequence
+ */
+ public DERSequence()
+ {
+ }
+
+ /**
+ * create a sequence containing one object
+ */
+ public DERSequence(
+ DEREncodable obj)
+ {
+ this.addObject(obj);
+ }
+
+ /**
+ * create a sequence containing a vector of objects.
+ */
+ public DERSequence(
+ DEREncodableVector v)
+ {
+ for (int i = 0; i != v.size(); i++)
+ {
+ this.addObject(v.get(i));
+ }
+ }
+
+ /*
+ * A note on the implementation:
+ *
+ * dateF = new SimpleDateFormat("yyMMddHHmmssz");
+ *
+ * To read in the time and get a date which is compatible with our local
+ * time zone.
+ * Anchor
can be a reference or a destination of a reference.
+ * Anchor
is a special kind of Phrase
.
+ * It is constructed in the same way.
+ *
+ *
+ * @see Element
+ * @see Phrase
+ */
+
+public class Anchor extends Phrase implements TextElementArray, MarkupAttributes {
+
+ // membervariables
+
+/** This is the anchor tag. */
+ public static final String ANCHOR = "anchor";
+
+/** This is the name of the
+ * Anchor anchor = new Anchor("this is a link");
+ * anchor.setName("LINK");
+ * anchor.setReference("http://www.lowagie.com");
+ *
Anchor
. */
+ protected String name = null;
+
+/** This is the reference of the Anchor
. */
+ protected String reference = null;
+
+ // constructors
+
+/**
+ * Constructs an Anchor
without specifying a leading.
+ */
+
+ public Anchor() {
+ super(16);
+ }
+
+/**
+ * Constructs an Anchor
with a certain leading.
+ *
+ * @param leading the leading
+ */
+
+ public Anchor(float leading) {
+ super(leading);
+ }
+
+/**
+ * Constructs an Anchor
with a certain Chunk
.
+ *
+ * @param chunk a Chunk
+ */
+
+ public Anchor(Chunk chunk) {
+ super(chunk);
+ }
+
+/**
+ * Constructs an Anchor
with a certain String
.
+ *
+ * @param string a String
+ */
+
+ public Anchor(String string) {
+ super(string);
+ }
+
+/**
+ * Constructs an Anchor
with a certain String
+ * and a certain Font
.
+ *
+ * @param string a String
+ * @param font a Font
+ */
+
+ public Anchor(String string, Font font) {
+ super(string, font);
+ }
+
+/**
+ * Constructs an Anchor
with a certain Chunk
+ * and a certain leading.
+ *
+ * @param leading the leading
+ * @param chunk a Chunk
+ */
+
+ public Anchor(float leading, Chunk chunk) {
+ super(leading, chunk);
+ }
+
+/**
+ * Constructs an Anchor
with a certain leading
+ * and a certain String
.
+ *
+ * @param leading the leading
+ * @param string a String
+ */
+
+ public Anchor(float leading, String string) {
+ super(leading, string);
+ }
+
+/**
+ * Constructs an Anchor
with a certain leading,
+ * a certain String
and a certain Font
.
+ *
+ * @param leading the leading
+ * @param string a String
+ * @param font a Font
+ */
+
+ public Anchor(float leading, String string, Font font) {
+ super(leading, string, font);
+ }
+
+/**
+ * Returns an Anchor
that has been constructed taking in account
+ * the value of some attributes.
+ *
+ * @param attributes Some attributes
+ */
+
+ public Anchor(Properties attributes) {
+ this("", FontFactory.getFont(attributes));
+ String value;
+ if ((value = (String)attributes.remove(ElementTags.ITEXT)) != null) {
+ Chunk chunk = new Chunk(value);
+ if ((value = (String)attributes.remove(ElementTags.GENERICTAG)) != null) {
+ chunk.setGenericTag(value);
+ }
+ add(chunk);
+ }
+ if ((value = (String)attributes.remove(ElementTags.LEADING)) != null) {
+ setLeading(Float.valueOf(value + "f").floatValue());
+ }
+ else if ((value = (String)attributes.remove(MarkupTags.CSS_KEY_LINEHEIGHT)) != null) {
+ setLeading(MarkupParser.parseLength(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.NAME)) != null) {
+ setName(value);
+ }
+ if ((value = (String)attributes.remove(ElementTags.REFERENCE)) != null) {
+ setReference(value);
+ }
+ if (attributes.size() > 0) setMarkupAttributes(attributes);
+ }
+
+ // implementation of the Element-methods
+
+/**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener an ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ Chunk chunk;
+ Iterator i = getChunks().iterator();
+ boolean localDestination = (reference != null && reference.startsWith("#"));
+ boolean notGotoOK = true;
+ while (i.hasNext()) {
+ chunk = (Chunk) i.next();
+ if (name != null && notGotoOK && !chunk.isEmpty()) {
+ chunk.setLocalDestination(name);
+ notGotoOK = false;
+ }
+ if (localDestination) {
+ chunk.setLocalGoto(reference.substring(1));
+ }
+ listener.add(chunk);
+ }
+ return true;
+ }
+ catch(DocumentException de) {
+ return false;
+ }
+ }
+
+/**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ ArrayList tmp = new ArrayList();
+ Chunk chunk;
+ Iterator i = iterator();
+ boolean localDestination = (reference != null && reference.startsWith("#"));
+ boolean notGotoOK = true;
+ while (i.hasNext()) {
+ chunk = (Chunk) i.next();
+ if (name != null && notGotoOK && !chunk.isEmpty()) {
+ chunk.setLocalDestination(name);
+ notGotoOK = false;
+ }
+ if (localDestination) {
+ chunk.setLocalGoto(reference.substring(1));
+ }
+ else if (reference != null)
+ chunk.setAnchor(reference);
+ tmp.add(chunk);
+ }
+ return tmp;
+ }
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.ANCHOR;
+ }
+
+ // methods
+
+/**
+ * Gets an iterator of Element
s.
+ *
+ * @return an Iterator
+ */
+
+ // suggestion by by Curt Thompson
+ public Iterator getElements() {
+ return this.iterator();
+ }
+
+/**
+ * Sets the name of this Anchor
.
+ *
+ * @param name a new name
+ */
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+/**
+ * Sets the reference of this Anchor
.
+ *
+ * @param reference a new reference
+ */
+
+ public void setReference(String reference) {
+ this.reference = reference;
+ }
+
+ // methods to retrieve information
+
+/**
+ * Returns the name of this Anchor
.
+ *
+ * @return a name
+ */
+
+ public String name() {
+ return name;
+ }
+
+/**
+ * Gets the reference of this Anchor
.
+ *
+ * @return a reference
+ */
+
+ public String reference() {
+ return reference;
+ }
+
+/**
+ * Gets the reference of this Anchor
.
+ *
+ * @return an URL
+ */
+
+ public URL url() {
+ try {
+ return new URL(reference);
+ }
+ catch(MalformedURLException mue) {
+ return null;
+ }
+ }
+
+/**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.ANCHOR.equals(tag);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/Annotation.java b/src/main/java/com/lowagie/text/Annotation.java
new file mode 100644
index 0000000..debc897
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Annotation.java
@@ -0,0 +1,737 @@
+/*
+ * $Id: Annotation.java,v 1.72 2005/11/01 12:27:05 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.net.URL;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * An Annotation
is a little note that can be added to a page on
+ * a document.
+ *
+ * @see Element
+ * @see Anchor
+ */
+
+public class Annotation implements Element, MarkupAttributes {
+
+ // membervariables
+
+ /** This is a possible annotation type. */
+ public static final int TEXT = 0;
+
+ /** This is a possible annotation type. */
+ public static final int URL_NET = 1;
+
+ /** This is a possible annotation type. */
+ public static final int URL_AS_STRING = 2;
+
+ /** This is a possible annotation type. */
+ public static final int FILE_DEST = 3;
+
+ /** This is a possible annotation type. */
+ public static final int FILE_PAGE = 4;
+
+ /** This is a possible annotation type. */
+ public static final int NAMED_DEST = 5;
+
+ /** This is a possible annotation type. */
+ public static final int LAUNCH = 6;
+
+ /** This is a possible annotation type. */
+ public static final int SCREEN = 7;
+
+ /** This is a possible attribute. */
+ public static String TITLE = "title";
+
+ /** This is a possible attribute. */
+ public static String CONTENT = "content";
+
+ /** This is a possible attribute. */
+ public static String URL = "url";
+
+ /** This is a possible attribute. */
+ public static String FILE = "file";
+
+ /** This is a possible attribute. */
+ public static String DESTINATION = "destination";
+
+ /** This is a possible attribute. */
+ public static String PAGE = "page";
+
+ /** This is a possible attribute. */
+ public static String NAMED = "named";
+
+ /** This is a possible attribute. */
+ public static String APPLICATION = "application";
+
+ /** This is a possible attribute. */
+ public static String PARAMETERS = "parameters";
+
+ /** This is a possible attribute. */
+ public static String OPERATION = "operation";
+
+ /** This is a possible attribute. */
+ public static String DEFAULTDIR = "defaultdir";
+
+ /** This is a possible attribute. */
+ public static String LLX = "llx";
+
+ /** This is a possible attribute. */
+ public static String LLY = "lly";
+
+ /** This is a possible attribute. */
+ public static String URX = "urx";
+
+ /** This is a possible attribute. */
+ public static String URY = "ury";
+
+ /** This is a possible attribute. */
+ public static String MIMETYPE = "mime";
+
+ /** This is the type of annotation. */
+ protected int annotationtype;
+
+ /** This is the title of the Annotation
. */
+ protected HashMap annotationAttributes = new HashMap();
+
+ /** Contains extra markupAttributes */
+ protected Properties markupAttributes = null;
+
+ /** This is the lower left x-value */
+ protected float llx = Float.NaN;
+
+ /** This is the lower left y-value */
+ protected float lly = Float.NaN;
+
+ /** This is the upper right x-value */
+ protected float urx = Float.NaN;
+
+ /** This is the upper right y-value */
+ protected float ury = Float.NaN;
+
+ // constructors
+
+ /**
+ * Constructs an Annotation
with a certain title and some
+ * text.
+ *
+ * @param llx
+ * lower left x coordinate
+ * @param lly
+ * lower left y coordinate
+ * @param urx
+ * upper right x coordinate
+ * @param ury
+ * upper right y coordinate
+ */
+
+ private Annotation(float llx, float lly, float urx, float ury) {
+ this.llx = llx;
+ this.lly = lly;
+ this.urx = urx;
+ this.ury = ury;
+ }
+
+ public Annotation(Annotation an) {
+ annotationtype = an.annotationtype;
+ annotationAttributes = an.annotationAttributes;
+ markupAttributes = an.markupAttributes;
+ llx = an.llx;
+ lly = an.lly;
+ urx = an.urx;
+ ury = an.ury;
+ }
+
+ /**
+ * Constructs an Annotation
with a certain title and some
+ * text.
+ *
+ * @param title
+ * the title of the annotation
+ * @param text
+ * the content of the annotation
+ */
+
+ public Annotation(String title, String text) {
+ annotationtype = TEXT;
+ annotationAttributes.put(TITLE, title);
+ annotationAttributes.put(CONTENT, text);
+ }
+
+ /**
+ * Constructs an Annotation
with a certain title and some
+ * text.
+ *
+ * @param title
+ * the title of the annotation
+ * @param text
+ * the content of the annotation
+ * @param llx
+ * the lower left x-value
+ * @param lly
+ * the lower left y-value
+ * @param urx
+ * the upper right x-value
+ * @param ury
+ * the upper right y-value
+ */
+
+ public Annotation(String title, String text, float llx, float lly,
+ float urx, float ury) {
+ this(llx, lly, urx, ury);
+ annotationtype = TEXT;
+ annotationAttributes.put(TITLE, title);
+ annotationAttributes.put(CONTENT, text);
+ }
+
+ /**
+ * Constructs an Annotation
.
+ *
+ * @param llx
+ * the lower left x-value
+ * @param lly
+ * the lower left y-value
+ * @param urx
+ * the upper right x-value
+ * @param ury
+ * the upper right y-value
+ * @param url
+ * the external reference
+ */
+
+ public Annotation(float llx, float lly, float urx, float ury, URL url) {
+ this(llx, lly, urx, ury);
+ annotationtype = URL_NET;
+ annotationAttributes.put(URL, url);
+ }
+
+ /**
+ * Constructs an Annotation
.
+ *
+ * @param llx
+ * the lower left x-value
+ * @param lly
+ * the lower left y-value
+ * @param urx
+ * the upper right x-value
+ * @param ury
+ * the upper right y-value
+ * @param url
+ * the external reference
+ */
+
+ public Annotation(float llx, float lly, float urx, float ury, String url) {
+ this(llx, lly, urx, ury);
+ annotationtype = URL_AS_STRING;
+ annotationAttributes.put(FILE, url);
+ }
+
+ /**
+ * Constructs an Annotation
.
+ *
+ * @param llx
+ * the lower left x-value
+ * @param lly
+ * the lower left y-value
+ * @param urx
+ * the upper right x-value
+ * @param ury
+ * the upper right y-value
+ * @param file
+ * an external PDF file
+ * @param dest
+ * the destination in this file
+ */
+
+ public Annotation(float llx, float lly, float urx, float ury, String file,
+ String dest) {
+ this(llx, lly, urx, ury);
+ annotationtype = FILE_DEST;
+ annotationAttributes.put(FILE, file);
+ annotationAttributes.put(DESTINATION, dest);
+ }
+
+ /**
+ * Creates a Screen anotation to embed media clips
+ *
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @param moviePath
+ * path to the media clip file
+ * @param mimeType
+ * mime type of the media
+ * @param showOnDisplay
+ * if true play on display of the page
+ */
+ public Annotation(float llx, float lly, float urx, float ury,
+ String moviePath, String mimeType, boolean showOnDisplay) {
+ this(llx, lly, urx, ury);
+ annotationtype = SCREEN;
+ annotationAttributes.put(FILE, moviePath);
+ annotationAttributes.put(MIMETYPE, mimeType);
+ annotationAttributes.put(PARAMETERS, new boolean[] {
+ false /* embedded */, showOnDisplay });
+ }
+
+ /**
+ * Constructs an Annotation
.
+ *
+ * @param llx
+ * the lower left x-value
+ * @param lly
+ * the lower left y-value
+ * @param urx
+ * the upper right x-value
+ * @param ury
+ * the upper right y-value
+ * @param file
+ * an external PDF file
+ * @param page
+ * a page number in this file
+ */
+
+ public Annotation(float llx, float lly, float urx, float ury, String file,
+ int page) {
+ this(llx, lly, urx, ury);
+ annotationtype = FILE_PAGE;
+ annotationAttributes.put(FILE, file);
+ annotationAttributes.put(PAGE, new Integer(page));
+ }
+
+ /**
+ * Constructs an Annotation
.
+ *
+ * @param llx
+ * the lower left x-value
+ * @param lly
+ * the lower left y-value
+ * @param urx
+ * the upper right x-value
+ * @param ury
+ * the upper right y-value
+ * @param named
+ * a named destination in this file
+ */
+
+ public Annotation(float llx, float lly, float urx, float ury, int named) {
+ this(llx, lly, urx, ury);
+ annotationtype = NAMED_DEST;
+ annotationAttributes.put(NAMED, new Integer(named));
+ }
+
+ /**
+ * Constructs an Annotation
.
+ *
+ * @param llx
+ * the lower left x-value
+ * @param lly
+ * the lower left y-value
+ * @param urx
+ * the upper right x-value
+ * @param ury
+ * the upper right y-value
+ * @param application
+ * an external application
+ * @param parameters
+ * parameters to pass to this application
+ * @param operation
+ * the operation to pass to this application
+ * @param defaultdir
+ * the default directory to run this application in
+ */
+
+ public Annotation(float llx, float lly, float urx, float ury,
+ String application, String parameters, String operation,
+ String defaultdir) {
+ this(llx, lly, urx, ury);
+ annotationtype = LAUNCH;
+ annotationAttributes.put(APPLICATION, application);
+ annotationAttributes.put(PARAMETERS, parameters);
+ annotationAttributes.put(OPERATION, operation);
+ annotationAttributes.put(DEFAULTDIR, defaultdir);
+ }
+
+ /**
+ * Returns an Annotation
that has been constructed taking in
+ * account the value of some attributes .
+ *
+ * @param attributes
+ * Some attributes
+ */
+
+ public Annotation(Properties attributes) {
+ String value = (String) attributes.remove(ElementTags.LLX);
+ if (value != null) {
+ llx = Float.valueOf(value + "f").floatValue();
+ }
+ value = (String) attributes.remove(ElementTags.LLY);
+ if (value != null) {
+ lly = Float.valueOf(value + "f").floatValue();
+ }
+ value = (String) attributes.remove(ElementTags.URX);
+ if (value != null) {
+ urx = Float.valueOf(value + "f").floatValue();
+ }
+ value = (String) attributes.remove(ElementTags.URY);
+ if (value != null) {
+ ury = Float.valueOf(value + "f").floatValue();
+ }
+ String title = (String) attributes.remove(ElementTags.TITLE);
+ String text = (String) attributes.remove(ElementTags.CONTENT);
+ if (title != null || text != null) {
+ annotationtype = TEXT;
+ } else if ((value = (String) attributes.remove(ElementTags.URL)) != null) {
+ annotationtype = URL_AS_STRING;
+ annotationAttributes.put(FILE, value);
+ } else if ((value = (String) attributes.remove(ElementTags.NAMED)) != null) {
+ annotationtype = NAMED_DEST;
+ annotationAttributes.put(NAMED, Integer.valueOf(value));
+ } else {
+ String file = (String) attributes.remove(ElementTags.FILE);
+ String destination = (String) attributes
+ .remove(ElementTags.DESTINATION);
+ String page = (String) attributes.remove(ElementTags.PAGE);
+ if (file != null) {
+ annotationAttributes.put(FILE, file);
+ if (destination != null) {
+ annotationtype = FILE_DEST;
+ annotationAttributes.put(DESTINATION, destination);
+ } else if (page != null) {
+ annotationtype = FILE_PAGE;
+ annotationAttributes.put(FILE, file);
+ annotationAttributes.put(PAGE, Integer.valueOf(page));
+ }
+ } else if ((value = (String) attributes.remove(ElementTags.NAMED)) != null) {
+ annotationtype = LAUNCH;
+ annotationAttributes.put(APPLICATION, value);
+ annotationAttributes.put(PARAMETERS, attributes
+ .remove(ElementTags.PARAMETERS));
+ annotationAttributes.put(OPERATION, attributes
+ .remove(ElementTags.OPERATION));
+ annotationAttributes.put(DEFAULTDIR, attributes
+ .remove(ElementTags.DEFAULTDIR));
+ }
+ }
+ if (annotationtype == TEXT) {
+ if (title == null)
+ title = "";
+ if (text == null)
+ text = "";
+ annotationAttributes.put(TITLE, title);
+ annotationAttributes.put(CONTENT, text);
+ }
+ if (attributes.size() > 0)
+ setMarkupAttributes(attributes);
+ }
+
+ // implementation of the Element-methods
+
+ /**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.ANNOTATION;
+ }
+
+ // methods
+
+ /**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener
+ * an ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ return listener.add(this);
+ } catch (DocumentException de) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ return new ArrayList();
+ }
+
+ // methods
+
+ /**
+ * Sets the dimensions of this annotation.
+ *
+ * @param llx
+ * the lower left x-value
+ * @param lly
+ * the lower left y-value
+ * @param urx
+ * the upper right x-value
+ * @param ury
+ * the upper right y-value
+ */
+
+ public void setDimensions(float llx, float lly, float urx, float ury) {
+ this.llx = llx;
+ this.lly = lly;
+ this.urx = urx;
+ this.ury = ury;
+ }
+
+ // methods to retrieve information
+
+ /**
+ * Returns the lower left x-value.
+ *
+ * @return a value
+ */
+
+ public float llx() {
+ return llx;
+ }
+
+ /**
+ * Returns the lower left y-value.
+ *
+ * @return a value
+ */
+
+ public float lly() {
+ return lly;
+ }
+
+ /**
+ * Returns the uppper right x-value.
+ *
+ * @return a value
+ */
+
+ public float urx() {
+ return urx;
+ }
+
+ /**
+ * Returns the uppper right y-value.
+ *
+ * @return a value
+ */
+
+ public float ury() {
+ return ury;
+ }
+
+ /**
+ * Returns the lower left x-value.
+ *
+ * @param def
+ * the default value
+ * @return a value
+ */
+
+ public float llx(float def) {
+ if (Float.isNaN(llx))
+ return def;
+ return llx;
+ }
+
+ /**
+ * Returns the lower left y-value.
+ *
+ * @param def
+ * the default value
+ * @return a value
+ */
+
+ public float lly(float def) {
+ if (Float.isNaN(lly))
+ return def;
+ return lly;
+ }
+
+ /**
+ * Returns the upper right x-value.
+ *
+ * @param def
+ * the default value
+ * @return a value
+ */
+
+ public float urx(float def) {
+ if (Float.isNaN(urx))
+ return def;
+ return urx;
+ }
+
+ /**
+ * Returns the upper right y-value.
+ *
+ * @param def
+ * the default value
+ * @return a value
+ */
+
+ public float ury(float def) {
+ if (Float.isNaN(ury))
+ return def;
+ return ury;
+ }
+
+ /**
+ * Returns the type of this Annotation
.
+ *
+ * @return a type
+ */
+
+ public int annotationType() {
+ return annotationtype;
+ }
+
+ /**
+ * Returns the title of this Annotation
.
+ *
+ * @return a name
+ */
+
+ public String title() {
+ String s = (String) annotationAttributes.get(TITLE);
+ if (s == null)
+ s = "";
+ return s;
+ }
+
+ /**
+ * Gets the content of this Annotation
.
+ *
+ * @return a reference
+ */
+
+ public String content() {
+ String s = (String) annotationAttributes.get(CONTENT);
+ if (s == null)
+ s = "";
+ return s;
+ }
+
+ /**
+ * Gets the content of this Annotation
.
+ *
+ * @return a reference
+ */
+
+ public HashMap attributes() {
+ return annotationAttributes;
+ }
+
+ /**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag
+ * the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.ANNOTATION.equals(tag);
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttribute(java.lang.String,
+ * java.lang.String)
+ */
+ public void setMarkupAttribute(String name, String value) {
+ if (markupAttributes == null) markupAttributes = new Properties();
+ markupAttributes.put(name, value);
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttributes(java.util.Properties)
+ */
+ public void setMarkupAttributes(Properties markupAttributes) {
+ this.markupAttributes = markupAttributes;
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttribute(java.lang.String)
+ */
+ public String getMarkupAttribute(String name) {
+ return (markupAttributes == null) ? null : String
+ .valueOf(markupAttributes.get(name));
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributeNames()
+ */
+ public Set getMarkupAttributeNames() {
+ return Chunk.getKeySet(markupAttributes);
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributes()
+ */
+ public Properties getMarkupAttributes() {
+ return markupAttributes;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/BadElementException.java b/src/main/java/com/lowagie/text/BadElementException.java
new file mode 100644
index 0000000..7c7b459
--- /dev/null
+++ b/src/main/java/com/lowagie/text/BadElementException.java
@@ -0,0 +1,89 @@
+/*
+ * $Id: BadElementException.java,v 1.50 2004/12/14 11:33:49 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+/**
+ * Signals an attempt to create an Element
that hasn't got the right form.
+ *
+ * @see DocumentException
+ * @see Cell
+ * @see Table
+ */
+
+public class BadElementException extends DocumentException {
+
+ // constructors
+ /**
+ * Constructs a BadElementException
+ * @param ex an Exception object that has to be turned into a BadElementException
+ */
+ public BadElementException(Exception ex) {
+ super(ex);
+ }
+
+/**
+ * Constructs a BadElementException
whithout a message.
+ */
+
+ BadElementException() {
+ super();
+ }
+
+/**
+ * Constructs a BadElementException
with a message.
+ *
+ * @param message a message describing the exception
+ */
+
+ public BadElementException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/Cell.java b/src/main/java/com/lowagie/text/Cell.java
new file mode 100644
index 0000000..32d6b70
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Cell.java
@@ -0,0 +1,1013 @@
+/*
+ * $Id: Cell.java,v 1.106 2005/11/12 18:00:57 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Properties;
+
+import com.lowagie.text.markup.MarkupParser;
+import com.lowagie.text.pdf.PdfPCell;
+
+/**
+ * A Cell
is a Rectangle
containing other
+ * Element
s.
+ * Cell
must be added to a Table
.
+ * The Table
will place the Cell
in
+ * a Row
.
+ *
+ *
+ * @see Rectangle
+ * @see Element
+ * @see Table
+ * @see Row
+ */
+
+public class Cell extends Rectangle implements TextElementArray {
+
+ // static final membervariable
+
+ // This accessor replaces the dangerous static member DUMMY_CELL
+ /**
+ * Get dummy cell used when merging inner tables.
+ * @return a cell with colspan 3 and no border
+ */
+ public static Cell getDummyCell() {
+ Cell cell = new Cell(true);
+ cell.setColspan(3);
+ cell.setBorder(NO_BORDER);
+ return cell;
+ }
+
+ // membervariables
+
+/** This is the
+ * Table table = new Table(3);
+ * table.setBorderWidth(1);
+ * table.setBorderColor(new Color(0, 0, 255));
+ * table.setCellpadding(5);
+ * table.setCellspacing(5);
+ * Cell cell = new Cell("header");
+ * cell.setHeader(true);
+ * cell.setColspan(3);
+ * table.addCell(cell);
+ * cell = new Cell("example cell with colspan 1 and rowspan 2");
+ * cell.setRowspan(2);
+ * cell.setBorderColor(new Color(255, 0, 0));
+ * table.addCell(cell);
+ * table.addCell("1.1");
+ * table.addCell("2.1");
+ * table.addCell("1.2");
+ * table.addCell("2.2");
+ *
ArrayList
of Element
s. */
+ protected ArrayList arrayList = null;
+
+/** This is the horizontal alignment. */
+ protected int horizontalAlignment = Element.ALIGN_UNDEFINED;
+
+/** This is the vertical alignment. */
+ protected int verticalAlignment = Element.ALIGN_UNDEFINED;
+
+/** This is the vertical alignment. */
+ protected String width;
+
+/** This is the colspan. */
+ protected int colspan = 1;
+
+/** This is the rowspan. */
+ protected int rowspan = 1;
+
+/** This is the leading. */
+ float leading = Float.NaN;
+
+/** Is this Cell
a header? */
+ protected boolean header;
+
+ /** Indicates that the largest ascender height should be used to determine the
+ * height of the first line. Note that this only has an effect when rendered
+ * to PDF. Setting this to true can help with vertical alignment problems. */
+ protected boolean useAscender = false;
+
+ /** Indicates that the largest descender height should be added to the height of
+ * the last line (so characters like y don't dip into the border). Note that
+ * this only has an effect when rendered to PDF. */
+ protected boolean useDescender = false;
+
+ /**
+ * Adjusts the cell contents to compensate for border widths. Note that
+ * this only has an effect when rendered to PDF.
+ */
+ protected boolean useBorderPadding;
+
+ // constructors
+
+/**
+ * Constructs an empty Cell
.
+ */
+
+ public Cell() {
+ // creates a Rectangle with BY DEFAULT a border of 0.5
+ super(0, 0, 0, 0);
+ setBorder(UNDEFINED);
+ setBorderWidth(0.5f);
+
+ // initializes the arraylist and adds an element
+ arrayList = new ArrayList();
+ }
+
+/**
+ * Constructs an empty Cell
(for internal use only).
+ *
+ * @param dummy a dummy value
+ */
+
+ public Cell(boolean dummy) {
+ this();
+ arrayList.add(new Paragraph(0));
+ }
+
+/**
+ * Constructs a Cell
with a certain content.
+ * String
will be converted into a Paragraph
.
+ *
+ * @param content a String
+ */
+
+ public Cell(String content) {
+ // creates a Rectangle with BY DEFAULT a border of 0.5
+ super(0, 0, 0, 0);
+ setBorder(UNDEFINED);
+ setBorderWidth(0.5f);
+
+ // initializes the arraylist and adds an element
+ arrayList = new ArrayList();
+ try {
+ addElement(new Paragraph(content));
+ }
+ catch(BadElementException bee) {
+ }
+ }
+
+/**
+ * Constructs a Cell
with a certain Element
.
+ * ListItem
, Row
or
+ * Cell
, an exception will be thrown.
+ *
+ * @param element the element
+ * @throws BadElementException when the creator was called with a ListItem
, Row
or Cell
+ */
+
+ public Cell(Element element) throws BadElementException {
+ // creates a Rectangle with BY DEFAULT a border of 0.5
+ super(0, 0, 0, 0);
+ setBorder(UNDEFINED);
+ setBorderWidth(0.5f);
+
+ // Update by Benoit WIART Cell
that has been constructed taking in account
+ * the value of some attributes.
+ *
+ * @param attributes Some attributes
+ */
+
+ public Cell(Properties attributes) {
+ this();
+ String value;
+ if ((value = (String)attributes.remove(ElementTags.HORIZONTALALIGN)) != null) {
+ setHorizontalAlignment(value);
+ }
+ if ((value = (String)attributes.remove(ElementTags.VERTICALALIGN)) != null) {
+ setVerticalAlignment(value);
+ }
+ if ((value = (String)attributes.remove(ElementTags.WIDTH)) != null) {
+ setWidth(value);
+ }
+ if ((value = (String)attributes.remove(ElementTags.COLSPAN)) != null) {
+ setColspan(Integer.parseInt(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.ROWSPAN)) != null) {
+ setRowspan(Integer.parseInt(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.LEADING)) != null) {
+ setLeading(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.HEADER)) != null) {
+ setHeader(new Boolean(value).booleanValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.NOWRAP)) != null) {
+ setNoWrap(new Boolean(value).booleanValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.BORDERWIDTH)) != null) {
+ setBorderWidth(Float.valueOf(value + "f").floatValue());
+ }
+ int border = 0;
+ if ((value = (String)attributes.remove(ElementTags.LEFT)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.LEFT;
+ }
+ if ((value = (String)attributes.remove(ElementTags.RIGHT)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.RIGHT;
+ }
+ if ((value = (String)attributes.remove(ElementTags.TOP)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.TOP;
+ }
+ if ((value = (String)attributes.remove(ElementTags.BOTTOM)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.BOTTOM;
+ }
+ setBorder(border);
+ String r = (String)attributes.remove(ElementTags.RED);
+ String g = (String)attributes.remove(ElementTags.GREEN);
+ String b = (String)attributes.remove(ElementTags.BLUE);
+ if (r != null || g != null || b != null) {
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+ if (r != null) red = Integer.parseInt(r);
+ if (g != null) green = Integer.parseInt(g);
+ if (b != null) blue = Integer.parseInt(b);
+ setBorderColor(new Color(red, green, blue));
+ }
+ else if ((value = (String)attributes.remove(ElementTags.BORDERCOLOR)) != null) {
+ setBorderColor(MarkupParser.decodeColor(value));
+ }
+ r = (String)attributes.remove(ElementTags.BGRED);
+ g = (String)attributes.remove(ElementTags.BGGREEN);
+ b = (String)attributes.remove(ElementTags.BGBLUE);
+ if (r != null || g != null || b != null) {
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+ if (r != null) red = Integer.parseInt(r);
+ if (g != null) green = Integer.parseInt(g);
+ if (b != null) blue = Integer.parseInt(b);
+ setBackgroundColor(new Color(red, green, blue));
+ }
+ else if ((value = (String)attributes.remove(ElementTags.BACKGROUNDCOLOR)) != null) {
+ setBackgroundColor(MarkupParser.decodeColor(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.GRAYFILL)) != null) {
+ setGrayFill(Float.valueOf(value + "f").floatValue());
+ }
+ if (attributes.size() > 0) setMarkupAttributes(attributes);
+ }
+
+ // implementation of the Element-methods
+
+/**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener an ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ return listener.add(this);
+ }
+ catch(DocumentException de) {
+ return false;
+ }
+ }
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.CELL;
+ }
+
+/**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ ArrayList tmp = new ArrayList();
+ for (Iterator i = arrayList.iterator(); i.hasNext(); ) {
+ tmp.addAll(((Element) i.next()).getChunks());
+ }
+ return tmp;
+ }
+
+ // methods to set the membervariables
+
+/**
+ * Adds an element to this Cell
.
+ * ListItem
s, Row
s, Cell
s,
+ * JPEG
s, GIF
s or PNG
s to a Cell
.
+ *
+ * @param element The Element
to add
+ * @throws BadElementException if the method was called with a ListItem
, Row
or Cell
+ */
+
+ public void addElement(Element element) throws BadElementException {
+ if (isTable()) {
+ Table table = (Table) arrayList.get(0);
+ Cell tmp = new Cell(element);
+ tmp.setBorder(NO_BORDER);
+ tmp.setColspan(table.columns());
+ table.addCell(tmp);
+ return;
+ }
+ switch(element.type()) {
+ case Element.LISTITEM:
+ case Element.ROW:
+ case Element.CELL:
+ throw new BadElementException("You can't add listitems, rows or cells to a cell.");
+ case Element.LIST:
+ if (Float.isNaN(leading)) {
+ leading = ((List) element).leading();
+ }
+ if (((List) element).size() == 0) return;
+ arrayList.add(element);
+ return;
+ case Element.ANCHOR:
+ case Element.PARAGRAPH:
+ case Element.PHRASE:
+ if (Float.isNaN(leading)) {
+ leading = ((Phrase) element).leading();
+ }
+ if (((Phrase) element).isEmpty()) return;
+ arrayList.add(element);
+ return;
+ case Element.CHUNK:
+ if (((Chunk) element).isEmpty()) return;
+ arrayList.add(element);
+ return;
+ case Element.TABLE:
+ Table table = new Table(3);
+ float[] widths = new float[3];
+ widths[1] = ((Table)element).widthPercentage();
+
+ switch(((Table)element).alignment()) {
+ case Element.ALIGN_LEFT:
+ widths[0] = 0f;
+ widths[2] = 100f - widths[1];
+ break;
+ case Element.ALIGN_CENTER:
+ widths[0] = (100f - widths[1]) / 2f;
+ widths[2] = widths[0];
+ break;
+ case Element.ALIGN_RIGHT:
+ widths[0] = 100f - widths[1];
+ widths[2] = 0f;
+ }
+ table.setWidths(widths);
+ Cell tmp;
+ if (arrayList.size() == 0) {
+ table.addCell(getDummyCell());
+ }
+ else {
+ tmp = new Cell();
+ tmp.setBorder(NO_BORDER);
+ tmp.setColspan(3);
+ for (Iterator i = arrayList.iterator(); i.hasNext(); ) {
+ tmp.add((Element) i.next());
+ }
+ table.addCell(tmp);
+ }
+ tmp = new Cell();
+ tmp.setBorder(NO_BORDER);
+ table.addCell(tmp);
+ table.insertTable((Table)element);
+ table.addCell(tmp);
+ table.addCell(getDummyCell());
+ clear();
+ arrayList.add(table);
+ return;
+ default:
+ arrayList.add(element);
+ }
+ }
+
+/**
+ * Add an Object
to this cell.
+ *
+ * @param o the object to add
+ * @return always true
+ */
+
+ public boolean add(Object o) {
+ try {
+ this.addElement((Element) o);
+ return true;
+ }
+ catch(ClassCastException cce) {
+ throw new ClassCastException("You can only add objects that implement the Element interface.");
+ }
+ catch(BadElementException bee) {
+ throw new ClassCastException(bee.getMessage());
+ }
+ }
+
+/**
+ * Sets the leading.
+ *
+ * @param value the new value
+ */
+
+ public void setLeading(float value) {
+ leading = value;
+ }
+
+/**
+ * Sets the horizontal alignment.
+ *
+ * @param value the new value
+ */
+
+ public void setHorizontalAlignment(int value) {
+ horizontalAlignment = value;
+ }
+
+/**
+ * Sets the alignment of this cell.
+ *
+ * @param alignment the new alignment as a String
+ */
+
+ public void setHorizontalAlignment(String alignment) {
+ if (ElementTags.ALIGN_CENTER.equalsIgnoreCase(alignment)) {
+ this.horizontalAlignment = Element.ALIGN_CENTER;
+ return;
+ }
+ if (ElementTags.ALIGN_RIGHT.equalsIgnoreCase(alignment)) {
+ this.horizontalAlignment = Element.ALIGN_RIGHT;
+ return;
+ }
+ if (ElementTags.ALIGN_JUSTIFIED.equalsIgnoreCase(alignment)) {
+ this.horizontalAlignment = Element.ALIGN_JUSTIFIED;
+ return;
+ }
+ if (ElementTags.ALIGN_JUSTIFIED_ALL.equalsIgnoreCase(alignment)) {
+ this.horizontalAlignment = Element.ALIGN_JUSTIFIED_ALL;
+ return;
+ }
+ this.horizontalAlignment = Element.ALIGN_LEFT;
+ }
+
+/**
+ * Sets the vertical alignment.
+ *
+ * @param value the new value
+ */
+
+ public void setVerticalAlignment(int value) {
+ verticalAlignment = value;
+ }
+
+/**
+ * Sets the alignment of this paragraph.
+ *
+ * @param alignment the new alignment as a String
+ */
+
+ public void setVerticalAlignment(String alignment) {
+ if (ElementTags.ALIGN_MIDDLE.equalsIgnoreCase(alignment)) {
+ this.verticalAlignment = Element.ALIGN_MIDDLE;
+ return;
+ }
+ if (ElementTags.ALIGN_BOTTOM.equalsIgnoreCase(alignment)) {
+ this.verticalAlignment = Element.ALIGN_BOTTOM;
+ return;
+ }
+ if (ElementTags.ALIGN_BASELINE.equalsIgnoreCase(alignment)) {
+ this.verticalAlignment = Element.ALIGN_BASELINE;
+ return;
+ }
+ this.verticalAlignment = Element.ALIGN_TOP;
+ }
+
+/**
+ * Sets the width.
+ *
+ * @param value the new value
+ */
+
+ public void setWidth(String value) {
+ width = value;
+ }
+
+/**
+ * Sets the colspan.
+ *
+ * @param value the new value
+ */
+
+ public void setColspan(int value) {
+ colspan = value;
+ }
+
+/**
+ * Sets the rowspan.
+ *
+ * @param value the new value
+ */
+
+ public void setRowspan(int value) {
+ rowspan = value;
+ }
+
+/**
+ * Sets header.
+ *
+ * @param value the new value
+ */
+
+ public void setHeader(boolean value) {
+ header = value;
+ }
+
+/**
+ * Set nowrap.
+ *
+ * @param value the new value
+ */
+
+ public void setNoWrap(boolean value) {
+ maxLines = 1;
+ }
+
+ // methods to retrieve information
+
+/**
+ * Gets the number of Element
s in the Cell.
+ *
+ * @return a size
.
+ */
+
+ public int size() {
+ return arrayList.size();
+ }
+
+/**
+ * Checks if the Cell
is empty.
+ *
+ * @return false
if there are non-empty Element
s in the Cell
.
+ */
+
+ public boolean isEmpty() {
+ switch(size()) {
+ case 0:
+ return true;
+ case 1:
+ Element element = (Element) arrayList.get(0);
+ switch (element.type()) {
+ case Element.CHUNK:
+ return ((Chunk) element).isEmpty();
+ case Element.ANCHOR:
+ case Element.PHRASE:
+ case Element.PARAGRAPH:
+ return ((Phrase) element).isEmpty();
+ case Element.LIST:
+ return ((List) element).size() == 0;
+ }
+ return false;
+ default:
+ return false;
+ }
+ }
+
+/**
+ * Makes sure there is at least 1 object in the Cell.
+ *
+ * Otherwise it might not be shown in the table.
+ */
+
+ void fill() {
+ if (size() == 0) arrayList.add(new Paragraph(0));
+ }
+
+/**
+ * Checks if the Cell
is empty.
+ *
+ * @return false
if there are non-empty Element
s in the Cell
.
+ */
+
+ public boolean isTable() {
+ return (size() == 1) && (((Element)arrayList.get(0)).type() == Element.TABLE);
+ }
+
+/**
+ * Gets an iterator of Element
s.
+ *
+ * @return an Iterator
.
+ */
+
+ public Iterator getElements() {
+ return arrayList.iterator();
+ }
+
+/**
+ * Gets the horizontal alignment.
+ *
+ * @return a value
+ */
+
+ public int horizontalAlignment() {
+ return horizontalAlignment;
+ }
+
+/**
+ * Gets the vertical alignment.
+ *
+ * @return a value
+ */
+
+ public int verticalAlignment() {
+ return verticalAlignment;
+ }
+
+/**
+ * Gets the width.
+ *
+ * @return a value
+ */
+
+ public String cellWidth() {
+ return width;
+ }
+
+/**
+ * Gets the colspan.
+ *
+ * @return a value
+ */
+
+ public int colspan() {
+ return colspan;
+ }
+
+/**
+ * Gets the rowspan.
+ *
+ * @return a value
+ */
+
+ public int rowspan() {
+ return rowspan;
+ }
+
+/**
+ * Gets the leading.
+ *
+ * @return a value
+ */
+
+ public float leading() {
+ if (Float.isNaN(leading)) {
+ return 16;
+ }
+ return leading;
+ }
+
+/**
+ * Is this Cell
a header?
+ *
+ * @return a value
+ */
+
+ public boolean header() {
+ return header;
+ }
+
+/**
+ * Get nowrap.
+ *
+ * @return a value
+ */
+
+ public boolean noWrap() {
+ return maxLines == 1;
+ }
+
+/**
+ * Clears all the Element
s of this Cell
.
+ */
+ public void clear() {
+ arrayList.clear();
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @return NA
+ */
+ public float top() {
+ throw new UnsupportedOperationException("Dimensions of a Cell can't be calculated. See the FAQ.");
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @return NA
+ */
+ public float bottom() {
+ throw new UnsupportedOperationException("Dimensions of a Cell can't be calculated. See the FAQ.");
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @return NA
+ */
+ public float left() {
+ throw new UnsupportedOperationException("Dimensions of a Cell can't be calculated. See the FAQ.");
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @return NA
+ */
+ public float right() {
+ throw new UnsupportedOperationException("Dimensions of a Cell can't be calculated. See the FAQ.");
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @param margin
+ * @return NA
+ */
+ public float top(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Cell can't be calculated. See the FAQ.");
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @param margin
+ * @return NA
+ */
+ public float bottom(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Cell can't be calculated. See the FAQ.");
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @param margin
+ * @return NA
+ */
+ public float left(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Cell can't be calculated. See the FAQ.");
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @param margin NA
+ * @return NA
+ */
+ public float right(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Cell can't be calculated. See the FAQ.");
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @param value NA
+ */
+ public void setTop(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Cell are attributed automagically. See the FAQ.");
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @param value NA
+ */
+ public void setBottom(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Cell are attributed automagically. See the FAQ.");
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @param value NA
+ */
+ public void setLeft(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Cell are attributed automagically. See the FAQ.");
+ }
+
+/**
+ * This method throws an UnsupportedOperationException
.
+ * @param value NA
+ */
+ public void setRight(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Cell are attributed automagically. See the FAQ.");
+ }
+
+/**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.CELL.equals(tag);
+ }
+
+/** Does this Cell
force a group change? */
+ protected boolean groupChange = true;
+
+/**
+ * Does this Cell
force a group change?
+ *
+ * @return a value
+ */
+
+ public boolean getGroupChange() {
+ return groupChange;
+ }
+
+/**
+ * Sets group change.
+ *
+ * @param value the new value
+ */
+
+ public void setGroupChange(boolean value) {
+ groupChange = value;
+ }
+
+ /**
+ * Getter for {@link #maxLines}
+ * @return the maxLines value
+ */
+ public int getMaxLines() {
+ return maxLines;
+ }
+ /**
+ * Setter for {@link #maxLines}
+ * @param value the maximum number of lines
+ */
+ public void setMaxLines(int value) {
+ maxLines = value;
+ }
+ /**
+ * Maximum number of lines allowed in the cell.
+ * The default value of this property is not to limit the maximum number of lines
+ * (contributed by dperezcar@fcc.es)
+ */
+ protected int maxLines = Integer.MAX_VALUE;
+ /**Setter for {@link #showTruncation}
+ * @param value Can be null for avoiding marking the truncation.*/
+ public void setShowTruncation(String value) {
+ showTruncation = value;
+ }
+ /**
+ * Getter for {@link #showTruncation}
+ * @return the showTruncation value
+ */
+ public String getShowTruncation() {
+ return showTruncation;
+ }
+ /**
+ * If a truncation happens due to the {@link #maxLines} property, then this text will
+ * be added to indicate a truncation has happened.
+ * Default value is null, and means avoiding marking the truncation.
+ * A useful value of this property could be e.g. "..."
+ * (contributed by dperezcar@fcc.es)
+ */
+ String showTruncation;
+
+
+ /**
+ * Sets the value of {@link #useAscender}.
+ * @param use use ascender height if true
+ */
+ public void setUseAscender(boolean use) {
+ useAscender = use;
+ }
+
+ /**
+ * Gets the value of {@link #useAscender}
+ * @return useAscender
+ */
+ public boolean isUseAscender() {
+ return useAscender;
+ }
+
+ /**
+ * Sets the value of {@link #useDescender}.
+ * @param use use descender height if true
+ */
+ public void setUseDescender(boolean use) {
+ useDescender = use;
+ }
+
+ /**
+ * gets the value of {@link #useDescender }
+ * @return useDescender
+ */
+ public boolean isUseDescender() {
+ return useDescender;
+ }
+
+ /**
+ * Sets the value of {@link #useBorderPadding}.
+ * @param use adjust layour for borders if true
+ */
+ public void setUseBorderPadding(boolean use) {
+ useBorderPadding = use;
+ }
+
+ /**
+ * Gets the value of {@link #useBorderPadding}.
+ * @return useBorderPadding
+ */
+ public boolean isUseBorderPadding() {
+ return useBorderPadding;
+ }
+
+ /**
+ * Creates a PdfPCell based on this Cell object.
+ * @return a PdfPCell
+ * @throws BadElementException
+ */
+ public PdfPCell createPdfPCell() throws BadElementException {
+ if (rowspan > 1) throw new BadElementException("PdfPCells can't have a rowspan > 1");
+ if (isTable()) return new PdfPCell(((Table)arrayList.get(0)).createPdfPTable());
+ PdfPCell cell = new PdfPCell();
+ cell.setVerticalAlignment(verticalAlignment);
+ cell.setHorizontalAlignment(horizontalAlignment);
+ cell.setColspan(colspan);
+ cell.setUseBorderPadding(useBorderPadding);
+ cell.setUseDescender(useDescender);
+ cell.setLeading(leading(), 0);
+ cell.cloneNonPositionParameters(this);
+ cell.setNoWrap(noWrap());
+ for (Iterator i = getElements(); i.hasNext(); ) {
+ Element e = (Element)i.next();
+ if (e.type() == Element.PHRASE || e.type() == Element.PARAGRAPH) {
+ Paragraph p = new Paragraph((Phrase)e);
+ p.setAlignment(horizontalAlignment);
+ e = p;
+ }
+ cell.addElement(e);
+ }
+ return cell;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/Chapter.java b/src/main/java/com/lowagie/text/Chapter.java
new file mode 100644
index 0000000..19006b7
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Chapter.java
@@ -0,0 +1,156 @@
+/*
+ * $Id: Chapter.java,v 1.69 2005/04/13 09:51:14 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ *
+ */
+
+package com.lowagie.text;
+
+import java.util.ArrayList;
+import java.util.Properties;
+
+/**
+ * A Chapter
is a special Section
.
+ * Paragraph
as title
+ * and an int
as chapter number. The chapter number is shown be
+ * default. If you don't want to see the chapter number, you have to set the
+ * numberdepth to 0.
+ *
+ */
+
+public class Chapter extends Section implements TextElementArray {
+
+ // constructors
+
+ /**
+ * Constructs a new
+ * Paragraph title2 = new Paragraph("This is Chapter 2", FontFactory.getFont(FontFactory.HELVETICA, 18, Font.BOLDITALIC, new Color(0, 0, 255)));
+ * Chapter chapter2 = new Chapter(title2, 2);
+ * chapter2.setNumberDepth(0);
+ * Paragraph someText = new Paragraph("This is some text");
+ * chapter2.add(someText);
+ * Paragraph title21 = new Paragraph("This is Section 1 in Chapter 2", FontFactory.getFont(FontFactory.HELVETICA, 16, Font.BOLD, new Color(255, 0, 0)));
+ * Section section1 = chapter2.addSection(title21);
+ * Paragraph someSectionText = new Paragraph("This is some silly paragraph in a chapter and/or section. It contains some text to test the functionality of Chapters and Section.");
+ * section1.add(someSectionText);
+ *
Chapter
.
+ *
+ * @param title the Chapter title (as a Paragraph
)
+ * @param number the Chapter number
+ */
+
+ public Chapter(Paragraph title, int number) {
+ super(title, 1);
+ numbers = new ArrayList();
+ numbers.add(new Integer(number));
+ }
+
+/**
+ * Constructs a new Chapter
.
+ *
+ * @param title the Chapter title (as a String
)
+ * @param number the Chapter number
+ */
+
+ public Chapter(String title, int number) {
+ this(new Paragraph(title), number);
+ }
+
+/**
+ * Creates a new Chapter
following a set of attributes.
+ *
+ * @param attributes the attributes
+ * @param number a userdefined Chapter number
+ */
+
+ public Chapter(Properties attributes, int number) {
+ this(new Paragraph(""), number);
+
+ String value;
+ if ((value = (String)attributes.remove(ElementTags.NUMBERDEPTH)) != null) {
+ setNumberDepth(Integer.parseInt(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENT)) != null) {
+ setIndentation(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENTATIONLEFT)) != null) {
+ setIndentationLeft(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENTATIONRIGHT)) != null) {
+ setIndentationRight(Float.valueOf(value + "f").floatValue());
+ }
+ }
+
+ // implementation of the Element-methods
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.CHAPTER;
+ }
+
+ // methods
+
+/**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.CHAPTER.equals(tag);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/ChapterAutoNumber.java b/src/main/java/com/lowagie/text/ChapterAutoNumber.java
new file mode 100644
index 0000000..efb83ba
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ChapterAutoNumber.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2005 by Michael Niedermair.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import com.lowagie.text.Chapter;
+import com.lowagie.text.Paragraph;
+
+/**
+ * Chapter with auto numbering.
+ *
+ * @author Michael Niedermair
+ */
+public class ChapterAutoNumber extends Chapter {
+
+ /**
+ * the chapter number (for all instance)
+ */
+ private static int chapternumber = 0;
+
+ /**
+ * Create a new object.
+ *
+ * @param para the Chapter title (as a Paragraph
)
+ */
+ public ChapterAutoNumber(final Paragraph para) {
+ super(para, ++chapternumber);
+ }
+
+ /**
+ * Create a new objet.
+ *
+ * @param title the Chapter title (as a String
)
+ */
+ public ChapterAutoNumber(final String title) {
+ super(title, ++chapternumber);
+ }
+
+ /**
+ * Create a new section for this chapter and ad it.
+ *
+ * @param title the Section title (as a String
)
+ * @return Returns the new section.
+ */
+ public Section addSection(final String title) {
+ return addSection(title, 2);
+ }
+
+ /**
+ * Create a new section for this chapter and add it.
+ *
+ * @param title the Section title (as a Paragraph
)
+ * @return Returns the new section.
+ */
+ public Section addSection(final Paragraph title) {
+ return addSection(title, 2);
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/Chunk.java b/src/main/java/com/lowagie/text/Chunk.java
new file mode 100644
index 0000000..c72e54c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Chunk.java
@@ -0,0 +1,937 @@
+/*
+ * $Id: Chunk.java,v 1.114 2005/12/08 14:56:53 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Set;
+import java.net.URL;
+
+import com.lowagie.text.pdf.PdfAction;
+import com.lowagie.text.pdf.PdfAnnotation;
+import com.lowagie.text.pdf.HyphenationEvent;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.markup.MarkupTags;
+import com.lowagie.text.markup.MarkupParser;
+
+/**
+ * This is the smallest significant part of text that can be added to a
+ * document.
+ * Chunk
s. A chunk
+ * is a String
with a certain Font
. All other
+ * layout parameters should be defined in the object to which this chunk of text
+ * is added.
+ *
+ *
+ *
+ */
+
+public class Chunk implements Element, MarkupAttributes {
+
+ // public static membervariables
+
+ /**
+ * The character stand in for an image.
+ */
+ public static final String OBJECT_REPLACEMENT_CHARACTER = "\ufffc";
+
+ /** This is a Chunk containing a newline. */
+ public static final Chunk NEWLINE = new Chunk("\n");
+
+ /** This is a Chunk containing a newpage. */
+ public static final Chunk NEXTPAGE = new Chunk("");
+ static {
+ NEXTPAGE.setNewPage();
+ }
+
+ /** Key for sub/superscript. */
+ public static final String SUBSUPSCRIPT = "SUBSUPSCRIPT";
+
+ /** Key for underline. */
+ public static final String UNDERLINE = "UNDERLINE";
+
+ /** Key for color. */
+ public static final String COLOR = "COLOR";
+
+ /** Key for encoding. */
+ public static final String ENCODING = "ENCODING";
+
+ /** Key for remote goto. */
+ public static final String REMOTEGOTO = "REMOTEGOTO";
+
+ /** Key for local goto. */
+ public static final String LOCALGOTO = "LOCALGOTO";
+
+ /** Key for local destination. */
+ public static final String LOCALDESTINATION = "LOCALDESTINATION";
+
+ /** Key for image. */
+ public static final String IMAGE = "IMAGE";
+
+ /** Key for generic tag. */
+ public static final String GENERICTAG = "GENERICTAG";
+
+ /** Key for newpage. */
+ public static final String NEWPAGE = "NEWPAGE";
+
+ /** Key for split character. */
+ public static final String SPLITCHARACTER = "SPLITCHARACTER";
+
+ /** Key for Action. */
+ public static final String ACTION = "ACTION";
+
+ /** Key for background. */
+ public static final String BACKGROUND = "BACKGROUND";
+
+ /** Key for annotation. */
+ public static final String PDFANNOTATION = "PDFANNOTATION";
+
+ /** Key for hyphenation. */
+ public static final String HYPHENATION = "HYPHENATION";
+
+ /** Key for text rendering mode. */
+ public static final String TEXTRENDERMODE = "TEXTRENDERMODE";
+
+ /** Key for text skewing. */
+ public static final String SKEW = "SKEW";
+
+ /** Key for text horizontal scaling. */
+ public static final String HSCALE = "HSCALE";
+
+ // member variables
+
+ /** This is the content of this chunk of text. */
+ protected StringBuffer content = null;
+
+ /** This is the
+ *
+ * Chunk chunk = new Chunk("Hello world",
+ * FontFactory.getFont(FontFactory.COURIER, 20, Font.ITALIC, new Color(255, 0,
+ * 0))); document.add(chunk);
+ *
+ *
+ *
+ * Font
of this chunk of text. */
+ protected Font font = null;
+
+ /** Contains some of the attributes for this Chunk. */
+ protected HashMap attributes = null;
+
+ /** Contains extra markupAttributes */
+ protected Properties markupAttributes = null;
+
+ // constructors
+
+ /**
+ * Empty constructor.
+ */
+ protected Chunk() {
+ }
+
+ /**
+ * A Chunk
copy constructor.
+ * @param ck the Chunk
to be copied
+ */
+ public Chunk(Chunk ck) {
+ if (ck.content != null) {
+ content = new StringBuffer(ck.content.toString());
+ }
+ if (ck.font != null) {
+ font = new Font(ck.font);
+ }
+ if (ck.attributes != null) {
+ attributes = new HashMap(ck.attributes);
+ }
+ if (ck.markupAttributes != null) {
+ markupAttributes = new Properties();
+ markupAttributes.putAll(ck.markupAttributes);
+ }
+ }
+
+ /**
+ * Constructs a chunk of text with a certain content and a certain
+ * Font
.
+ *
+ * @param content
+ * the content
+ * @param font
+ * the font
+ */
+
+ public Chunk(String content, Font font) {
+ this.content = new StringBuffer(content);
+ this.font = font;
+ }
+
+ /**
+ * Constructs a chunk of text with a certain content, without specifying a
+ * Font
.
+ *
+ * @param content
+ * the content
+ */
+ public Chunk(String content) {
+ this(content, new Font());
+ }
+
+ /**
+ * Constructs a chunk of text with a char and a certain Font
.
+ *
+ * @param c
+ * the content
+ * @param font
+ * the font
+ */
+ public Chunk(char c, Font font) {
+ this.content = new StringBuffer();
+ this.content.append(c);
+ this.font = font;
+ }
+
+ /**
+ * Constructs a chunk of text with a char, without specifying a Font
+ *
.
+ *
+ * @param c
+ * the content
+ */
+ public Chunk(char c) {
+ this(c, new Font());
+ }
+
+ /**
+ * Constructs a chunk containing an Image
.
+ *
+ * @param image
+ * the image
+ * @param offsetX
+ * the image offset in the x direction
+ * @param offsetY
+ * the image offset in the y direction
+ */
+
+ public Chunk(Image image, float offsetX, float offsetY) {
+ this(OBJECT_REPLACEMENT_CHARACTER, new Font());
+ Image copyImage = Image.getInstance(image);
+ copyImage.setAbsolutePosition(Float.NaN, Float.NaN);
+ setAttribute(IMAGE, new Object[] { copyImage, new Float(offsetX),
+ new Float(offsetY), new Boolean(false) });
+ }
+
+ /**
+ * Constructs a chunk containing an Image
.
+ *
+ * @param image
+ * the image
+ * @param offsetX
+ * the image offset in the x direction
+ * @param offsetY
+ * the image offset in the y direction
+ * @param changeLeading
+ * true if the leading has to be adapted to the image
+ */
+
+ public Chunk(Image image, float offsetX, float offsetY,
+ boolean changeLeading) {
+ this(OBJECT_REPLACEMENT_CHARACTER, new Font());
+ setAttribute(IMAGE, new Object[] { image, new Float(offsetX),
+ new Float(offsetY), new Boolean(changeLeading) });
+ }
+
+ /**
+ * Returns a Chunk
that has been constructed taking in
+ * account the value of some attributes .
+ *
+ * @param attributes
+ * Some attributes
+ */
+
+ public Chunk(Properties attributes) {
+ this("", FontFactory.getFont(attributes));
+ String value;
+ if ((value = (String) attributes.remove(ElementTags.ITEXT)) != null) {
+ append(value);
+ }
+ if ((value = (String) attributes.remove(ElementTags.LOCALGOTO)) != null) {
+ setLocalGoto(value);
+ }
+ if ((value = (String) attributes.remove(ElementTags.REMOTEGOTO)) != null) {
+ String destination = (String) attributes
+ .remove(ElementTags.DESTINATION);
+ String page = (String) attributes.remove(ElementTags.PAGE);
+ if (page != null) {
+ setRemoteGoto(value, Integer.parseInt(page));
+ } else if (destination != null) {
+ setRemoteGoto(value, destination);
+ }
+ }
+ if ((value = (String) attributes.remove(ElementTags.LOCALDESTINATION)) != null) {
+ setLocalDestination(value);
+ }
+ if ((value = (String) attributes.remove(ElementTags.SUBSUPSCRIPT)) != null) {
+ setTextRise(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String) attributes
+ .remove(MarkupTags.CSS_KEY_VERTICALALIGN)) != null
+ && value.endsWith("%")) {
+ float p = Float.valueOf(
+ value.substring(0, value.length() - 1) + "f").floatValue() / 100f;
+ setTextRise(p * font.size());
+ }
+ if ((value = (String) attributes.remove(ElementTags.GENERICTAG)) != null) {
+ setGenericTag(value);
+ }
+ if ((value = (String) attributes.remove(ElementTags.BACKGROUNDCOLOR)) != null) {
+ setBackground(MarkupParser.decodeColor(value));
+ }
+ if (attributes.size() > 0)
+ setMarkupAttributes(attributes);
+ }
+
+ // implementation of the Element-methods
+
+ /**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener
+ * an ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ return listener.add(this);
+ } catch (DocumentException de) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.CHUNK;
+ }
+
+ /**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ ArrayList tmp = new ArrayList();
+ tmp.add(this);
+ return tmp;
+ }
+
+ // methods
+
+ /**
+ * appends some text to this Chunk
.
+ *
+ * @param string
+ * String
+ * @return a StringBuffer
+ */
+
+ public StringBuffer append(String string) {
+ return content.append(string);
+ }
+
+ // methods to retrieve information
+
+ /**
+ * Gets the font of this Chunk
.
+ *
+ * @return a Font
+ */
+
+ public Font font() {
+ return font;
+ }
+
+ /**
+ * Sets the font of this Chunk
.
+ *
+ * @param font
+ * a Font
+ */
+
+ public void setFont(Font font) {
+ this.font = font;
+ }
+
+ /**
+ * Returns the content of this Chunk
.
+ *
+ * @return a String
+ */
+
+ public String content() {
+ return content.toString();
+ }
+
+ /**
+ * Returns the content of this Chunk
.
+ *
+ * @return a String
+ */
+
+ public String toString() {
+ return content.toString();
+ }
+
+ /**
+ * Checks is this Chunk
is empty.
+ *
+ * @return false
if the Chunk contains other characters than
+ * space.
+ */
+
+ public boolean isEmpty() {
+ return (content.toString().trim().length() == 0)
+ && (content.toString().indexOf("\n") == -1)
+ && (attributes == null);
+ }
+
+ /**
+ * Gets the width of the Chunk in points.
+ *
+ * @return a width in points
+ */
+ public float getWidthPoint() {
+ if (getImage() != null) {
+ return getImage().scaledWidth();
+ }
+ return font.getCalculatedBaseFont(true).getWidthPoint(content(),
+ font.getCalculatedSize())
+ * getHorizontalScaling();
+ }
+
+ /**
+ * Sets the text displacement relative to the baseline. Positive values rise
+ * the text, negative values lower the text.
+ * Chunk
+ */
+
+ public Chunk setTextRise(float rise) {
+ return setAttribute(SUBSUPSCRIPT, new Float(rise));
+ }
+
+ /**
+ * Gets the text displacement relatiev to the baseline.
+ *
+ * @return a displacement in points
+ */
+ public float getTextRise() {
+ if (attributes.containsKey(SUBSUPSCRIPT)) {
+ Float f = (Float) attributes.get(SUBSUPSCRIPT);
+ return f.floatValue();
+ }
+ return 0.0f;
+ }
+
+ /**
+ * Sets the text rendering mode. It can outline text, simulate bold and make
+ * text invisible.
+ *
+ * @param mode
+ * the text rendering mode. It can be
+ * PdfContentByte.TEXT_RENDER_MODE_FILL
,
+ * PdfContentByte.TEXT_RENDER_MODE_STROKE
,
+ * PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE
and
+ * PdfContentByte.TEXT_RENDER_MODE_INVISIBLE
.
+ * @param strokeWidth
+ * the stroke line width for the modes
+ * PdfContentByte.TEXT_RENDER_MODE_STROKE
and
+ * PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE
.
+ * @param strokeColor
+ * the stroke color or null
to follow the text
+ * color
+ * @return this Chunk
+ */
+ public Chunk setTextRenderMode(int mode, float strokeWidth,
+ Color strokeColor) {
+ return setAttribute(TEXTRENDERMODE, new Object[] { new Integer(mode),
+ new Float(strokeWidth), strokeColor });
+ }
+
+ /**
+ * Skews the text to simulate italic and other effects. Try alpha=0
+ *
and beta=12
.
+ *
+ * @param alpha
+ * the first angle in degrees
+ * @param beta
+ * the second angle in degrees
+ * @return this Chunk
+ */
+ public Chunk setSkew(float alpha, float beta) {
+ alpha = (float) Math.tan(alpha * Math.PI / 180);
+ beta = (float) Math.tan(beta * Math.PI / 180);
+ return setAttribute(SKEW, new float[] { alpha, beta });
+ }
+
+ /**
+ * Sets the text horizontal scaling. A value of 1 is normal and a value of
+ * 0.5f shrinks the text to half it's width.
+ *
+ * @param scale
+ * the horizontal scaling factor
+ * @return this Chunk
+ */
+ public Chunk setHorizontalScaling(float scale) {
+ return setAttribute(HSCALE, new Float(scale));
+ }
+
+ /**
+ * Gets the horizontal scaling.
+ *
+ * @return a percentage in float
+ */
+ public float getHorizontalScaling() {
+ if (attributes == null)
+ return 1f;
+ Float f = (Float) attributes.get(HSCALE);
+ if (f == null)
+ return 1f;
+ return f.floatValue();
+ }
+
+ /**
+ * Sets an action for this Chunk
.
+ *
+ * @param action
+ * the action
+ * @return this Chunk
+ */
+
+ public Chunk setAction(PdfAction action) {
+ return setAttribute(ACTION, action);
+ }
+
+ /**
+ * Sets an anchor for this Chunk
.
+ *
+ * @param url
+ * the URL
to link to
+ * @return this Chunk
+ */
+
+ public Chunk setAnchor(URL url) {
+ return setAttribute(ACTION, new PdfAction(url.toExternalForm()));
+ }
+
+ /**
+ * Sets an anchor for this Chunk
.
+ *
+ * @param url
+ * the url to link to
+ * @return this Chunk
+ */
+
+ public Chunk setAnchor(String url) {
+ return setAttribute(ACTION, new PdfAction(url));
+ }
+
+ /**
+ * Sets a local goto for this Chunk
.
+ * Chunk
+ */
+
+ public Chunk setLocalGoto(String name) {
+ return setAttribute(LOCALGOTO, name);
+ }
+
+ /**
+ * Sets the color of the background Chunk
.
+ *
+ * @param color
+ * the color of the background
+ * @return this Chunk
+ */
+ public Chunk setBackground(Color color) {
+ return setBackground(color, 0, 0, 0, 0);
+ }
+
+ /**
+ * Sets the color and the size of the background Chunk
.
+ *
+ * @param color
+ * the color of the background
+ * @param extraLeft
+ * increase the size of the rectangle in the left
+ * @param extraBottom
+ * increase the size of the rectangle in the bottom
+ * @param extraRight
+ * increase the size of the rectangle in the right
+ * @param extraTop
+ * increase the size of the rectangle in the top
+ * @return this Chunk
+ */
+ public Chunk setBackground(Color color, float extraLeft, float extraBottom,
+ float extraRight, float extraTop) {
+ return setAttribute(BACKGROUND, new Object[] { color,
+ new float[] { extraLeft, extraBottom, extraRight, extraTop } });
+ }
+
+ /**
+ * Sets an horizontal line that can be an underline or a strikethrough.
+ * Actually, the line can be anywhere vertically and has always the
+ * Chunk
width. Multiple call to this method will produce multiple
+ * lines.
+ *
+ * @param thickness
+ * the absolute thickness of the line
+ * @param yPosition
+ * the absolute y position relative to the baseline
+ * @return this Chunk
+ */
+ public Chunk setUnderline(float thickness, float yPosition) {
+ return setUnderline(null, thickness, 0f, yPosition, 0f,
+ PdfContentByte.LINE_CAP_BUTT);
+ }
+
+ /**
+ * Sets an horizontal line that can be an underline or a strikethrough.
+ * Actually, the line can be anywhere vertically and has always the
+ * Chunk
width. Multiple call to this method will produce multiple
+ * lines.
+ *
+ * @param color
+ * the color of the line or null
to follow the
+ * text color
+ * @param thickness
+ * the absolute thickness of the line
+ * @param thicknessMul
+ * the thickness multiplication factor with the font size
+ * @param yPosition
+ * the absolute y position relative to the baseline
+ * @param yPositionMul
+ * the position multiplication factor with the font size
+ * @param cap
+ * the end line cap. Allowed values are
+ * PdfContentByte.LINE_CAP_BUTT, PdfContentByte.LINE_CAP_ROUND
+ * and PdfContentByte.LINE_CAP_PROJECTING_SQUARE
+ * @return this Chunk
+ */
+ public Chunk setUnderline(Color color, float thickness, float thicknessMul,
+ float yPosition, float yPositionMul, int cap) {
+ if (attributes == null)
+ attributes = new HashMap();
+ Object obj[] = {
+ color,
+ new float[] { thickness, thicknessMul, yPosition, yPositionMul,
+ (float) cap } };
+ Object unders[][] = addToArray((Object[][]) attributes.get(UNDERLINE),
+ obj);
+ return setAttribute(UNDERLINE, unders);
+ }
+
+ /**
+ * Utility method to extend an array.
+ *
+ * @param original
+ * the original array or null
+ * @param item
+ * the item to be added to the array
+ * @return a new array with the item appended
+ */
+ public static Object[][] addToArray(Object original[][], Object item[]) {
+ if (original == null) {
+ original = new Object[1][];
+ original[0] = item;
+ return original;
+ } else {
+ Object original2[][] = new Object[original.length + 1][];
+ System.arraycopy(original, 0, original2, 0, original.length);
+ original2[original.length] = item;
+ return original2;
+ }
+ }
+
+ /**
+ * Sets a generic annotation to this Chunk
.
+ *
+ * @param annotation
+ * the annotation
+ * @return this Chunk
+ */
+ public Chunk setAnnotation(PdfAnnotation annotation) {
+ return setAttribute(PDFANNOTATION, annotation);
+ }
+
+ /**
+ * sets the hyphenation engine to this Chunk
.
+ *
+ * @param hyphenation
+ * the hyphenation engine
+ * @return this Chunk
+ */
+ public Chunk setHyphenation(HyphenationEvent hyphenation) {
+ return setAttribute(HYPHENATION, hyphenation);
+ }
+
+ /**
+ * Sets a goto for a remote destination for this Chunk
.
+ *
+ * @param filename
+ * the file name of the destination document
+ * @param name
+ * the name of the destination to go to
+ * @return this Chunk
+ */
+
+ public Chunk setRemoteGoto(String filename, String name) {
+ return setAttribute(REMOTEGOTO, new Object[] { filename, name });
+ }
+
+ /**
+ * Sets a goto for a remote destination for this Chunk
.
+ *
+ * @param filename
+ * the file name of the destination document
+ * @param page
+ * the page of the destination to go to. First page is 1
+ * @return this Chunk
+ */
+
+ public Chunk setRemoteGoto(String filename, int page) {
+ return setAttribute(REMOTEGOTO, new Object[] { filename,
+ new Integer(page) });
+ }
+
+ /**
+ * Sets a local destination for this Chunk
.
+ *
+ * @param name
+ * the name for this destination
+ * @return this Chunk
+ */
+ public Chunk setLocalDestination(String name) {
+ return setAttribute(LOCALDESTINATION, name);
+ }
+
+ /**
+ * Sets the generic tag Chunk
.
+ * PdfPageEvent
.
+ *
+ * @param text
+ * the text for the tag
+ * @return this Chunk
+ */
+
+ public Chunk setGenericTag(String text) {
+ return setAttribute(GENERICTAG, text);
+ }
+
+ /**
+ * Sets the split characters.
+ *
+ * @param splitCharacter
+ * the SplitCharacter
interface
+ * @return this Chunk
+ */
+
+ public Chunk setSplitCharacter(SplitCharacter splitCharacter) {
+ return setAttribute(SPLITCHARACTER, splitCharacter);
+ }
+
+ /**
+ * Sets a new page tag..
+ *
+ * @return this Chunk
+ */
+
+ public Chunk setNewPage() {
+ return setAttribute(NEWPAGE, null);
+ }
+
+ /**
+ * Sets an arbitrary attribute.
+ *
+ * @param name
+ * the key for the attribute
+ * @param obj
+ * the value of the attribute
+ * @return this Chunk
+ */
+
+ private Chunk setAttribute(String name, Object obj) {
+ if (attributes == null)
+ attributes = new HashMap();
+ attributes.put(name, obj);
+ return this;
+ }
+
+ /**
+ * Gets the attributes for this Chunk
.
+ * Chunk
+ */
+
+ public HashMap getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * Checks the attributes of this Chunk
.
+ *
+ * @return false if there aren't any.
+ */
+
+ public boolean hasAttributes() {
+ return attributes != null;
+ }
+
+ /**
+ * Returns the image.
+ *
+ * @return the image
+ */
+
+ public Image getImage() {
+ if (attributes == null)
+ return null;
+ Object obj[] = (Object[]) attributes.get(Chunk.IMAGE);
+ if (obj == null)
+ return null;
+ else {
+ return (Image) obj[0];
+ }
+ }
+
+ /**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag
+ * the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.CHUNK.equals(tag);
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttribute(java.lang.String,
+ * java.lang.String)
+ */
+ public void setMarkupAttribute(String name, String value) {
+ if (markupAttributes == null)
+ markupAttributes = new Properties();
+ markupAttributes.put(name, value);
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttributes(java.util.Properties)
+ */
+ public void setMarkupAttributes(Properties markupAttributes) {
+ this.markupAttributes = markupAttributes;
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttribute(java.lang.String)
+ */
+ public String getMarkupAttribute(String name) {
+ return (markupAttributes == null) ? null : String
+ .valueOf(markupAttributes.get(name));
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributeNames()
+ */
+ public Set getMarkupAttributeNames() {
+ return getKeySet(markupAttributes);
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributes()
+ */
+ public Properties getMarkupAttributes() {
+ return markupAttributes;
+ }
+
+ /**
+ * Gets the keys of a Hashtable
+ *
+ * @param table
+ * a Hashtable
+ * @return the keyset of a Hashtable (or an empty set if table is null)
+ */
+ public static Set getKeySet(Hashtable table) {
+ return (table == null) ? Collections.EMPTY_SET : table.keySet();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/DocListener.java b/src/main/java/com/lowagie/text/DocListener.java
new file mode 100644
index 0000000..bea3564
--- /dev/null
+++ b/src/main/java/com/lowagie/text/DocListener.java
@@ -0,0 +1,182 @@
+/*
+ * $Id: DocListener.java,v 1.52 2004/12/14 11:52:47 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright (c) 1999, 2000, 2001, 2002 Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+/**
+ * A class that implements DocListener
will perform some
+ * actions when some actions are performed on a Document
.
+ *
+ * @see ElementListener
+ * @see Document
+ * @see DocWriter
+ */
+
+public interface DocListener extends ElementListener {
+
+ // methods
+
+/**
+ * Signals that the Document
has been opened and that
+ * Elements
can be added.
+ */
+
+ public void open();
+
+/**
+ * Sets the pagesize.
+ *
+ * @param pageSize the new pagesize
+ * @return a boolean
+ */
+
+ public boolean setPageSize(Rectangle pageSize);
+
+/**
+ * Signals that a Watermark
was added to the Document
.
+ *
+ * @param watermark the Watermark object
+ * @return true
if the element was added, false
if not.
+ */
+
+ public boolean add(Watermark watermark);
+
+/**
+ * Signals that a Watermark
was removed from the Document
.
+ */
+
+ public void removeWatermark();
+
+/**
+ * Sets the margins.
+ *
+ * @param marginLeft the margin on the left
+ * @param marginRight the margin on the right
+ * @param marginTop the margin on the top
+ * @param marginBottom the margin on the bottom
+ * @return a boolean
+ */
+
+ public boolean setMargins(float marginLeft, float marginRight, float marginTop, float marginBottom);
+
+ /**
+ * Parameter that allows you to do margin mirroring (odd/even pages)
+ * @param marginMirroring
+ * @return true if succesfull
+ */
+ public boolean setMarginMirroring(boolean marginMirroring);
+
+/**
+ * Signals that an new page has to be started.
+ *
+ * @return true
if the page was added, false
if not.
+ * @throws DocumentException when a document isn't open yet, or has been closed
+ */
+
+ public boolean newPage() throws DocumentException;
+
+/**
+ * Changes the header of this document.
+ *
+ * @param header the new header
+ */
+
+ public void setHeader(HeaderFooter header);
+
+/**
+ * Resets the header of this document.
+ */
+
+ public void resetHeader();
+
+/**
+ * Changes the footer of this document.
+ *
+ * @param footer the new footer
+ */
+
+ public void setFooter(HeaderFooter footer);
+
+/**
+ * Resets the footer of this document.
+ */
+
+ public void resetFooter();
+
+/**
+ * Sets the page number to 0.
+ */
+
+ public void resetPageCount();
+
+/**
+ * Sets the page number.
+ *
+ * @param pageN the new page number
+ */
+
+ public void setPageCount(int pageN);
+
+/**
+ * Clears text wrapping around images (if applicable).
+ * Method suggested by Pelikan Stephan
+ * @throws DocumentException
+ */
+ public void clearTextWrap() throws DocumentException;
+
+/**
+ * Signals that the Document
was closed and that no other
+ * Elements
will be added.
+ * DocListener
will be closed.
+ */
+
+ public void close();
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/DocWriter.java b/src/main/java/com/lowagie/text/DocWriter.java
new file mode 100644
index 0000000..9622017
--- /dev/null
+++ b/src/main/java/com/lowagie/text/DocWriter.java
@@ -0,0 +1,510 @@
+/*
+ * $Id: DocWriter.java,v 1.70 2004/12/14 11:52:47 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.io.BufferedOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import com.lowagie.text.pdf.OutputStreamCounter;
+
+/**
+ * An abstract Writer
class for documents.
+ * DocWriter
is the abstract class of several writers such
+ * as PdfWriter
and HtmlWriter
.
+ * A DocWriter
can be added as a DocListener
+ * to a certain Document
by getting an instance (see method
+ * getInstance()
in the specific writer-classes).
+ * Every Element
added to the original Document
+ * will be written to the OutputStream
of the listening
+ * DocWriter
.
+ *
+ * @see Document
+ * @see DocListener
+ */
+
+public abstract class DocWriter implements DocListener {
+
+/** This is some byte that is often used. */
+ public static final byte NEWLINE = (byte)'\n';
+
+/** This is some byte that is often used. */
+ public static final byte TAB = (byte)'\t';
+
+/** This is some byte that is often used. */
+ public static final byte LT = (byte)'<';
+
+/** This is some byte that is often used. */
+ public static final byte SPACE = (byte)' ';
+
+/** This is some byte that is often used. */
+ public static final byte EQUALS = (byte)'=';
+
+/** This is some byte that is often used. */
+ public static final byte QUOTE = (byte)'\"';
+
+/** This is some byte that is often used. */
+ public static final byte GT = (byte)'>';
+
+/** This is some byte that is often used. */
+ public static final byte FORWARD = (byte)'/';
+
+ // membervariables
+
+/** The pageSize. */
+ protected Rectangle pageSize;
+
+/** This is the document that has to be written. */
+ protected Document document;
+
+/** The outputstream of this writer. */
+ protected OutputStreamCounter os;
+
+/** Is the writer open for writing? */
+ protected boolean open = false;
+
+/** Do we have to pause all writing actions? */
+ protected boolean pause = false;
+
+/** Closes the stream on document close */
+ protected boolean closeStream = true;
+
+ // constructor
+
+ protected DocWriter() {
+ }
+
+/**
+ * Constructs a DocWriter
.
+ *
+ * @param document The Document
that has to be written
+ * @param os The OutputStream
the writer has to write to.
+ */
+
+ protected DocWriter(Document document, OutputStream os) {
+ this.document = document;
+ this.os = new OutputStreamCounter(new BufferedOutputStream(os));
+ }
+
+ // implementation of the DocListener methods
+
+/**
+ * Signals that an Element
was added to the Document
.
+ * DocWriter
byte array to a classes
+ * derived from this abstract class.
+ *
+ * @param element A high level object to add
+ * @return
array according
+ * to the font's encoding.
+ * @return an array of false
+ * @throws DocumentException when a document isn't open yet, or has been closed
+ */
+
+ public boolean add(Element element) throws DocumentException {
+ return false;
+ }
+
+/**
+ * Signals that the Document
was opened.
+ */
+
+ public void open() {
+ open = true;
+ }
+
+/**
+ * Sets the pagesize.
+ *
+ * @param pageSize the new pagesize
+ * @return a boolean
+ */
+
+ public boolean setPageSize(Rectangle pageSize) {
+ this.pageSize = pageSize;
+ return true;
+ }
+
+/**
+ * Sets the Watermark
.
+ * DocWriter
corresponding with the key
+ */
+
+ public PdfObject get(PdfName key) {
+ return (PdfObject) hashMap.get(key);
+ }
+
+ // methods concerning the type of Dictionary
+
+/**
+ * Checks if a classes
+ * derived from this abstract class if they actually support the use of
+ * a
PdfObjectWatermark
.
+ *
+ * @param watermark A watermark object
+ * @return false
(because watermarks aren't supported by default).
+ */
+
+ public boolean add(Watermark watermark) {
+ return false;
+ }
+
+/**
+ * Removes the Watermark
(if there is one).
+ */
+
+ public void removeWatermark() {
+ }
+
+/**
+ * Sets the margins.
+ * false
+ */
+
+ public boolean setMargins(float marginLeft, float marginRight, float marginTop, float marginBottom) {
+ return false;
+ }
+
+/**
+ * Signals that an new page has to be started.
+ * true
if the page was added, false
if not.
+ * @throws DocumentException when a document isn't open yet, or has been closed
+ */
+
+ public boolean newPage() throws DocumentException {
+ if (!open) {
+ return false;
+ }
+ return true;
+ }
+
+/**
+ * Changes the header of this document.
+ * DocWriter
corresponding with the key
+ */
+
+ public void remove(PdfName key) {
+ hashMap.remove(key);
+ list.remove(key);
+ }
+
+/**
+ * Gets a classes
+ * derived from this abstract class if they actually support the use of
+ * headers.
+ *
+ * @param header the new header
+ */
+
+ public void setHeader(HeaderFooter header) {
+ }
+
+/**
+ * Resets the header of this document.
+ *
PdfObjectDocWriter
corresponding with the key
+ */
+ public void putDel(PdfName key, PdfObject value) {
+ if (value == null) {
+ hashMap.remove(key);
+ list.remove(key);
+ return;
+ }
+ hashMap.put(key, value);
+ list.add(key);
+ }
+
+/**
+ * Removes a classes
+ * derived from this abstract class if they actually support the use of
+ * headers.
+ */
+
+ public void resetHeader() {
+ }
+
+/**
+ * Changes the footer of this document.
+ *
PdfObjectDocWriter
corresponding with the key
+ */
+ public void putEx(PdfName key, PdfObject value) {
+ if (value == null)
+ return;
+ hashMap.put(key, value);
+ list.add(key);
+ }
+
+/**
+ * Adds a classes
+ * derived from this abstract class if they actually support the use of
+ * footers.
+ *
+ * @param footer the new footer
+ */
+
+ public void setFooter(HeaderFooter footer) {
+ }
+
+/**
+ * Resets the footer of this document.
+ *
PdfObjectDocWriter
array according
+ * to the font's encoding.
+ * @param text the classes
+ * derived from this abstract class if they actually support the use of
+ * footers.
+ */
+
+ public void resetFooter() {
+ }
+
+/**
+ * Sets the page number to 0.
+ *
byteDocWriter
+ */
+
+ protected void initFooter() {
+ if (footer != null) {
+ try {
+ // Set the page number. HTML has no notion of a page, so it should always
+ // add up to 1
+ footer.setPageNumber(pageN + 1);
+ add(footer.paragraph());
+ }
+ catch(Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+ }
+
+/**
+ * Writes a Metatag in the header.
+ *
+ * @param meta the element that has to be written
+ * @throws IOException
+ */
+
+ protected void writeHeader(Meta meta) throws IOException {
+ addTabs(2);
+ writeStart(HtmlTags.META);
+ switch(meta.type()) {
+ case Element.HEADER:
+ write(HtmlTags.NAME, ((Header) meta).name());
+ break;
+ case Element.SUBJECT:
+ write(HtmlTags.NAME, HtmlTags.SUBJECT);
+ break;
+ case Element.KEYWORDS:
+ write(HtmlTags.NAME, HtmlTags.KEYWORDS);
+ break;
+ case Element.AUTHOR:
+ write(HtmlTags.NAME, HtmlTags.AUTHOR);
+ break;
+ }
+ write(HtmlTags.CONTENT, HtmlEncoder.encode(meta.content()));
+ writeEnd();
+ }
+
+/**
+ * Writes a link in the header.
+ *
+ * @param header the element that has to be written
+ * @throws IOException
+ */
+
+ protected void writeLink(Header header) throws IOException {
+ addTabs(2);
+ writeStart(HtmlTags.LINK);
+ write(HtmlTags.REL, header.name());
+ write(HtmlTags.TYPE, HtmlTags.TEXT_CSS);
+ write(HtmlTags.REFERENCE, header.content());
+ writeEnd();
+ }
+
+/**
+ * Writes a JavaScript section or, if the markup attribute HtmlTags.URL is set, a JavaScript reference in the header.
+ *
+ * @param header the element that has to be written
+ * @throws IOException
+ */
+
+ protected void writeJavaScript(Header header) throws IOException {
+ addTabs(2);
+ writeStart(HtmlTags.SCRIPT);
+ write(HtmlTags.LANGUAGE, HtmlTags.JAVASCRIPT);
+ if (header.getMarkupAttribute(HtmlTags.URL) != null) {
+ /* JavaScript reference example:
+ *
+ *
+ */
+ write(HtmlTags.URL, header.getMarkupAttribute(HtmlTags.URL));
+ os.write(GT);
+ writeEnd(HtmlTags.SCRIPT);
+ }
+ else {
+ /* JavaScript coding convention:
+ *
+ *
+ */
+ write(HtmlTags.TYPE, MarkupTags.HTML_VALUE_JAVASCRIPT);
+ os.write(GT);
+ addTabs(2);
+ write(new String(BEGINCOMMENT) + "\n");
+ write(header.content());
+ addTabs(2);
+ write("//" + new String(ENDCOMMENT));
+ addTabs(2);
+ writeEnd(HtmlTags.SCRIPT);
+ }
+ }
+
+/**
+ * Writes some comment.
+ * classes
+ * derived from this abstract class if they actually support the use of
+ * pagenumbers.
+ */
+
+ public void resetPageCount() {
+ }
+
+/**
+ * Sets the page number.
+ *
DocumentDocWriter
+ */
+
+ protected void initHeader() {
+ if (header != null) {
+ try {
+ add(header.paragraph());
+ }
+ catch(Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+ }
+
+/**
+ * Adds the header to the top of the classes
+ * derived from this abstract class if they actually support the use of
+ * pagenumbers.
+ *
+ * @param pageN the new page number
+ */
+
+ public void setPageCount(int pageN) {
+ }
+
+/**
+ * Signals that the
+ *
+ *
+ *
+ */
+
+public class Document implements DocListener {
+
+ // membervariables
+
+ /** This constant may only be changed by Paulo Soares and/or Bruno Lowagie. */
+ private static final String ITEXT_VERSION = "iText 1.4.2 (by lowagie.com)";
+
+ /**
+ * Allows the pdf documents to be produced without compression for debugging
+ * purposes.
+ */
+ public static boolean compress = true;
+
+ /** The DocListener. */
+ private ArrayList listeners = new ArrayList();
+
+ /** Is the document open or not? */
+ protected boolean open;
+
+ /** Has the document already been closed? */
+ protected boolean close;
+
+ // membervariables concerning the layout
+
+ /** The size of the page. */
+ protected Rectangle pageSize;
+
+ /** The watermark on the pages. */
+ protected Watermark watermark = null;
+
+ /** margin in x direction starting from the left */
+ protected float marginLeft = 0;
+
+ /** margin in x direction starting from the right */
+ protected float marginRight = 0;
+
+ /** margin in y direction starting from the top */
+ protected float marginTop = 0;
+
+ /** margin in y direction starting from the bottom */
+ protected float marginBottom = 0;
+
+ protected boolean marginMirroring = false;
+
+ /** Content of JavaScript onLoad function */
+ protected String javaScript_onLoad = null;
+
+ /** Content of JavaScript onUnLoad function */
+ protected String javaScript_onUnLoad = null;
+
+ /** Style class in HTML body tag */
+ protected String htmlStyleClass = null;
+
+ // headers, footers
+
+ /** Current pagenumber */
+ protected int pageN = 0;
+
+ /** This is the textual part of a Page; it can contain a header */
+ protected HeaderFooter header = null;
+
+ /** This is the textual part of the footer */
+ protected HeaderFooter footer = null;
+
+ // constructor
+
+ /**
+ * Constructs a new Document
was closed and that no other
+ * Elements
will be added.
+ */
+
+ public void close() {
+ open = false;
+ try {
+ os.flush();
+ if (closeStream)
+ os.close();
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+ // methods
+
+/** Converts a String
into a Byte
array
+ * according to the ISO-8859-1 codepage.
+ * @param text the text to be converted
+ * @return the conversion result
+ */
+
+ public static final byte[] getISOBytes(String text)
+ {
+ if (text == null)
+ return null;
+ int len = text.length();
+ byte b[] = new byte[len];
+ for (int k = 0; k < len; ++k)
+ b[k] = (byte)text.charAt(k);
+ return b;
+ }
+
+/**
+ * Let the writer know that all writing has to be paused.
+ */
+
+ public void pause() {
+ pause = true;
+ }
+
+/**
+ * Let the writer know that writing may be resumed.
+ */
+
+ public void resume() {
+ pause = false;
+ }
+
+/**
+ * Flushes the BufferedOutputStream
.
+ */
+
+ public void flush() {
+ try {
+ os.flush();
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+/**
+ * Writes a String
to the OutputStream
.
+ *
+ * @param string the String
to write
+ * @throws IOException
+ */
+
+ protected void write(String string) throws IOException {
+ os.write(getISOBytes(string));
+ }
+
+/**
+ * Writes a number of tabs.
+ *
+ * @param indent the number of tabs to add
+ * @throws IOException
+ */
+
+ protected void addTabs(int indent) throws IOException {
+ os.write(NEWLINE);
+ for (int i = 0; i < indent; i++) {
+ os.write(TAB);
+ }
+ }
+
+/**
+ * Writes a key-value pair to the outputstream.
+ *
+ * @param key the name of an attribute
+ * @param value the value of an attribute
+ * @throws IOException
+ */
+
+ protected void write(String key, String value)
+ throws IOException {
+ os.write(SPACE);
+ write(key);
+ os.write(EQUALS);
+ os.write(QUOTE);
+ write(value);
+ os.write(QUOTE);
+ }
+
+/**
+ * Writes a starttag to the outputstream.
+ *
+ * @param tag the name of the tag
+ * @throws IOException
+ */
+
+ protected void writeStart(String tag)
+ throws IOException {
+ os.write(LT);
+ write(tag);
+ }
+
+/**
+ * Writes an endtag to the outputstream.
+ *
+ * @param tag the name of the tag
+ * @throws IOException
+ */
+
+ protected void writeEnd(String tag)
+ throws IOException {
+ os.write(LT);
+ os.write(FORWARD);
+ write(tag);
+ os.write(GT);
+ }
+
+/**
+ * Writes an endtag to the outputstream.
+ * @throws IOException
+ */
+
+ protected void writeEnd()
+ throws IOException {
+ os.write(SPACE);
+ os.write(FORWARD);
+ os.write(GT);
+ }
+
+/**
+ * Writes the markup attributes of the specified MarkupAttributes
+ * object to the OutputStream
.
+ * @param mAtt the MarkupAttributes
to write.
+ * @return true, if writing the markup attributes succeeded
+ * @throws IOException
+ */
+ protected boolean writeMarkupAttributes(MarkupAttributes mAtt)
+ throws IOException
+ {
+ Iterator attributeIterator = mAtt.getMarkupAttributeNames().iterator();
+ boolean result = attributeIterator.hasNext();
+ while (attributeIterator.hasNext()) {
+ String name = String.valueOf(attributeIterator.next());
+ write(name, mAtt.getMarkupAttribute(name));
+ }
+ return result;
+ }
+
+
+/**
+ * Returns true
if the specified Element
implements
+ * MarkupAttributes
and has one or more attributes to write.
+ * @param element the Element
to check.
+ * @return boolean
.
+ */
+ protected static boolean hasMarkupAttributes(Element element) {
+ return (element instanceof MarkupAttributes &&
+ !(((MarkupAttributes)element).getMarkupAttributeNames().isEmpty()));
+ }
+
+ /** Checks if the stream is to be closed on document close
+ * @return true if the stream is closed on documnt close
+ *
+ */
+ public boolean isCloseStream() {
+ return closeStream;
+ }
+
+ /** Sets the close state of the stream after document close
+ * @param closeStream true if the stream is closed on document close
+ *
+ */
+ public void setCloseStream(boolean closeStream) {
+ this.closeStream = closeStream;
+ }
+
+
+ /**
+ * @see com.lowagie.text.DocListener#clearTextWrap()
+ */
+ public void clearTextWrap() throws DocumentException {
+ // do nothing
+ }
+ /**
+ * @see com.lowagie.text.DocListener#setMarginMirroring(boolean)
+ */
+ public boolean setMarginMirroring(boolean MarginMirroring) {
+ return false;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/Document.java b/src/main/java/com/lowagie/text/Document.java
new file mode 100644
index 0000000..ebce896
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Document.java
@@ -0,0 +1,916 @@
+/*
+ * $Id: Document.java,v 1.110 2006/06/23 11:56:04 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import java.util.Date;
+
+/**
+ * A generic Document class.
+ * HTMLDocument
.
+ * The Document
signals all the listeners when an element has
+ * been added.
+ *
+ *
+ * Example:
+ * OutputStream
) is closed too.
+ *
+ *
+ *
// creation of the document with a certain size and certain margins
+ * Document document = new Document(PageSize.A4, 50, 50, 50, 50);
+ * try { // creation of the different writers HtmlWriter.getInstance(
+ * document , System.out); PdfWriter.getInstance(
+ * document , new FileOutputStream("text.pdf"));
+ * // we add some meta information to the document
+ * document.addAuthor("Bruno Lowagie");
+ * document.addSubject("This is the result of a Test.");
+ * // we define a header and a footer HeaderFooter header = new
+ * HeaderFooter(new Phrase("This is a header."), false); HeaderFooter footer =
+ * new HeaderFooter(new Phrase("This is page "), new Phrase("."));
+ * footer.setAlignment(Element.ALIGN_CENTER);
+ * document.setHeader(header);
+ * document.setFooter(footer); // we open the document for
+ * writing document.open(); document.add(new
+ * Paragraph("Hello world")); } catch(DocumentException de) {
+ * System.err.println(de.getMessage()); } document.close();
+ *
Document
-object.
+ */
+
+ public Document() {
+ this(PageSize.A4);
+ }
+
+ /**
+ * Constructs a new Document
-object.
+ *
+ * @param pageSize
+ * the pageSize
+ */
+
+ public Document(Rectangle pageSize) {
+ this(pageSize, 36, 36, 36, 36);
+ }
+
+ /**
+ * Constructs a new Document
-object.
+ *
+ * @param pageSize
+ * the pageSize
+ * @param marginLeft
+ * the margin on the left
+ * @param marginRight
+ * the margin on the right
+ * @param marginTop
+ * the margin on the top
+ * @param marginBottom
+ * the margin on the bottom
+ */
+
+ public Document(Rectangle pageSize, float marginLeft, float marginRight,
+ float marginTop, float marginBottom) {
+ this.pageSize = pageSize;
+ this.marginLeft = marginLeft;
+ this.marginRight = marginRight;
+ this.marginTop = marginTop;
+ this.marginBottom = marginBottom;
+ }
+
+ // listener methods
+
+ /**
+ * Adds a DocListener
to the Document
.
+ *
+ * @param listener
+ * the new DocListener.
+ */
+
+ public void addDocListener(DocListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes a DocListener
from the Document
.
+ *
+ * @param listener
+ * the DocListener that has to be removed.
+ */
+
+ public void removeDocListener(DocListener listener) {
+ listeners.remove(listener);
+ }
+
+ // methods implementing the DocListener interface
+
+ /**
+ * Adds an Element
to the Document
.
+ *
+ * @param element
+ * the Element
to add
+ * @return true
if the element was added, false
+ *
if not
+ * @throws DocumentException
+ * when a document isn't open yet, or has been closed
+ */
+
+ public boolean add(Element element) throws DocumentException {
+ if (close) {
+ throw new DocumentException(
+ "The document has been closed. You can't add any Elements.");
+ }
+ int type = element.type();
+ if (open) {
+ if (!(type == Element.CHUNK || type == Element.PHRASE
+ || type == Element.PARAGRAPH || type == Element.TABLE
+ || type == Element.PTABLE
+ || type == Element.MULTI_COLUMN_TEXT
+ || type == Element.ANCHOR || type == Element.ANNOTATION
+ || type == Element.CHAPTER || type == Element.SECTION
+ || type == Element.LIST || type == Element.LISTITEM
+ || type == Element.RECTANGLE || type == Element.JPEG
+ || type == Element.IMGRAW || type == Element.IMGTEMPLATE || type == Element.GRAPHIC)) {
+ throw new DocumentException(
+ "The document is open; you can only add Elements with content.");
+ }
+ } else {
+ if (!(type == Element.HEADER || type == Element.TITLE
+ || type == Element.SUBJECT || type == Element.KEYWORDS
+ || type == Element.AUTHOR || type == Element.PRODUCER
+ || type == Element.CREATOR || type == Element.CREATIONDATE)) {
+ throw new DocumentException(
+ "The document is not open yet; you can only add Meta information.");
+ }
+ }
+ boolean success = false;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ success |= listener.add(element);
+ }
+ return success;
+ }
+
+ /**
+ * Opens the document.
+ * boolean
+ */
+
+ public boolean setPageSize(Rectangle pageSize) {
+ this.pageSize = pageSize;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.setPageSize(pageSize);
+ }
+ return true;
+ }
+
+ /**
+ * Sets the Watermark
.
+ *
+ * @param watermark
+ * the watermark to add
+ * @return true
if the element was added, false
+ *
if not.
+ */
+
+ public boolean add(Watermark watermark) {
+ this.watermark = watermark;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.add(watermark);
+ }
+ return true;
+ }
+
+ /**
+ * Removes the Watermark
.
+ */
+
+ public void removeWatermark() {
+ this.watermark = null;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.removeWatermark();
+ }
+ }
+
+ /**
+ * Sets the margins.
+ *
+ * @param marginLeft
+ * the margin on the left
+ * @param marginRight
+ * the margin on the right
+ * @param marginTop
+ * the margin on the top
+ * @param marginBottom
+ * the margin on the bottom
+ * @return a boolean
+ */
+
+ public boolean setMargins(float marginLeft, float marginRight,
+ float marginTop, float marginBottom) {
+ this.marginLeft = marginLeft;
+ this.marginRight = marginRight;
+ this.marginTop = marginTop;
+ this.marginBottom = marginBottom;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.setMargins(marginLeft, marginRight, marginTop,
+ marginBottom);
+ }
+ return true;
+ }
+
+ /**
+ * Signals that an new page has to be started.
+ *
+ * @return true
if the page was added, false
+ * if not.
+ * @throws DocumentException
+ * when a document isn't open yet, or has been closed
+ */
+
+ public boolean newPage() throws DocumentException {
+ if (!open || close) {
+ return false;
+ }
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.newPage();
+ }
+ return true;
+ }
+
+ /**
+ * Changes the header of this document.
+ *
+ * @param header
+ * the new header
+ */
+
+ public void setHeader(HeaderFooter header) {
+ this.header = header;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.setHeader(header);
+ }
+ }
+
+ /**
+ * Resets the header of this document.
+ */
+
+ public void resetHeader() {
+ this.header = null;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.resetHeader();
+ }
+ }
+
+ /**
+ * Changes the footer of this document.
+ *
+ * @param footer
+ * the new footer
+ */
+
+ public void setFooter(HeaderFooter footer) {
+ this.footer = footer;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.setFooter(footer);
+ }
+ }
+
+ /**
+ * Resets the footer of this document.
+ */
+
+ public void resetFooter() {
+ this.footer = null;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.resetFooter();
+ }
+ }
+
+ /**
+ * Sets the page number to 0.
+ */
+
+ public void resetPageCount() {
+ pageN = 0;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.resetPageCount();
+ }
+ }
+
+ /**
+ * Sets the page number.
+ *
+ * @param pageN
+ * the new page number
+ */
+
+ public void setPageCount(int pageN) {
+ this.pageN = pageN;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.setPageCount(pageN);
+ }
+ }
+
+ /**
+ * Returns the current page number.
+ *
+ * @return the current page number
+ */
+
+ public int getPageNumber() {
+ return this.pageN;
+ }
+
+ /**
+ * Closes the document.
+ * true
if successful, false
otherwise
+ */
+
+ public boolean addHeader(String name, String content) {
+ try {
+ return add(new Header(name, content));
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ /**
+ * Adds the title to a Document.
+ *
+ * @param title
+ * the title
+ * @return true
if successful, false
otherwise
+ */
+
+ public boolean addTitle(String title) {
+ try {
+ return add(new Meta(Element.TITLE, title));
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ /**
+ * Adds the subject to a Document.
+ *
+ * @param subject
+ * the subject
+ * @return true
if successful, false
otherwise
+ */
+
+ public boolean addSubject(String subject) {
+ try {
+ return add(new Meta(Element.SUBJECT, subject));
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ /**
+ * Adds the keywords to a Document.
+ *
+ * @param keywords
+ * adds the keywords to the document
+ * @return true
if successful, false
otherwise
+ */
+
+ public boolean addKeywords(String keywords) {
+ try {
+ return add(new Meta(Element.KEYWORDS, keywords));
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ /**
+ * Adds the author to a Document.
+ *
+ * @param author
+ * the name of the author
+ * @return true
if successful, false
otherwise
+ */
+
+ public boolean addAuthor(String author) {
+ try {
+ return add(new Meta(Element.AUTHOR, author));
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ /**
+ * Adds the creator to a Document.
+ *
+ * @param creator
+ * the name of the creator
+ * @return true
if successful, false
otherwise
+ */
+
+ public boolean addCreator(String creator) {
+ try {
+ return add(new Meta(Element.CREATOR, creator));
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ /**
+ * Adds the producer to a Document.
+ *
+ * @return true
if successful, false
otherwise
+ */
+
+ public boolean addProducer() {
+ try {
+ return add(new Meta(Element.PRODUCER, "iText by lowagie.com"));
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ /**
+ * Adds the current date and time to a Document.
+ *
+ * @return true
if successful, false
otherwise
+ */
+
+ public boolean addCreationDate() {
+ try {
+ /* bugfix by 'taqua' (Thomas) */
+ final SimpleDateFormat sdf = new SimpleDateFormat(
+ "EEE MMM dd HH:mm:ss zzz yyyy");
+ return add(new Meta(Element.CREATIONDATE, sdf.format(new Date())));
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ // methods to get the layout of the document.
+
+ /**
+ * Returns the left margin.
+ *
+ * @return the left margin
+ */
+
+ public float leftMargin() {
+ return marginLeft;
+ }
+
+ /**
+ * Return the right margin.
+ *
+ * @return the right margin
+ */
+
+ public float rightMargin() {
+ return marginRight;
+ }
+
+ /**
+ * Returns the top margin.
+ *
+ * @return the top margin
+ */
+
+ public float topMargin() {
+ return marginTop;
+ }
+
+ /**
+ * Returns the bottom margin.
+ *
+ * @return the bottom margin
+ */
+
+ public float bottomMargin() {
+ return marginBottom;
+ }
+
+ /**
+ * Returns the lower left x-coordinate.
+ *
+ * @return the lower left x-coordinate
+ */
+
+ public float left() {
+ return pageSize.left(marginLeft);
+ }
+
+ /**
+ * Returns the upper right x-coordinate.
+ *
+ * @return the upper right x-coordinate
+ */
+
+ public float right() {
+ return pageSize.right(marginRight);
+ }
+
+ /**
+ * Returns the upper right y-coordinate.
+ *
+ * @return the upper right y-coordinate
+ */
+
+ public float top() {
+ return pageSize.top(marginTop);
+ }
+
+ /**
+ * Returns the lower left y-coordinate.
+ *
+ * @return the lower left y-coordinate
+ */
+
+ public float bottom() {
+ return pageSize.bottom(marginBottom);
+ }
+
+ /**
+ * Returns the lower left x-coordinate considering a given margin.
+ *
+ * @param margin
+ * a margin
+ * @return the lower left x-coordinate
+ */
+
+ public float left(float margin) {
+ return pageSize.left(marginLeft + margin);
+ }
+
+ /**
+ * Returns the upper right x-coordinate, considering a given margin.
+ *
+ * @param margin
+ * a margin
+ * @return the upper right x-coordinate
+ */
+
+ public float right(float margin) {
+ return pageSize.right(marginRight + margin);
+ }
+
+ /**
+ * Returns the upper right y-coordinate, considering a given margin.
+ *
+ * @param margin
+ * a margin
+ * @return the upper right y-coordinate
+ */
+
+ public float top(float margin) {
+ return pageSize.top(marginTop + margin);
+ }
+
+ /**
+ * Returns the lower left y-coordinate, considering a given margin.
+ *
+ * @param margin
+ * a margin
+ * @return the lower left y-coordinate
+ */
+
+ public float bottom(float margin) {
+ return pageSize.bottom(marginBottom + margin);
+ }
+
+ /**
+ * Gets the pagesize.
+ *
+ * @return the page size
+ */
+
+ public Rectangle getPageSize() {
+ return this.pageSize;
+ }
+
+ /**
+ * Checks if the document is open.
+ *
+ * @return true
if the document is open
+ */
+ public boolean isOpen() {
+ return open;
+ }
+
+ /**
+ * Gets the iText version.
+ * This method may only be changed by Paulo Soares and/or Bruno Lowagie.
+ * @return iText version
+ */
+ public static final String getVersion() {
+ return ITEXT_VERSION;
+ }
+
+ /**
+ * Adds a JavaScript onLoad function to the HTML body tag
+ *
+ * @param code
+ * the JavaScript code to be executed on load of the HTML page
+ */
+
+ public void setJavaScript_onLoad(String code) {
+ this.javaScript_onLoad = code;
+ }
+
+ /**
+ * Gets the JavaScript onLoad command.
+ *
+ * @return the JavaScript onLoad command
+ */
+
+ public String getJavaScript_onLoad() {
+ return this.javaScript_onLoad;
+ }
+
+ /**
+ * Adds a JavaScript onUnLoad function to the HTML body tag
+ *
+ * @param code
+ * the JavaScript code to be executed on unload of the HTML page
+ */
+
+ public void setJavaScript_onUnLoad(String code) {
+ this.javaScript_onUnLoad = code;
+ }
+
+ /**
+ * Gets the JavaScript onUnLoad command.
+ *
+ * @return the JavaScript onUnLoad command
+ */
+
+ public String getJavaScript_onUnLoad() {
+ return this.javaScript_onUnLoad;
+ }
+
+ /**
+ * Adds a style class to the HTML body tag
+ *
+ * @param htmlStyleClass
+ * the style class for the HTML body tag
+ */
+
+ public void setHtmlStyleClass(String htmlStyleClass) {
+ this.htmlStyleClass = htmlStyleClass;
+ }
+
+ /**
+ * Gets the style class of the HTML body tag
+ *
+ * @return the style class of the HTML body tag
+ */
+
+ public String getHtmlStyleClass() {
+ return this.htmlStyleClass;
+ }
+
+ /**
+ * @see com.lowagie.text.DocListener#clearTextWrap()
+ */
+ public void clearTextWrap() throws DocumentException {
+ if (open && !close) {
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.clearTextWrap();
+ }
+ }
+ }
+
+ /**
+ * Set the margin mirroring. It will mirror margins for odd/even pages.
+ * true
to mirror the margins
+ * @return always true
+ */
+ public boolean setMarginMirroring(boolean marginMirroring) {
+ this.marginMirroring = marginMirroring;
+ DocListener listener;
+ for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
+ listener = (DocListener) iterator.next();
+ listener.setMarginMirroring(marginMirroring);
+ }
+ return true;
+ }
+
+ /**
+ * Gets the margin mirroring flag.
+ *
+ * @return the margin mirroring flag
+ */
+ public boolean isMarginMirroring() {
+ return marginMirroring;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/DocumentException.java b/src/main/java/com/lowagie/text/DocumentException.java
new file mode 100644
index 0000000..53d7174
--- /dev/null
+++ b/src/main/java/com/lowagie/text/DocumentException.java
@@ -0,0 +1,186 @@
+/*
+ * $Id: DocumentException.java,v 1.50 2004/12/14 11:52:46 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ *
+ */
+
+package com.lowagie.text;
+
+/**
+ * Signals that an error has occurred in a Document
.
+ *
+ * @see BadElementException
+ * @see Document
+ * @see DocWriter
+ * @see DocListener
+ */
+
+public class DocumentException extends Exception {
+ private Exception ex;
+
+ /**
+ * Creates a Document exception.
+ * @param ex an exception that has to be turned into a DocumentException
+ */
+ public DocumentException(Exception ex) {
+ this.ex = ex;
+ }
+
+ // constructors
+
+/**
+ * Constructs a DocumentException
whithout a message.
+ */
+
+ public DocumentException() {
+ super();
+ }
+
+/**
+ * Constructs a DocumentException
with a message.
+ *
+ * @param message a message describing the exception
+ */
+
+ public DocumentException(String message) {
+ super(message);
+ }
+
+ /**
+ * We print the message of the checked exception
+ * @return the error message
+ */
+ public String getMessage() {
+ if (ex == null)
+ return super.getMessage();
+ else
+ return ex.getMessage();
+ }
+
+ /**
+ * and make sure we also produce a localized version
+ * @return a localized message
+ */
+ public String getLocalizedMessage() {
+ if (ex == null)
+ return super.getLocalizedMessage();
+ else
+ return ex.getLocalizedMessage();
+ }
+
+ /**
+ * The toString() is changed to be prefixed with ExceptionConverter
+ * @return the String version of the exception
+ */
+ public String toString() {
+ if (ex == null)
+ return super.toString();
+ else
+ return split(getClass().getName()) + ": " + ex;
+ }
+
+ /** we have to override this as well */
+ public void printStackTrace() {
+ printStackTrace(System.err);
+ }
+
+ /**
+ * here we prefix, with s.print(), not s.println(), the stack
+ * trace with "ExceptionConverter:"
+ * @param s a printstream object
+ */
+ public void printStackTrace(java.io.PrintStream s) {
+ if (ex == null)
+ super.printStackTrace(s);
+ else {
+ synchronized (s) {
+ s.print(split(getClass().getName()) + ": ");
+ ex.printStackTrace(s);
+ }
+ }
+ }
+
+ /**
+ * Again, we prefix the stack trace with "ExceptionConverter:"
+ * @param s A PrintWriter object
+ */
+ public void printStackTrace(java.io.PrintWriter s) {
+ if (ex == null)
+ super.printStackTrace(s);
+ else {
+ synchronized (s) {
+ s.print(split(getClass().getName()) + ": ");
+ ex.printStackTrace(s);
+ }
+ }
+ }
+
+ /**
+ * Removes everything in a String that comes before a '.'
+ * @param s the original string
+ * @return the part that comes after the dot
+ */
+ private static String split(String s) {
+ int i = s.lastIndexOf('.');
+ if (i < 0)
+ return s;
+ else
+ return s.substring(i + 1);
+ }
+
+ /** requests to fill in the stack trace we will have to ignore.
+ * We can't throw an exception here, because this method
+ * is called by the constructor of Throwable */
+// public Throwable fillInStackTrace() {
+// if (ex == null)
+// return super.fillInStackTrace();
+// else
+// return this;
+// }
+
+}
diff --git a/src/main/java/com/lowagie/text/Element.java b/src/main/java/com/lowagie/text/Element.java
new file mode 100644
index 0000000..1822a97
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Element.java
@@ -0,0 +1,325 @@
+/*
+ * $Id: Element.java,v 1.66 2005/05/04 14:41:15 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.util.ArrayList;
+
+/**
+ * Interface for a text element.
+ *
+ *
+ *
+ * @see Anchor
+ * @see Cell
+ * @see Chapter
+ * @see Chunk
+ * @see Graphic
+ * @see Header
+ * @see Image
+ * @see Jpeg
+ * @see List
+ * @see ListItem
+ * @see Meta
+ * @see Paragraph
+ * @see Phrase
+ * @see Rectangle
+ * @see Row
+ * @see Section
+ * @see Table
+ */
+
+public interface Element {
+
+ // static membervariables (meta information)
+
+ /** This is a possible type of Element
. */
+ public static final int HEADER = 0;
+
+ /** This is a possible type of Element
. */
+ public static final int TITLE = 1;
+
+ /** This is a possible type of Element
. */
+ public static final int SUBJECT = 2;
+
+ /** This is a possible type of Element
. */
+ public static final int KEYWORDS = 3;
+
+ /** This is a possible type of Element . */
+ public static final int AUTHOR = 4;
+
+ /** This is a possible type of
DocumentElement . */
+ public static final int PRODUCER = 5;
+
+ /** This is a possible type of
+ *
+ */
+
+public class HtmlWriter extends DocWriter implements DocListener {
+
+ // static membervariables (tags)
+
+/** This is a possible HTML-tag. */
+ public static final byte[] BEGINCOMMENT = getISOBytes("");
+
+/** This is a possible HTML-tag. */
+ public static final String NBSP = " ";
+
+ // membervariables
+
+/** This is the current font of the HTML. */
+ protected Stack currentfont = new Stack();
+
+/** This is the standard font of the HTML. */
+ protected Font standardfont = new Font();
+
+/** This is a path for images. */
+ protected String imagepath = null;
+
+/** Stores the page number. */
+ protected int pageN = 0;
+
+/** This is the textual part of a header */
+ protected HeaderFooter header = null;
+
+/** This is the textual part of the footer */
+ protected HeaderFooter footer = null;
+
+ // constructor
+
+/**
+ * Constructs a Element . */
+ public static final int CREATIONDATE = 6;
+
+ /** This is a possible type of
's to empty/null spaces.
+ */
+
+ private void fillEmptyMatrixCells() {
+ try {
+ for (int i=0; i < rows.size(); i++) {
+ for (int j=0; j < columns; j++) {
+ if ( ((Row) rows.get(i)).isReserved(j) == false) {
+ addCell(defaultLayout, new Point(i, j));
+ }
+ }
+ }
+ }
+ catch(BadElementException bee) {
+ throw new ExceptionConverter(bee);
+ }
+ }
+
+ /**
+ * check if Element . */
+ public static final int CREATOR = 7;
+
+ // static membervariables (content)
+
+ /** This is a possible type of
, UNSUPPORTED_MARKER or NOPARAM_MARKER
+ */
+
+ private static final int marker(int marker) {
+ for (int i = 0; i < VALID_MARKERS.length; i++) {
+ if (marker == VALID_MARKERS[i]) {
+ return VALID_MARKER;
+ }
+ }
+ for (int i = 0; i < NOPARAM_MARKERS.length; i++) {
+ if (marker == NOPARAM_MARKERS[i]) {
+ return NOPARAM_MARKER;
+ }
+ }
+ for (int i = 0; i < UNSUPPORTED_MARKERS.length; i++) {
+ if (marker == UNSUPPORTED_MARKERS[i]) {
+ return UNSUPPORTED_MARKER;
+ }
+ }
+ return NOT_A_MARKER;
+ }
+
+ // private methods
+
+ /**
+ * This method checks if the image is a valid JPEG and processes some parameters.
+ * @throws BadElementException
+ * @throws IOException
+ */
+
+ private void processParameters() throws BadElementException, IOException {
+ type = JPEG;
+ originalType = ORIGINAL_JPEG;
+ InputStream is = null;
+ try {
+ String errorID;
+ if (rawData == null){
+ is = url.openStream();
+ errorID = url.toString();
+ }
+ else{
+ is = new java.io.ByteArrayInputStream(rawData);
+ errorID = "Byte array";
+ }
+ if (is.read() != 0xFF || is.read() != 0xD8) {
+ throw new BadElementException(errorID + " is not a valid JPEG-file.");
+ }
+ boolean firstPass = true;
+ int len;
+ while (true) {
+ int v = is.read();
+ if (v < 0)
+ throw new IOException("Premature EOF while reading JPG.");
+ if (v == 0xFF) {
+ int marker = is.read();
+ if (firstPass && marker == M_APP0) {
+ firstPass = false;
+ len = getShort(is);
+ if (len < 16) {
+ skip(is, len - 2);
+ continue;
+ }
+ byte bcomp[] = new byte[JFIF_ID.length];
+ int r = is.read(bcomp);
+ if (r != bcomp.length)
+ throw new BadElementException(errorID + " corrupted JFIF marker.");
+ boolean found = true;
+ for (int k = 0; k < bcomp.length; ++k) {
+ if (bcomp[k] != JFIF_ID[k]) {
+ found = false;
+ break;
+ }
+ }
+ if (!found) {
+ skip(is, len - 2 - bcomp.length);
+ continue;
+ }
+ skip(is, 2);
+ int units = is.read();
+ int dx = getShort(is);
+ int dy = getShort(is);
+ if (units == 1) {
+ dpiX = dx;
+ dpiY = dy;
+ }
+ else if (units == 2) {
+ dpiX = (int)((float)dx * 2.54f + 0.5f);
+ dpiY = (int)((float)dy * 2.54f + 0.5f);
+ }
+ skip(is, len - 2 - bcomp.length - 7);
+ continue;
+ }
+ if (marker == M_APPE) {
+ len = getShort(is);
+ byte[] byteappe = new byte[len];
+ for (int k = 0; k < len; ++k) {
+ byteappe[k] = (byte)is.read();
+ }
+ if (byteappe.length > 12) {
+ String appe = new String(byteappe, 0, 5, "ISO-8859-1");
+ if (appe.equals("Adobe")) {
+ invert = true;
+ }
+ }
+ }
+ firstPass = false;
+ int markertype = marker(marker);
+ if (markertype == VALID_MARKER) {
+ skip(is, 2);
+ if (is.read() != 0x08) {
+ throw new BadElementException(errorID + " must have 8 bits per component.");
+ }
+ scaledHeight = getShort(is);
+ setTop(scaledHeight);
+ scaledWidth = getShort(is);
+ setRight(scaledWidth);
+ colorspace = is.read();
+ bpc = 8;
+ break;
+ }
+ else if (markertype == UNSUPPORTED_MARKER) {
+ throw new BadElementException(errorID + ": unsupported JPEG marker: " + marker);
+ }
+ else if (markertype != NOPARAM_MARKER) {
+ skip(is, getShort(is) - 2);
+ }
+ }
+ }
+ }
+ finally {
+ if (is != null) {
+ is.close();
+ }
+ plainWidth = width();
+ plainHeight = height();
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/List.java b/src/main/java/com/lowagie/text/List.java
new file mode 100644
index 0000000..547cd31
--- /dev/null
+++ b/src/main/java/com/lowagie/text/List.java
@@ -0,0 +1,558 @@
+/*
+ * $Id: List.java,v 1.76 2005/09/27 23:28:52 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * A Element
. */
+ public static final int CHUNK = 10;
+
+ /** This is a possible type of Element
. */
+ public static final int PHRASE = 11;
+
+ /** This is a possible type of Element
. */
+ public static final int PARAGRAPH = 12;
+
+ /** This is a possible type of Element
*/
+ public static final int SECTION = 13;
+
+ /** This is a possible type of Element
*/
+ public static final int LIST = 14;
+
+ /** This is a possible type of Element
*/
+ public static final int LISTITEM = 15;
+
+ /** This is a possible type of Element
*/
+ public static final int CHAPTER = 16;
+
+ /** This is a possible type of Element
*/
+ public static final int ANCHOR = 17;
+
+ // static membervariables (tables)
+
+ /** This is a possible type of Element
. */
+ public static final int CELL = 20;
+
+ /** This is a possible type of Element
. */
+ public static final int ROW = 21;
+
+ /** This is a possible type of Element
. */
+ public static final int TABLE = 22;
+
+ /** This is a possible type of Element
. */
+ public static final int PTABLE = 23;
+
+ // static membervariables (annotations)
+
+ /** This is a possible type of Element
. */
+ public static final int ANNOTATION = 29;
+
+ // static membervariables (geometric figures)
+
+ /** This is a possible type of Element
. */
+ public static final int RECTANGLE = 30;
+
+ /** This is a possible type of Element
. */
+ public static final int JPEG = 32;
+
+ /** This is a possible type of Element
. */
+ public static final int IMGRAW = 34;
+
+ /** This is a possible type of Element
. */
+ public static final int IMGTEMPLATE = 35;
+
+ /** This is a possible type of Element
. */
+ public static final int GRAPHIC = 39;
+
+ /** This is a possible type of Element
. */
+ public static final int MULTI_COLUMN_TEXT = 40;
+
+ // static membervariables (alignment)
+
+ /**
+ * A possible value for paragraph alignment. This specifies that the text is
+ * aligned to the left indent and extra whitespace should be placed on the
+ * right.
+ */
+ public static final int ALIGN_UNDEFINED = -1;
+
+ /**
+ * A possible value for paragraph alignment. This specifies that the text is
+ * aligned to the left indent and extra whitespace should be placed on the
+ * right.
+ */
+ public static final int ALIGN_LEFT = 0;
+
+ /**
+ * A possible value for paragraph alignment. This specifies that the text is
+ * aligned to the center and extra whitespace should be placed equally on
+ * the left and right.
+ */
+ public static final int ALIGN_CENTER = 1;
+
+ /**
+ * A possible value for paragraph alignment. This specifies that the text is
+ * aligned to the right indent and extra whitespace should be placed on the
+ * left.
+ */
+ public static final int ALIGN_RIGHT = 2;
+
+ /**
+ * A possible value for paragraph alignment. This specifies that extra
+ * whitespace should be spread out through the rows of the paragraph with
+ * the text lined up with the left and right indent except on the last line
+ * which should be aligned to the left.
+ */
+ public static final int ALIGN_JUSTIFIED = 3;
+
+ /**
+ * A possible value for vertical alignment.
+ */
+
+ public static final int ALIGN_TOP = 4;
+
+ /**
+ * A possible value for vertical alignment.
+ */
+
+ public static final int ALIGN_MIDDLE = 5;
+
+ /**
+ * A possible value for vertical alignment.
+ */
+
+ public static final int ALIGN_BOTTOM = 6;
+
+ /**
+ * A possible value for vertical alignment.
+ */
+ public static final int ALIGN_BASELINE = 7;
+
+ /**
+ * Does the same as ALIGN_JUSTIFIED but the last line is also spread out.
+ */
+ public static final int ALIGN_JUSTIFIED_ALL = 8;
+
+ // static member variables for CCITT compression
+
+ /**
+ * Pure two-dimensional encoding (Group 4)
+ */
+ public static final int CCITTG4 = 0x100;
+
+ /**
+ * Pure one-dimensional encoding (Group 3, 1-D)
+ */
+ public static final int CCITTG3_1D = 0x101;
+
+ /**
+ * Mixed one- and two-dimensional encoding (Group 3, 2-D)
+ */
+ public static final int CCITTG3_2D = 0x102;
+
+ /**
+ * A flag indicating whether 1-bits are to be interpreted as black pixels
+ * and 0-bits as white pixels,
+ */
+ public static final int CCITT_BLACKIS1 = 1;
+
+ /**
+ * A flag indicating whether the filter expects extra 0-bits before each
+ * encoded line so that the line begins on a byte boundary.
+ */
+ public static final int CCITT_ENCODEDBYTEALIGN = 2;
+
+ /**
+ * A flag indicating whether end-of-line bit patterns are required to be
+ * present in the encoding.
+ */
+ public static final int CCITT_ENDOFLINE = 4;
+
+ /**
+ * A flag indicating whether the filter expects the encoded data to be
+ * terminated by an end-of-block pattern, overriding the Rows parameter. The
+ * use of this flag will set the key /EndOfBlock to false.
+ */
+ public static final int CCITT_ENDOFBLOCK = 8;
+
+ // methods
+
+ /**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener
+ * an ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener);
+
+ /**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type();
+
+ /**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks();
+
+ /**
+ * Gets the content of the text element.
+ *
+ * @return a type
+ */
+
+ public String toString();
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/ElementListener.java b/src/main/java/com/lowagie/text/ElementListener.java
new file mode 100644
index 0000000..cfde339
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ElementListener.java
@@ -0,0 +1,75 @@
+/*
+ * $Id: ElementListener.java,v 1.48 2004/12/14 11:52:46 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.util.EventListener;
+
+/**
+ * A class that implements ElementListener
will perform some
+ * actions when an Element
is added.
+ *
+ * @see DocListener
+ */
+
+public interface ElementListener extends EventListener {
+
+ // methods
+
+/**
+ * Signals that an Element
was added to the Document
.
+ *
+ * @param element a high level object
+ * @return true
if the element was added, false
if not.
+ * @throws DocumentException when a document isn't open yet, or has been closed
+ */
+
+ public boolean add(Element element) throws DocumentException;
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/ElementTags.java b/src/main/java/com/lowagie/text/ElementTags.java
new file mode 100644
index 0000000..bb06d7e
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ElementTags.java
@@ -0,0 +1,461 @@
+/*
+ * $Id: ElementTags.java,v 1.84 2005/04/06 07:01:18 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright (c) 2001, 2002 Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+/**
+ * A class that contains all the possible tagnames and their attributes.
+ */
+
+public class ElementTags {
+
+/** the root tag. */
+ public static final String ITEXT = "itext";
+
+/** attribute of the root and annotation tag (also a special tag within a chapter or section) */
+ public static final String TITLE = "title";
+
+/** attribute of the root tag */
+ public static final String SUBJECT = "subject";
+
+/** attribute of the root tag */
+ public static final String KEYWORDS = "keywords";
+
+/** attribute of the root tag */
+ public static final String AUTHOR = "author";
+
+/** attribute of the root tag */
+ public static final String CREATIONDATE = "creationdate";
+
+/** attribute of the root tag */
+ public static final String PRODUCER = "producer";
+
+ // Chapters and Sections
+
+/** the chapter tag */
+ public static final String CHAPTER = "chapter";
+
+/** the section tag */
+ public static final String SECTION = "section";
+
+/** attribute of section/chapter tag */
+ public static final String NUMBERDEPTH = "numberdepth";
+
+/** attribute of section/chapter tag */
+ public static final String DEPTH = "depth";
+
+/** attribute of section/chapter tag */
+ public static final String NUMBER = "number";
+
+/** attribute of section/chapter tag */
+ public static final String INDENT = "indent";
+
+/** attribute of chapter/section/paragraph/table/cell tag */
+ public static final String LEFT = "left";
+
+/** attribute of chapter/section/paragraph/table/cell tag */
+ public static final String RIGHT = "right";
+
+ // Phrases, Anchors, Lists and Paragraphs
+
+/** the phrase tag */
+ public static final String PHRASE = "phrase";
+
+/** the anchor tag */
+ public static final String ANCHOR = "anchor";
+
+/** the list tag */
+ public static final String LIST = "list";
+
+/** the listitem tag */
+ public static final String LISTITEM = "listitem";
+
+/** the paragraph tag */
+ public static final String PARAGRAPH = "paragraph";
+
+/** attribute of phrase/paragraph/cell tag */
+ public static final String LEADING = "leading";
+
+/** attribute of paragraph/image/table tag */
+ public static final String ALIGN = "align";
+
+/** attribute of paragraph */
+ public static final String KEEPTOGETHER = "keeptogether";
+
+/** attribute of anchor tag */
+ public static final String NAME = "name";
+
+/** attribute of anchor tag */
+ public static final String REFERENCE = "reference";
+
+/** attribute of list tag */
+ public static final String LISTSYMBOL = "listsymbol";
+
+/** attribute of list tag */
+ public static final String NUMBERED = "numbered";
+
+/** attribute of the list tag */
+ public static final String LETTERED = "lettered";
+
+/** attribute of list tag */
+ public static final String FIRST = "first";
+
+/** attribute of list tag */
+ public static final String SYMBOLINDENT = "symbolindent";
+
+/** attribute of list tag */
+ public static final String INDENTATIONLEFT = "indentationleft";
+
+/** attribute of list tag */
+ public static final String INDENTATIONRIGHT = "indentationright";
+
+ // Chunks
+
+/** the chunk tag */
+ public static final String IGNORE = "ignore";
+
+/** the chunk tag */
+ public static final String ENTITY = "entity";
+
+/** the chunk tag */
+ public static final String ID = "id";
+
+/** the chunk tag */
+ public static final String CHUNK = "chunk";
+
+/** attribute of the chunk tag */
+ public static final String ENCODING = "encoding";
+
+/** attribute of the chunk tag */
+ public static final String EMBEDDED = "embedded";
+
+/** attribute of the chunk/table/cell tag */
+ public static final String COLOR = "color";
+
+/** attribute of the chunk/table/cell tag */
+ public static final String RED = "red";
+
+/** attribute of the chunk/table/cell tag */
+ public static final String GREEN = "green";
+
+/** attribute of the chunk/table/cell tag */
+ public static final String BLUE = "blue";
+
+/** attribute of the chunk tag */
+ public static final String SUBSUPSCRIPT = Chunk.SUBSUPSCRIPT.toLowerCase();
+
+/** attribute of the chunk tag */
+ public static final String LOCALGOTO = Chunk.LOCALGOTO.toLowerCase();
+
+/** attribute of the chunk tag */
+ public static final String REMOTEGOTO = Chunk.REMOTEGOTO.toLowerCase();
+
+/** attribute of the chunk tag */
+ public static final String LOCALDESTINATION = Chunk.LOCALDESTINATION.toLowerCase();
+
+/** attribute of the chunk tag */
+ public static final String GENERICTAG = Chunk.GENERICTAG.toLowerCase();
+
+ // tables/cells
+
+/** the table tag */
+ public static final String TABLE = "table";
+
+/** the cell tag */
+ public static final String ROW = "row";
+
+/** the cell tag */
+ public static final String CELL = "cell";
+
+/** attribute of the table tag */
+ public static final String COLUMNS = "columns";
+
+/** attribute of the table tag */
+ public static final String LASTHEADERROW = "lastHeaderRow";
+
+/** attribute of the table tag */
+ public static final String CELLPADDING = "cellpadding";
+
+/** attribute of the table tag */
+ public static final String CELLSPACING = "cellspacing";
+
+/** attribute of the table tag */
+ public static final String OFFSET = "offset";
+
+/** attribute of the table tag */
+ public static final String WIDTHS = "widths";
+
+/** attribute of the table tag */
+ public static final String TABLEFITSPAGE = "tablefitspage";
+
+/** attribute of the table tag */
+ public static final String CELLSFITPAGE = "cellsfitpage";
+
+/** attribute of the cell tag */
+ public static final String HORIZONTALALIGN = "horizontalalign";
+
+/** attribute of the cell tag */
+ public static final String VERTICALALIGN = "verticalalign";
+
+/** attribute of the cell tag */
+ public static final String COLSPAN = "colspan";
+
+/** attribute of the cell tag */
+ public static final String ROWSPAN = "rowspan";
+
+/** attribute of the cell tag */
+ public static final String HEADER = "header";
+
+/** attribute of the cell tag */
+ public static final String NOWRAP = "nowrap";
+
+/** attribute of the table/cell tag */
+ public static final String BORDERWIDTH = "borderwidth";
+
+/** attribute of the table/cell tag */
+ public static final String TOP = "top";
+
+/** attribute of the table/cell tag */
+ public static final String BOTTOM = "bottom";
+
+/** attribute of the table/cell tag */
+ public static final String WIDTH = "width";
+
+/** attribute of the table/cell tag */
+ public static final String BORDERCOLOR = "bordercolor";
+
+/** attribute of the table/cell tag */
+ public static final String BACKGROUNDCOLOR = "backgroundcolor";
+
+/** attribute of the table/cell tag */
+ public static final String BGRED = "bgred";
+
+/** attribute of the table/cell tag */
+ public static final String BGGREEN = "bggreen";
+
+/** attribute of the table/cell tag */
+ public static final String BGBLUE = "bgblue";
+
+/** attribute of the table/cell tag */
+ public static final String GRAYFILL = "grayfill";
+
+ // Misc
+
+/** the image tag */
+ public static final String IMAGE = "image";
+
+/** attribute of the image and annotation tag */
+ public static final String URL = "url";
+
+/** attribute of the image tag */
+ public static final String UNDERLYING = "underlying";
+
+/** attribute of the image tag */
+ public static final String TEXTWRAP = "textwrap";
+
+/** attribute of the image tag */
+ public static final String ALT = "alt";
+
+/** attribute of the image tag */
+ public static final String ABSOLUTEX = "absolutex";
+
+/** attribute of the image tag */
+ public static final String ABSOLUTEY = "absolutey";
+
+/** attribute of the image tag */
+ public static final String PLAINWIDTH = "plainwidth";
+
+/** attribute of the image tag */
+ public static final String PLAINHEIGHT = "plainheight";
+
+/** attribute of the image tag */
+ public static final String SCALEDWIDTH = "scaledwidth";
+
+/** attribute of the image tag */
+ public static final String SCALEDHEIGHT = "scaledheight";
+
+/** attribute of the image tag */
+ public static final String ROTATION = "rotation";
+
+/** the newpage tag */
+ public static final String NEWPAGE = "newpage";
+
+/** the newpage tag */
+ public static final String NEWLINE = "newline";
+
+/** the annotation tag */
+ public static final String ANNOTATION = "annotation";
+
+/** attribute of the annotation tag */
+ public static String FILE = "file";
+
+/** attribute of the annotation tag */
+ public static String DESTINATION = "destination";
+
+/** attribute of the annotation tag */
+ public static String PAGE = "page";
+
+/** attribute of the annotation tag */
+ public static String NAMED = "named";
+
+/** attribute of the annotation tag */
+ public static String APPLICATION = "application";
+
+/** attribute of the annotation tag */
+ public static String PARAMETERS = "parameters";
+
+/** attribute of the annotation tag */
+ public static String OPERATION = "operation";
+
+/** attribute of the annotation tag */
+ public static String DEFAULTDIR = "defaultdir";
+
+/** attribute of the annotation tag */
+ public static String LLX = "llx";
+
+/** attribute of the annotation tag */
+ public static String LLY = "lly";
+
+/** attribute of the annotation tag */
+ public static String URX = "urx";
+
+/** attribute of the annotation tag */
+ public static String URY = "ury";
+
+/** attribute of the annotation tag */
+ public static final String CONTENT = "content";
+
+ // alignment attribute values
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_LEFT = "Left";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_CENTER = "Center";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_RIGHT = "Right";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_JUSTIFIED = "Justify";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_JUSTIFIED_ALL = "JustifyAll";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_TOP = "Top";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_MIDDLE = "Middle";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_BOTTOM = "Bottom";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_BASELINE = "Baseline";
+
+/** the possible value of an alignment attribute */
+ public static final String DEFAULT = "Default";
+
+/** the possible value of an alignment attribute */
+ public static final String UNKNOWN = "unknown";
+
+/** the possible value of an alignment attribute */
+ public static final String FONT = "font";
+
+/** the possible value of an alignment attribute */
+ public static final String SIZE = "size";
+
+/** the possible value of an alignment attribute */
+ public static final String STYLE = "fontstyle";
+
+/** the possible value of a tag */
+ public static final String HORIZONTALRULE = "horizontalrule";
+
+ /** the possible value of a tag */
+ public static final String PAGE_SIZE = "pagesize";
+
+ /** the possible value of a tag */
+ public static final String ORIENTATION = "orientation";
+
+ // methods
+
+/**
+ * Translates the alignment value.
+ *
+ * @param alignment the alignment value
+ * @return the translated value
+ */
+
+ public static String getAlignment(int alignment) {
+ switch(alignment) {
+ case Element.ALIGN_LEFT:
+ return ALIGN_LEFT;
+ case Element.ALIGN_CENTER:
+ return ALIGN_CENTER;
+ case Element.ALIGN_RIGHT:
+ return ALIGN_RIGHT;
+ case Element.ALIGN_JUSTIFIED:
+ case Element.ALIGN_JUSTIFIED_ALL:
+ return ALIGN_JUSTIFIED;
+ case Element.ALIGN_TOP:
+ return ALIGN_TOP;
+ case Element.ALIGN_MIDDLE:
+ return ALIGN_MIDDLE;
+ case Element.ALIGN_BOTTOM:
+ return ALIGN_BOTTOM;
+ case Element.ALIGN_BASELINE:
+ return ALIGN_BASELINE;
+ default:
+ return DEFAULT;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/Entities.java b/src/main/java/com/lowagie/text/Entities.java
new file mode 100644
index 0000000..ad14e62
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Entities.java
@@ -0,0 +1,388 @@
+/*
+ * $Id: Entities.java,v 1.43 2004/12/14 11:52:46 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.util.HashMap;
+
+/**
+ * This class contains entities that can be used in an entity tag.
+ */
+
+public class Entities {
+
+ /** This is a map that contains all possible id values of the entity tag. */
+ public static final HashMap map;
+
+ static {
+ map = new HashMap();
+ map.put("169", new Integer(227));
+ map.put("172", new Integer(216));
+ map.put("174", new Integer(210));
+ map.put("177", new Integer(177));
+ map.put("215", new Integer(180));
+ map.put("247", new Integer(184));
+ map.put("8230", new Integer(188));
+ map.put("8242", new Integer(162));
+ map.put("8243", new Integer(178));
+ map.put("8260", new Integer(164));
+ map.put("8364", new Integer(240));
+ map.put("8465", new Integer(193));
+ map.put("8472", new Integer(195));
+ map.put("8476", new Integer(194));
+ map.put("8482", new Integer(212));
+ map.put("8501", new Integer(192));
+ map.put("8592", new Integer(172));
+ map.put("8593", new Integer(173));
+ map.put("8594", new Integer(174));
+ map.put("8595", new Integer(175));
+ map.put("8596", new Integer(171));
+ map.put("8629", new Integer(191));
+ map.put("8656", new Integer(220));
+ map.put("8657", new Integer(221));
+ map.put("8658", new Integer(222));
+ map.put("8659", new Integer(223));
+ map.put("8660", new Integer(219));
+ map.put("8704", new Integer(34));
+ map.put("8706", new Integer(182));
+ map.put("8707", new Integer(36));
+ map.put("8709", new Integer(198));
+ map.put("8711", new Integer(209));
+ map.put("8712", new Integer(206));
+ map.put("8713", new Integer(207));
+ map.put("8717", new Integer(39));
+ map.put("8719", new Integer(213));
+ map.put("8721", new Integer(229));
+ map.put("8722", new Integer(45));
+ map.put("8727", new Integer(42));
+ map.put("8729", new Integer(183));
+ map.put("8730", new Integer(214));
+ map.put("8733", new Integer(181));
+ map.put("8734", new Integer(165));
+ map.put("8736", new Integer(208));
+ map.put("8743", new Integer(217));
+ map.put("8744", new Integer(218));
+ map.put("8745", new Integer(199));
+ map.put("8746", new Integer(200));
+ map.put("8747", new Integer(242));
+ map.put("8756", new Integer(92));
+ map.put("8764", new Integer(126));
+ map.put("8773", new Integer(64));
+ map.put("8776", new Integer(187));
+ map.put("8800", new Integer(185));
+ map.put("8801", new Integer(186));
+ map.put("8804", new Integer(163));
+ map.put("8805", new Integer(179));
+ map.put("8834", new Integer(204));
+ map.put("8835", new Integer(201));
+ map.put("8836", new Integer(203));
+ map.put("8838", new Integer(205));
+ map.put("8839", new Integer(202));
+ map.put("8853", new Integer(197));
+ map.put("8855", new Integer(196));
+ map.put("8869", new Integer(94));
+ map.put("8901", new Integer(215));
+ map.put("8992", new Integer(243));
+ map.put("8993", new Integer(245));
+ map.put("9001", new Integer(225));
+ map.put("9002", new Integer(241));
+ map.put("913", new Integer(65));
+ map.put("914", new Integer(66));
+ map.put("915", new Integer(71));
+ map.put("916", new Integer(68));
+ map.put("917", new Integer(69));
+ map.put("918", new Integer(90));
+ map.put("919", new Integer(72));
+ map.put("920", new Integer(81));
+ map.put("921", new Integer(73));
+ map.put("922", new Integer(75));
+ map.put("923", new Integer(76));
+ map.put("924", new Integer(77));
+ map.put("925", new Integer(78));
+ map.put("926", new Integer(88));
+ map.put("927", new Integer(79));
+ map.put("928", new Integer(80));
+ map.put("929", new Integer(82));
+ map.put("931", new Integer(83));
+ map.put("932", new Integer(84));
+ map.put("933", new Integer(85));
+ map.put("934", new Integer(70));
+ map.put("935", new Integer(67));
+ map.put("936", new Integer(89));
+ map.put("937", new Integer(87));
+ map.put("945", new Integer(97));
+ map.put("946", new Integer(98));
+ map.put("947", new Integer(103));
+ map.put("948", new Integer(100));
+ map.put("949", new Integer(101));
+ map.put("950", new Integer(122));
+ map.put("951", new Integer(104));
+ map.put("952", new Integer(113));
+ map.put("953", new Integer(105));
+ map.put("954", new Integer(107));
+ map.put("955", new Integer(108));
+ map.put("956", new Integer(109));
+ map.put("957", new Integer(110));
+ map.put("958", new Integer(120));
+ map.put("959", new Integer(111));
+ map.put("960", new Integer(112));
+ map.put("961", new Integer(114));
+ map.put("962", new Integer(86));
+ map.put("963", new Integer(115));
+ map.put("964", new Integer(116));
+ map.put("965", new Integer(117));
+ map.put("966", new Integer(102));
+ map.put("967", new Integer(99));
+ map.put("9674", new Integer(224));
+ map.put("968", new Integer(121));
+ map.put("969", new Integer(119));
+ map.put("977", new Integer(74));
+ map.put("978", new Integer(161));
+ map.put("981", new Integer(106));
+ map.put("982", new Integer(118));
+ map.put("9824", new Integer(170));
+ map.put("9827", new Integer(167));
+ map.put("9829", new Integer(169));
+ map.put("9830", new Integer(168));
+ map.put("Alpha", new Integer(65));
+ map.put("Beta", new Integer(66));
+ map.put("Chi", new Integer(67));
+ map.put("Delta", new Integer(68));
+ map.put("Epsilon", new Integer(69));
+ map.put("Eta", new Integer(72));
+ map.put("Gamma", new Integer(71));
+ map.put("Iota", new Integer(73));
+ map.put("Kappa", new Integer(75));
+ map.put("Lambda", new Integer(76));
+ map.put("Mu", new Integer(77));
+ map.put("Nu", new Integer(78));
+ map.put("Omega", new Integer(87));
+ map.put("Omicron", new Integer(79));
+ map.put("Phi", new Integer(70));
+ map.put("Pi", new Integer(80));
+ map.put("Prime", new Integer(178));
+ map.put("Psi", new Integer(89));
+ map.put("Rho", new Integer(82));
+ map.put("Sigma", new Integer(83));
+ map.put("Tau", new Integer(84));
+ map.put("Theta", new Integer(81));
+ map.put("Upsilon", new Integer(85));
+ map.put("Xi", new Integer(88));
+ map.put("Zeta", new Integer(90));
+ map.put("alefsym", new Integer(192));
+ map.put("alpha", new Integer(97));
+ map.put("and", new Integer(217));
+ map.put("ang", new Integer(208));
+ map.put("asymp", new Integer(187));
+ map.put("beta", new Integer(98));
+ map.put("cap", new Integer(199));
+ map.put("chi", new Integer(99));
+ map.put("clubs", new Integer(167));
+ map.put("cong", new Integer(64));
+ map.put("copy", new Integer(211));
+ map.put("crarr", new Integer(191));
+ map.put("cup", new Integer(200));
+ map.put("dArr", new Integer(223));
+ map.put("darr", new Integer(175));
+ map.put("delta", new Integer(100));
+ map.put("diams", new Integer(168));
+ map.put("divide", new Integer(184));
+ map.put("empty", new Integer(198));
+ map.put("epsilon", new Integer(101));
+ map.put("equiv", new Integer(186));
+ map.put("eta", new Integer(104));
+ map.put("euro", new Integer(240));
+ map.put("exist", new Integer(36));
+ map.put("forall", new Integer(34));
+ map.put("frasl", new Integer(164));
+ map.put("gamma", new Integer(103));
+ map.put("ge", new Integer(179));
+ map.put("hArr", new Integer(219));
+ map.put("harr", new Integer(171));
+ map.put("hearts", new Integer(169));
+ map.put("hellip", new Integer(188));
+ map.put("horizontal arrow extender", new Integer(190));
+ map.put("image", new Integer(193));
+ map.put("infin", new Integer(165));
+ map.put("int", new Integer(242));
+ map.put("iota", new Integer(105));
+ map.put("isin", new Integer(206));
+ map.put("kappa", new Integer(107));
+ map.put("lArr", new Integer(220));
+ map.put("lambda", new Integer(108));
+ map.put("lang", new Integer(225));
+ map.put("large brace extender", new Integer(239));
+ map.put("large integral extender", new Integer(244));
+ map.put("large left brace (bottom)", new Integer(238));
+ map.put("large left brace (middle)", new Integer(237));
+ map.put("large left brace (top)", new Integer(236));
+ map.put("large left bracket (bottom)", new Integer(235));
+ map.put("large left bracket (extender)", new Integer(234));
+ map.put("large left bracket (top)", new Integer(233));
+ map.put("large left parenthesis (bottom)", new Integer(232));
+ map.put("large left parenthesis (extender)", new Integer(231));
+ map.put("large left parenthesis (top)", new Integer(230));
+ map.put("large right brace (bottom)", new Integer(254));
+ map.put("large right brace (middle)", new Integer(253));
+ map.put("large right brace (top)", new Integer(252));
+ map.put("large right bracket (bottom)", new Integer(251));
+ map.put("large right bracket (extender)", new Integer(250));
+ map.put("large right bracket (top)", new Integer(249));
+ map.put("large right parenthesis (bottom)", new Integer(248));
+ map.put("large right parenthesis (extender)", new Integer(247));
+ map.put("large right parenthesis (top)", new Integer(246));
+ map.put("larr", new Integer(172));
+ map.put("le", new Integer(163));
+ map.put("lowast", new Integer(42));
+ map.put("loz", new Integer(224));
+ map.put("minus", new Integer(45));
+ map.put("mu", new Integer(109));
+ map.put("nabla", new Integer(209));
+ map.put("ne", new Integer(185));
+ map.put("not", new Integer(216));
+ map.put("notin", new Integer(207));
+ map.put("nsub", new Integer(203));
+ map.put("nu", new Integer(110));
+ map.put("omega", new Integer(119));
+ map.put("omicron", new Integer(111));
+ map.put("oplus", new Integer(197));
+ map.put("or", new Integer(218));
+ map.put("otimes", new Integer(196));
+ map.put("part", new Integer(182));
+ map.put("perp", new Integer(94));
+ map.put("phi", new Integer(102));
+ map.put("pi", new Integer(112));
+ map.put("piv", new Integer(118));
+ map.put("plusmn", new Integer(177));
+ map.put("prime", new Integer(162));
+ map.put("prod", new Integer(213));
+ map.put("prop", new Integer(181));
+ map.put("psi", new Integer(121));
+ map.put("rArr", new Integer(222));
+ map.put("radic", new Integer(214));
+ map.put("radical extender", new Integer(96));
+ map.put("rang", new Integer(241));
+ map.put("rarr", new Integer(174));
+ map.put("real", new Integer(194));
+ map.put("reg", new Integer(210));
+ map.put("rho", new Integer(114));
+ map.put("sdot", new Integer(215));
+ map.put("sigma", new Integer(115));
+ map.put("sigmaf", new Integer(86));
+ map.put("sim", new Integer(126));
+ map.put("spades", new Integer(170));
+ map.put("sub", new Integer(204));
+ map.put("sube", new Integer(205));
+ map.put("sum", new Integer(229));
+ map.put("sup", new Integer(201));
+ map.put("supe", new Integer(202));
+ map.put("tau", new Integer(116));
+ map.put("there4", new Integer(92));
+ map.put("theta", new Integer(113));
+ map.put("thetasym", new Integer(74));
+ map.put("times", new Integer(180));
+ map.put("trade", new Integer(212));
+ map.put("uArr", new Integer(221));
+ map.put("uarr", new Integer(173));
+ map.put("upsih", new Integer(161));
+ map.put("upsilon", new Integer(117));
+ map.put("vertical arrow extender", new Integer(189));
+ map.put("weierp", new Integer(195));
+ map.put("xi", new Integer(120));
+ map.put("zeta", new Integer(122));
+ }
+
+ /**
+ * Gets a chunk with a symbol character.
+ * @param e a symbol value (see Entities class: alfa is greek alfa,...)
+ * @param font the font if the symbol isn't found (otherwise Font.SYMBIL)
+ * @return a Chunk
+ */
+
+ public static Chunk get(String e, Font font) {
+ int s = getCorrespondingSymbol(e);
+ if (s == -1) {
+ try {
+ return new Chunk(String.valueOf((char)Integer.parseInt(e)), font);
+ }
+ catch(Exception exception) {
+ return new Chunk(e, font);
+ }
+ }
+ Font symbol = new Font(Font.SYMBOL, font.size(), font.style(), font.color());
+ return new Chunk(String.valueOf((char)s), symbol);
+ }
+
+/**
+ * Looks for the corresponding symbol in the font Symbol.
+ *
+ * @param c the original ASCII-char
+ * @return the corresponding symbol in font Symbol
+ */
+
+ public static int getCorrespondingSymbol(String c) {
+ Integer integer = (Integer) map.get(c);
+ if (integer == null) {
+ return -1;
+ }
+ return integer.intValue();
+ }
+
+/**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.ENTITY.equals(tag);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/ExceptionConverter.java b/src/main/java/com/lowagie/text/ExceptionConverter.java
new file mode 100644
index 0000000..8d25395
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ExceptionConverter.java
@@ -0,0 +1,135 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text;
+
+/**
+ * The ExceptionConverter changes a checked exception into an
+ * unchecked exception.
+ */
+public class ExceptionConverter extends RuntimeException {
+ /** we keep a handle to the wrapped exception */
+ private Exception ex;
+ /** prefix for the exception */
+ private String prefix;
+
+ /**
+ * Construct a RuntimeException based on another Exception
+ * @param ex the exception that has to be turned into a RuntimeException
+ */
+ public ExceptionConverter(Exception ex) {
+ this.ex = ex;
+ prefix = (ex instanceof RuntimeException) ? "" : "ExceptionConverter: ";
+ }
+
+ /**
+ * and allow the user of ExceptionConverter to get a handle to it.
+ * @return the original exception
+ */
+ public Exception getException() {
+ return ex;
+ }
+
+ /**
+ * We print the message of the checked exception
+ * @return message of the original exception
+ */
+ public String getMessage() {
+ return ex.getMessage();
+ }
+
+ /**
+ * and make sure we also produce a localized version
+ * @return localized version of the message
+ */
+ public String getLocalizedMessage() {
+ return ex.getLocalizedMessage();
+ }
+
+ /**
+ * The toString() is changed to be prefixed with ExceptionConverter
+ * @return Stringversion of the exception
+ */
+ public String toString() {
+ return prefix + ex;
+ }
+
+ /** we have to override this as well */
+ public void printStackTrace() {
+ printStackTrace(System.err);
+ }
+
+ /**
+ * here we prefix, with s.print(), not s.println(), the stack
+ * trace with "ExceptionConverter:"
+ * @param s
+ */
+ public void printStackTrace(java.io.PrintStream s) {
+ synchronized (s) {
+ s.print(prefix);
+ ex.printStackTrace(s);
+ }
+ }
+
+ /**
+ * Again, we prefix the stack trace with "ExceptionConverter:"
+ * @param s
+ */
+ public void printStackTrace(java.io.PrintWriter s) {
+ synchronized (s) {
+ s.print(prefix);
+ ex.printStackTrace(s);
+ }
+ }
+
+ /**
+ * requests to fill in the stack trace we will have to ignore.
+ * We can't throw an exception here, because this method
+ * is called by the constructor of Throwable
+ * @return a Throwable
+ */
+ public Throwable fillInStackTrace() {
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/Font.java b/src/main/java/com/lowagie/text/Font.java
new file mode 100644
index 0000000..412a833
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Font.java
@@ -0,0 +1,806 @@
+/*
+ * $Id: Font.java,v 1.94 2005/10/05 14:16:04 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.awt.Color;
+
+import com.lowagie.text.pdf.BaseFont;
+import com.lowagie.text.markup.MarkupTags;
+
+/**
+ * Contains all the specifications of a font: fontfamily, size, style and color.
+ *
+ *
+ *
+ */
+
+public class Font implements Comparable {
+
+ // static membervariables for the different families
+
+ /** a possible value of a font family. */
+ public static final int COURIER = 0;
+
+ /** a possible value of a font family. */
+ public static final int HELVETICA = 1;
+
+ /** a possible value of a font family. */
+ public static final int TIMES_ROMAN = 2;
+
+ /** a possible value of a font family. */
+ public static final int SYMBOL = 3;
+
+ /** a possible value of a font family. */
+ public static final int ZAPFDINGBATS = 4;
+
+ // static membervariables for the different styles
+
+ /** this is a possible style. */
+ public static final int NORMAL = 0;
+
+ /** this is a possible style. */
+ public static final int BOLD = 1;
+
+ /** this is a possible style. */
+ public static final int ITALIC = 2;
+
+ /** this is a possible style. */
+ public static final int UNDERLINE = 4;
+
+ /** this is a possible style. */
+ public static final int STRIKETHRU = 8;
+
+ /** this is a possible style. */
+ public static final int BOLDITALIC = BOLD | ITALIC;
+
+ // static membervariables
+
+ /** the value of an undefined attribute. */
+ public static final int UNDEFINED = -1;
+
+ /** the value of the default size. */
+ public static final int DEFAULTSIZE = 12;
+
+ // membervariables
+
+ /** the value of the fontfamily. */
+ private int family = UNDEFINED;
+
+ /** the value of the fontsize. */
+ private float size = UNDEFINED;
+
+ /** the value of the style. */
+ private int style = UNDEFINED;
+
+ /** the value of the color. */
+ private Color color = null;
+
+ /** the external font */
+ private BaseFont baseFont = null;
+
+ // constructors
+
+ /**
+ * Copy constructor of a Font
+ *
+ * @param other
+ * the font that has to be copied
+ */
+ public Font(Font other) {
+ this.color = other.color;
+ this.family = other.family;
+ this.size = other.size;
+ this.style = other.style;
+ this.baseFont = other.baseFont;
+ }
+
+ /**
+ * Constructs a Font.
+ *
+ * @param family
+ * the family to which this font belongs
+ * @param size
+ * the size of this font
+ * @param style
+ * the style of this font
+ * @param color
+ * the
+ *
+ * Paragraph p = new Paragraph("This is a paragraph", new
+ * Font(Font.HELVETICA, 18, Font.BOLDITALIC, new Color(0, 0, 255)) );
+ *
+ *
+ *
+ * Color
of this font.
+ */
+
+ public Font(int family, float size, int style, Color color) {
+ this.family = family;
+ this.size = size;
+ this.style = style;
+ this.color = color;
+ }
+
+ /**
+ * Constructs a Font.
+ *
+ * @param bf
+ * the external font
+ * @param size
+ * the size of this font
+ * @param style
+ * the style of this font
+ * @param color
+ * the Color
of this font.
+ */
+
+ public Font(BaseFont bf, float size, int style, Color color) {
+ this.baseFont = bf;
+ this.size = size;
+ this.style = style;
+ this.color = color;
+ }
+
+ /**
+ * Constructs a Font.
+ *
+ * @param bf
+ * the external font
+ * @param size
+ * the size of this font
+ * @param style
+ * the style of this font
+ */
+ public Font(BaseFont bf, float size, int style) {
+ this(bf, size, style, null);
+ }
+
+ /**
+ * Constructs a Font.
+ *
+ * @param bf
+ * the external font
+ * @param size
+ * the size of this font
+ */
+ public Font(BaseFont bf, float size) {
+ this(bf, size, UNDEFINED, null);
+ }
+
+ /**
+ * Constructs a Font.
+ *
+ * @param bf
+ * the external font
+ */
+ public Font(BaseFont bf) {
+ this(bf, UNDEFINED, UNDEFINED, null);
+ }
+
+ /**
+ * Constructs a Font.
+ *
+ * @param family
+ * the family to which this font belongs
+ * @param size
+ * the size of this font
+ * @param style
+ * the style of this font
+ */
+
+ public Font(int family, float size, int style) {
+ this(family, size, style, null);
+ }
+
+ /**
+ * Constructs a Font.
+ *
+ * @param family
+ * the family to which this font belongs
+ * @param size
+ * the size of this font
+ */
+
+ public Font(int family, float size) {
+ this(family, size, UNDEFINED, null);
+ }
+
+ /**
+ * Constructs a Font.
+ *
+ * @param family
+ * the family to which this font belongs
+ */
+
+ public Font(int family) {
+ this(family, UNDEFINED, UNDEFINED, null);
+ }
+
+ /**
+ * Constructs a Font.
+ */
+
+ public Font() {
+ this(UNDEFINED, UNDEFINED, UNDEFINED, null);
+ }
+
+ // implementation of the Comparable interface
+
+ /**
+ * Compares this Font
with another
+ *
+ * @param object
+ * the other Font
+ * @return a value
+ */
+
+ public int compareTo(Object object) {
+ if (object == null) {
+ return -1;
+ }
+ Font font;
+ try {
+ font = (Font) object;
+ if (baseFont != null && !baseFont.equals(font.getBaseFont())) {
+ return -2;
+ }
+ if (this.family != font.family()) {
+ return 1;
+ }
+ if (this.size != font.size()) {
+ return 2;
+ }
+ if (this.style != font.style()) {
+ return 3;
+ }
+ if (this.color == null) {
+ if (font.color == null) {
+ return 0;
+ }
+ return 4;
+ }
+ if (font.color == null) {
+ return 4;
+ }
+ if (this.color.equals(font.color())) {
+ return 0;
+ }
+ return 4;
+ } catch (ClassCastException cce) {
+ return -3;
+ }
+ }
+
+ // methods
+
+ /**
+ * Sets the family using a String
("Courier", "Helvetica",
+ * "Times New Roman", "Symbol" or "ZapfDingbats").
+ *
+ * @param family
+ * A String
representing a certain font-family.
+ */
+
+ public void setFamily(String family) {
+ this.family = getFamilyIndex(family);
+ }
+
+ /**
+ * Translates a String
-value of a certain family into the
+ * index that is used for this family in this class.
+ *
+ * @param family
+ * A String
representing a certain font-family
+ * @return the corresponding index
+ */
+
+ public static int getFamilyIndex(String family) {
+ if (family.equalsIgnoreCase(FontFactory.COURIER)) {
+ return COURIER;
+ }
+ if (family.equalsIgnoreCase(FontFactory.HELVETICA)) {
+ return HELVETICA;
+ }
+ if (family.equalsIgnoreCase(FontFactory.TIMES_ROMAN)) {
+ return TIMES_ROMAN;
+ }
+ if (family.equalsIgnoreCase(FontFactory.SYMBOL)) {
+ return SYMBOL;
+ }
+ if (family.equalsIgnoreCase(FontFactory.ZAPFDINGBATS)) {
+ return ZAPFDINGBATS;
+ }
+ return UNDEFINED;
+ }
+
+ /**
+ * Gets the familyname as a String.
+ *
+ * @return the familyname
+ */
+
+ public String getFamilyname() {
+ String tmp = "unknown";
+ switch (family()) {
+ case Font.COURIER:
+ return FontFactory.COURIER;
+ case Font.HELVETICA:
+ return FontFactory.HELVETICA;
+ case Font.TIMES_ROMAN:
+ return FontFactory.TIMES_ROMAN;
+ case Font.SYMBOL:
+ return FontFactory.SYMBOL;
+ case Font.ZAPFDINGBATS:
+ return FontFactory.ZAPFDINGBATS;
+ default:
+ if (baseFont != null) {
+ String[][] names = baseFont.getFamilyFontName();
+ for (int i = 0; i < names.length; i++) {
+ if ("0".equals(names[i][2])) {
+ return names[i][3];
+ }
+ if ("1033".equals(names[i][2])) {
+ tmp = names[i][3];
+ }
+ if ("".equals(names[i][2])) {
+ tmp = names[i][3];
+ }
+ }
+ }
+ }
+ return tmp;
+ }
+
+ /**
+ * Sets the size.
+ *
+ * @param size
+ * The new size of the font.
+ */
+
+ public void setSize(float size) {
+ this.size = size;
+ }
+
+ /**
+ * Sets the style using a String
containing one of more of
+ * the following values: normal, bold, italic, underline, strike.
+ *
+ * @param style
+ * A String
representing a certain style.
+ */
+
+ public void setStyle(String style) {
+ if (this.style == UNDEFINED)
+ this.style = NORMAL;
+ this.style |= getStyleValue(style);
+ }
+
+ /**
+ * Sets the style.
+ *
+ * @param style
+ * the style.
+ */
+
+ public void setStyle(int style) {
+ if (this.style == UNDEFINED)
+ this.style = NORMAL;
+ this.style |= style;
+ }
+
+ /**
+ * Translates a String
-value of a certain style into the
+ * index value is used for this style in this class.
+ *
+ * @param style
+ * A String
+ * @return the corresponding value
+ */
+
+ public static int getStyleValue(String style) {
+ int s = 0;
+ if (style.indexOf(MarkupTags.CSS_VALUE_NORMAL) != -1) {
+ s |= NORMAL;
+ }
+ if (style.indexOf(MarkupTags.CSS_VALUE_BOLD) != -1) {
+ s |= BOLD;
+ }
+ if (style.indexOf(MarkupTags.CSS_VALUE_ITALIC) != -1) {
+ s |= ITALIC;
+ }
+ if (style.indexOf(MarkupTags.CSS_VALUE_OBLIQUE) != -1) {
+ s |= ITALIC;
+ }
+ if (style.indexOf(MarkupTags.CSS_VALUE_UNDERLINE) != -1) {
+ s |= UNDERLINE;
+ }
+ if (style.indexOf(MarkupTags.CSS_VALUE_LINETHROUGH) != -1) {
+ s |= STRIKETHRU;
+ }
+ return s;
+ }
+
+ /**
+ * Sets the color.
+ *
+ * @param color
+ * the new color of the font
+ */
+
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ /**
+ * Sets the color.
+ *
+ * @param red
+ * the red-value of the new color
+ * @param green
+ * the green-value of the new color
+ * @param blue
+ * the blue-value of the new color
+ */
+
+ public void setColor(int red, int green, int blue) {
+ this.color = new Color(red, green, blue);
+ }
+
+ /**
+ * Gets the leading that can be used with this font.
+ *
+ * @param linespacing
+ * a certain linespacing
+ * @return the height of a line
+ */
+
+ public float leading(float linespacing) {
+ if (size == UNDEFINED) {
+ return linespacing * DEFAULTSIZE;
+ }
+ return linespacing * size;
+ }
+
+ /**
+ * Checks if the properties of this font are undefined or null.
+ * boolean
+ */
+
+ public boolean isStandardFont() {
+ return (family == UNDEFINED && size == UNDEFINED && style == UNDEFINED
+ && color == null && baseFont == null);
+ }
+
+ /**
+ * Replaces the attributes that are equal to null with the
+ * attributes of a given font.
+ *
+ * @param font
+ * the font of a bigger element class
+ * @return a Font
+ */
+
+ public Font difference(Font font) {
+ // size
+ float dSize = font.size;
+ if (dSize == UNDEFINED) {
+ dSize = this.size;
+ }
+ // style
+ int dStyle = UNDEFINED;
+ int style1 = this.style;
+ int style2 = font.style();
+ if (style1 != UNDEFINED || style2 != UNDEFINED) {
+ if (style1 == UNDEFINED)
+ style1 = 0;
+ if (style2 == UNDEFINED)
+ style2 = 0;
+ dStyle = style1 | style2;
+ }
+ // color
+ Color dColor = font.color;
+ if (dColor == null) {
+ dColor = this.color;
+ }
+ // family
+ if (font.baseFont != null) {
+ return new Font(font.baseFont, dSize, dStyle, dColor);
+ }
+ if (font.family() != UNDEFINED) {
+ return new Font(font.family, dSize, dStyle, dColor);
+ }
+ if (this.baseFont != null) {
+ if (dStyle == style1) {
+ return new Font(this.baseFont, dSize, dStyle, dColor);
+ } else {
+ return FontFactory.getFont(this.getFamilyname(), dSize, dStyle,
+ dColor);
+ }
+ }
+ return new Font(this.family, dSize, dStyle, dColor);
+ }
+
+ // methods to retrieve the membervariables
+
+ /**
+ * Gets the family of this font.
+ *
+ * @return the value of the family
+ */
+
+ public int family() {
+ return family;
+ }
+
+ /**
+ * Gets the size of this font.
+ *
+ * @return a size
+ */
+
+ public float size() {
+ return size;
+ }
+
+ /**
+ * Gets the style of this font.
+ *
+ * @return a size
+ */
+
+ public int style() {
+ return style;
+ }
+
+ /**
+ * checks if this font is Bold.
+ *
+ * @return a boolean
+ */
+
+ public boolean isBold() {
+ if (style == UNDEFINED) {
+ return false;
+ }
+ return (style & BOLD) == BOLD;
+ }
+
+ /**
+ * checks if this font is Bold.
+ *
+ * @return a boolean
+ */
+
+ public boolean isItalic() {
+ if (style == UNDEFINED) {
+ return false;
+ }
+ return (style & ITALIC) == ITALIC;
+ }
+
+ /**
+ * checks if this font is underlined.
+ *
+ * @return a boolean
+ */
+
+ public boolean isUnderlined() {
+ if (style == UNDEFINED) {
+ return false;
+ }
+ return (style & UNDERLINE) == UNDERLINE;
+ }
+
+ /**
+ * checks if the style of this font is STRIKETHRU.
+ *
+ * @return a boolean
+ */
+
+ public boolean isStrikethru() {
+ if (style == UNDEFINED) {
+ return false;
+ }
+ return (style & STRIKETHRU) == STRIKETHRU;
+ }
+
+ /**
+ * Gets the color of this font.
+ *
+ * @return a color
+ */
+
+ public Color color() {
+ return color;
+ }
+
+ /**
+ * Gets the BaseFont
inside this object.
+ *
+ * @return the BaseFont
+ */
+
+ public BaseFont getBaseFont() {
+ return baseFont;
+ }
+
+ /**
+ * Gets the BaseFont
this class represents. For the built-in
+ * fonts a BaseFont
is calculated.
+ *
+ * @param specialEncoding
+ * true
to use the special encoding for Symbol and
+ * ZapfDingbats, false
to always use Cp1252
+ *
+ * @return the BaseFont
this class represents
+ */
+ public BaseFont getCalculatedBaseFont(boolean specialEncoding) {
+ if (baseFont != null)
+ return baseFont;
+ int style = this.style;
+ if (style == UNDEFINED) {
+ style = NORMAL;
+ }
+ String fontName = BaseFont.HELVETICA;
+ String encoding = BaseFont.WINANSI;
+ BaseFont cfont = null;
+ switch (family) {
+ case COURIER:
+ switch (style & BOLDITALIC) {
+ case BOLD:
+ fontName = BaseFont.COURIER_BOLD;
+ break;
+ case ITALIC:
+ fontName = BaseFont.COURIER_OBLIQUE;
+ break;
+ case BOLDITALIC:
+ fontName = BaseFont.COURIER_BOLDOBLIQUE;
+ break;
+ default:
+ //case NORMAL:
+ fontName = BaseFont.COURIER;
+ break;
+ }
+ break;
+ case TIMES_ROMAN:
+ switch (style & BOLDITALIC) {
+ case BOLD:
+ fontName = BaseFont.TIMES_BOLD;
+ break;
+ case ITALIC:
+ fontName = BaseFont.TIMES_ITALIC;
+ break;
+ case BOLDITALIC:
+ fontName = BaseFont.TIMES_BOLDITALIC;
+ break;
+ default:
+ case NORMAL:
+ fontName = BaseFont.TIMES_ROMAN;
+ break;
+ }
+ break;
+ case SYMBOL:
+ fontName = BaseFont.SYMBOL;
+ if (specialEncoding)
+ encoding = BaseFont.SYMBOL;
+ break;
+ case ZAPFDINGBATS:
+ fontName = BaseFont.ZAPFDINGBATS;
+ if (specialEncoding)
+ encoding = BaseFont.ZAPFDINGBATS;
+ break;
+ default:
+ case Font.HELVETICA:
+ switch (style & BOLDITALIC) {
+ case BOLD:
+ fontName = BaseFont.HELVETICA_BOLD;
+ break;
+ case ITALIC:
+ fontName = BaseFont.HELVETICA_OBLIQUE;
+ break;
+ case BOLDITALIC:
+ fontName = BaseFont.HELVETICA_BOLDOBLIQUE;
+ break;
+ default:
+ case NORMAL:
+ fontName = BaseFont.HELVETICA;
+ break;
+ }
+ break;
+ }
+ try {
+ cfont = BaseFont.createFont(fontName, encoding, false);
+ } catch (Exception ee) {
+ throw new ExceptionConverter(ee);
+ }
+ return cfont;
+ }
+
+ /**
+ * Gets the style that can be used with the calculated BaseFont
+ *
.
+ *
+ * @return the style that can be used with the calculated BaseFont
+ *
+ */
+ public int getCalculatedStyle() {
+ int style = this.style;
+ if (style == UNDEFINED) {
+ style = NORMAL;
+ }
+ if (baseFont != null)
+ return style;
+ if (family == SYMBOL || family == ZAPFDINGBATS)
+ return style;
+ else
+ return style & (~BOLDITALIC);
+ }
+
+ /**
+ * Gets the size that can be used with the calculated BaseFont
+ *
.
+ *
+ * @return the size that can be used with the calculated BaseFont
+ *
+ */
+ public float getCalculatedSize() {
+ float s = this.size;
+ if (s == UNDEFINED) {
+ s = DEFAULTSIZE;
+ }
+ return s;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/FontFactory.java b/src/main/java/com/lowagie/text/FontFactory.java
new file mode 100644
index 0000000..4d96836
--- /dev/null
+++ b/src/main/java/com/lowagie/text/FontFactory.java
@@ -0,0 +1,429 @@
+/*
+ * $Id: FontFactory.java,v 1.73 2006/05/29 11:00:30 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Enumeration;
+import java.io.File;
+import com.lowagie.text.pdf.BaseFont;
+import com.lowagie.text.markup.MarkupTags;
+import com.lowagie.text.markup.MarkupParser;
+
+/**
+ * If you are using True Type fonts, you can declare the paths of the different ttf- and ttc-files
+ * to this static class first and then create fonts in your code using one of the static getFont-method
+ * without having to enter a path as parameter.
+ *
+ * @author Bruno Lowagie
+ */
+
+public class FontFactory {
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String COURIER = BaseFont.COURIER;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String COURIER_BOLD = BaseFont.COURIER_BOLD;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String COURIER_OBLIQUE = BaseFont.COURIER_OBLIQUE;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String COURIER_BOLDOBLIQUE = BaseFont.COURIER_BOLDOBLIQUE;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String HELVETICA = BaseFont.HELVETICA;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String HELVETICA_BOLD = BaseFont.HELVETICA_BOLD;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String HELVETICA_OBLIQUE = BaseFont.HELVETICA_OBLIQUE;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String HELVETICA_BOLDOBLIQUE = BaseFont.HELVETICA_BOLDOBLIQUE;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String SYMBOL = BaseFont.SYMBOL;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String TIMES = "Times";
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String TIMES_ROMAN = BaseFont.TIMES_ROMAN;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String TIMES_BOLD = BaseFont.TIMES_BOLD;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String TIMES_ITALIC = BaseFont.TIMES_ITALIC;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String TIMES_BOLDITALIC = BaseFont.TIMES_BOLDITALIC;
+
+/** This is a possible value of a base 14 type 1 font */
+ public static final String ZAPFDINGBATS = BaseFont.ZAPFDINGBATS;
+
+ private static FontFactoryImp fontImp = new FontFactoryImp();
+
+/** This is the default encoding to use. */
+ public static String defaultEncoding = BaseFont.WINANSI;
+
+/** This is the default value of the embedded variable. */
+ public static boolean defaultEmbedding = BaseFont.NOT_EMBEDDED;
+
+/** Creates new FontFactory */
+ private FontFactory() {
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @param size the size of this font
+ * @param style the style of this font
+ * @param color the Color
of this font.
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, String encoding, boolean embedded, float size, int style, Color color) {
+ return fontImp.getFont(fontname, encoding, embedded, size, style, color);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @param size the size of this font
+ * @param style the style of this font
+ * @param color the Color
of this font.
+ * @param cached true if the font comes from the cache or is added to
+ * the cache if new, false if the font is always created new
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, String encoding, boolean embedded, float size, int style, Color color, boolean cached) {
+ return fontImp.getFont(fontname, encoding, embedded, size, style, color, cached);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param attributes the attributes of a Font
object.
+ * @return the Font constructed based on the attributes
+ */
+
+ public static Font getFont(Properties attributes) {
+ fontImp.defaultEmbedding = defaultEmbedding;
+ fontImp.defaultEncoding = defaultEncoding;
+ return fontImp.getFont(attributes);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @param size the size of this font
+ * @param style the style of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, String encoding, boolean embedded, float size, int style) {
+ return getFont(fontname, encoding, embedded, size, style, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @param size the size of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, String encoding, boolean embedded, float size) {
+ return getFont(fontname, encoding, embedded, size, Font.UNDEFINED, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, String encoding, boolean embedded) {
+ return getFont(fontname, encoding, embedded, Font.UNDEFINED, Font.UNDEFINED, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param size the size of this font
+ * @param style the style of this font
+ * @param color the Color
of this font.
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, String encoding, float size, int style, Color color) {
+ return getFont(fontname, encoding, defaultEmbedding, size, style, color);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param size the size of this font
+ * @param style the style of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, String encoding, float size, int style) {
+ return getFont(fontname, encoding, defaultEmbedding, size, style, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param size the size of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, String encoding, float size) {
+ return getFont(fontname, encoding, defaultEmbedding, size, Font.UNDEFINED, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, String encoding) {
+ return getFont(fontname, encoding, defaultEmbedding, Font.UNDEFINED, Font.UNDEFINED, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param size the size of this font
+ * @param style the style of this font
+ * @param color the Color
of this font.
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, float size, int style, Color color) {
+ return getFont(fontname, defaultEncoding, defaultEmbedding, size, style, color);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param size the size of this font
+ * @param style the style of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, float size, int style) {
+ return getFont(fontname, defaultEncoding, defaultEmbedding, size, style, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param size the size of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname, float size) {
+ return getFont(fontname, defaultEncoding, defaultEmbedding, size, Font.UNDEFINED, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @return the Font constructed based on the parameters
+ */
+
+ public static Font getFont(String fontname) {
+ return getFont(fontname, defaultEncoding, defaultEmbedding, Font.UNDEFINED, Font.UNDEFINED, null);
+ }
+
+ /**
+ * Register a font by giving explicitly the font family and name.
+ * @param familyName the font family
+ * @param fullName the font name
+ * @param path the font path
+ */
+ public void registerFamily(String familyName, String fullName, String path) {
+ fontImp.registerFamily(familyName, fullName, path);
+ }
+
+/**
+ * Register a ttf- or a ttc-file.
+ *
+ * @param path the path to a ttf- or ttc-file
+ */
+
+ public static void register(String path) {
+ register(path, null);
+ }
+
+/**
+ * Register a font file and use an alias for the font contained in it.
+ *
+ * @param path the path to a font file
+ * @param alias the alias you want to use for the font
+ */
+
+ public static void register(String path, String alias) {
+ fontImp.register(path, alias);
+ }
+
+ /** Register all the fonts in a directory.
+ * @param dir the directory
+ * @return the number of fonts registered
+ */
+ public static int registerDirectory(String dir) {
+ return fontImp.registerDirectory(dir);
+ }
+
+ /** Register fonts in some probable directories. It usually works in Windows,
+ * Linux and Solaris.
+ * @return the number of fonts registered
+ */
+ public static int registerDirectories() {
+ return fontImp.registerDirectories();
+ }
+
+/**
+ * Gets a set of registered fontnames.
+ * @return a set of registered fonts
+ */
+
+ public static Set getRegisteredFonts() {
+ return fontImp.getRegisteredFonts();
+ }
+
+/**
+ * Gets a set of registered fontnames.
+ * @return a set of registered font families
+ */
+
+ public static Set getRegisteredFamilies() {
+ return fontImp.getRegisteredFamilies();
+ }
+
+/**
+ * Gets a set of registered fontnames.
+ * @param fontname of a font that may or may not be registered
+ * @return true if a given font is registered
+ */
+
+ public static boolean contains(String fontname) {
+ return fontImp.isRegistered(fontname);
+ }
+
+/**
+ * Checks if a certain font is registered.
+ *
+ * @param fontname the name of the font that has to be checked.
+ * @return true if the font is found
+ */
+
+ public static boolean isRegistered(String fontname) {
+ return fontImp.isRegistered(fontname);
+ }
+
+ /**
+ * Gets the font factory implementation.
+ * @return the font factory implementation
+ */
+ public static FontFactoryImp getFontImp() {
+ return fontImp;
+ }
+
+ /**
+ * Sets the font factory implementation.
+ * @param fontImp the font factory implementation
+ */
+ public static void setFontImp(FontFactoryImp fontImp) {
+ if (fontImp == null)
+ throw new NullPointerException("FontFactoryImp cannot be null.");
+ FontFactory.fontImp = fontImp;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/FontFactoryImp.java b/src/main/java/com/lowagie/text/FontFactoryImp.java
new file mode 100644
index 0000000..b9ae4c1
--- /dev/null
+++ b/src/main/java/com/lowagie/text/FontFactoryImp.java
@@ -0,0 +1,659 @@
+/*
+ * $Id: FontFactoryImp.java,v 1.5 2006/06/23 12:54:11 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Enumeration;
+import java.io.File;
+import com.lowagie.text.pdf.BaseFont;
+import com.lowagie.text.markup.MarkupTags;
+import com.lowagie.text.markup.MarkupParser;
+
+/**
+ * If you are using True Type fonts, you can declare the paths of the different ttf- and ttc-files
+ * to this class first and then create fonts in your code using one of the getFont method
+ * without having to enter a path as parameter.
+ *
+ * @author Bruno Lowagie
+ */
+
+public class FontFactoryImp {
+
+/** This is a map of postscriptfontnames of True Type fonts and the path of their ttf- or ttc-file. */
+ private Properties trueTypeFonts = new Properties();
+
+ private static String[] TTFamilyOrder = {
+ "3", "1", "1033",
+ "3", "0", "1033",
+ "1", "0", "0",
+ "0", "3", "0"
+ };
+
+/** This is a map of fontfamilies. */
+ private Hashtable fontFamilies = new Hashtable();
+
+/** This is the default encoding to use. */
+ public String defaultEncoding = BaseFont.WINANSI;
+
+/** This is the default value of the embedded variable. */
+ public boolean defaultEmbedding = BaseFont.NOT_EMBEDDED;
+
+/** Creates new FontFactory */
+ public FontFactoryImp() {
+ trueTypeFonts.setProperty(FontFactory.COURIER.toLowerCase(), FontFactory.COURIER);
+ trueTypeFonts.setProperty(FontFactory.COURIER_BOLD.toLowerCase(), FontFactory.COURIER_BOLD);
+ trueTypeFonts.setProperty(FontFactory.COURIER_OBLIQUE.toLowerCase(), FontFactory.COURIER_OBLIQUE);
+ trueTypeFonts.setProperty(FontFactory.COURIER_BOLDOBLIQUE.toLowerCase(), FontFactory.COURIER_BOLDOBLIQUE);
+ trueTypeFonts.setProperty(FontFactory.HELVETICA.toLowerCase(), FontFactory.HELVETICA);
+ trueTypeFonts.setProperty(FontFactory.HELVETICA_BOLD.toLowerCase(), FontFactory.HELVETICA_BOLD);
+ trueTypeFonts.setProperty(FontFactory.HELVETICA_OBLIQUE.toLowerCase(), FontFactory.HELVETICA_OBLIQUE);
+ trueTypeFonts.setProperty(FontFactory.HELVETICA_BOLDOBLIQUE.toLowerCase(), FontFactory.HELVETICA_BOLDOBLIQUE);
+ trueTypeFonts.setProperty(FontFactory.SYMBOL.toLowerCase(), FontFactory.SYMBOL);
+ trueTypeFonts.setProperty(FontFactory.TIMES_ROMAN.toLowerCase(), FontFactory.TIMES_ROMAN);
+ trueTypeFonts.setProperty(FontFactory.TIMES_BOLD.toLowerCase(), FontFactory.TIMES_BOLD);
+ trueTypeFonts.setProperty(FontFactory.TIMES_ITALIC.toLowerCase(), FontFactory.TIMES_ITALIC);
+ trueTypeFonts.setProperty(FontFactory.TIMES_BOLDITALIC.toLowerCase(), FontFactory.TIMES_BOLDITALIC);
+ trueTypeFonts.setProperty(FontFactory.ZAPFDINGBATS.toLowerCase(), FontFactory.ZAPFDINGBATS);
+
+ ArrayList tmp;
+ tmp = new ArrayList();
+ tmp.add(FontFactory.COURIER);
+ tmp.add(FontFactory.COURIER_BOLD);
+ tmp.add(FontFactory.COURIER_OBLIQUE);
+ tmp.add(FontFactory.COURIER_BOLDOBLIQUE);
+ fontFamilies.put(FontFactory.COURIER.toLowerCase(), tmp);
+ tmp = new ArrayList();
+ tmp.add(FontFactory.HELVETICA);
+ tmp.add(FontFactory.HELVETICA_BOLD);
+ tmp.add(FontFactory.HELVETICA_OBLIQUE);
+ tmp.add(FontFactory.HELVETICA_BOLDOBLIQUE);
+ fontFamilies.put(FontFactory.HELVETICA.toLowerCase(), tmp);
+ tmp = new ArrayList();
+ tmp.add(FontFactory.SYMBOL);
+ fontFamilies.put(FontFactory.SYMBOL.toLowerCase(), tmp);
+ tmp = new ArrayList();
+ tmp.add(FontFactory.TIMES_ROMAN);
+ tmp.add(FontFactory.TIMES_BOLD);
+ tmp.add(FontFactory.TIMES_ITALIC);
+ tmp.add(FontFactory.TIMES_BOLDITALIC);
+ fontFamilies.put(FontFactory.TIMES.toLowerCase(), tmp);
+ fontFamilies.put(FontFactory.TIMES_ROMAN.toLowerCase(), tmp);
+ tmp = new ArrayList();
+ tmp.add(FontFactory.ZAPFDINGBATS);
+ fontFamilies.put(FontFactory.ZAPFDINGBATS.toLowerCase(), tmp);
+ }
+
+ /**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @param size the size of this font
+ * @param style the style of this font
+ * @param color the Color
of this font.
+ * @return the Font constructed based on the parameters
+ */
+ public Font getFont(String fontname, String encoding, boolean embedded, float size, int style, Color color) {
+ return getFont(fontname, encoding, embedded, size, style, color, true);
+ }
+
+
+
+ /**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @param size the size of this font
+ * @param style the style of this font
+ * @param color the Color
of this font.
+ * @param cached true if the font comes from the cache or is added to
+ * the cache if new, false if the font is always created new
+ * @return the Font constructed based on the parameters
+ */
+ public Font getFont(String fontname, String encoding, boolean embedded, float size, int style, Color color, boolean cached) {
+ if (fontname == null) return new Font(Font.UNDEFINED, size, style, color);
+ String lowercasefontname = fontname.toLowerCase();
+ ArrayList tmp = (ArrayList) fontFamilies.get(lowercasefontname);
+ if (tmp != null) {
+ // some bugs were fixed here by Daniel Marczisovszky
+ int s = style == Font.UNDEFINED ? Font.NORMAL : style;
+ int fs = Font.NORMAL;
+ boolean found = false;
+ for (Iterator i = tmp.iterator(); i.hasNext(); ) {
+ String f = (String) i.next();
+ String lcf = f.toLowerCase();
+ fs = Font.NORMAL;
+ if (lcf.toLowerCase().indexOf("bold") != -1) fs |= Font.BOLD;
+ if (lcf.toLowerCase().indexOf("italic") != -1 || lcf.toLowerCase().indexOf("oblique") != -1) fs |= Font.ITALIC;
+ if ((s & Font.BOLDITALIC) == fs) {
+ fontname = f;
+ found = true;
+ break;
+ }
+ }
+ if (style != Font.UNDEFINED && found) {
+ style &= ~fs;
+ }
+ }
+ BaseFont basefont = null;
+ try {
+ try {
+ // the font is a type 1 font or CJK font
+ basefont = BaseFont.createFont(fontname, encoding, embedded, cached, null, null);
+ }
+ catch(DocumentException de) {
+ // the font is a true type font or an unknown font
+ fontname = trueTypeFonts.getProperty(fontname.toLowerCase());
+ // the font is not registered as truetype font
+ if (fontname == null) return new Font(Font.UNDEFINED, size, style, color);
+ // the font is registered as truetype font
+ basefont = BaseFont.createFont(fontname, encoding, embedded, cached, null, null);
+ }
+ }
+ catch(DocumentException de) {
+ // this shouldn't happen
+ throw new ExceptionConverter(de);
+ }
+ catch(IOException ioe) {
+ // the font is registered as a true type font, but the path was wrong
+ return new Font(Font.UNDEFINED, size, style, color);
+ }
+ catch(NullPointerException npe) {
+ // null was entered as fontname and/or encoding
+ return new Font(Font.UNDEFINED, size, style, color);
+ }
+ return new Font(basefont, size, style, color);
+ }
+
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param attributes the attributes of a Font
object.
+ * @return the Font constructed based on the attributes
+ */
+
+ public Font getFont(Properties attributes) {
+ String fontname = null;
+ String encoding = defaultEncoding;
+ boolean embedded = defaultEmbedding;
+ float size = Font.UNDEFINED;
+ int style = Font.NORMAL;
+ Color color = null;
+ String value = (String) attributes.remove(MarkupTags.HTML_ATTR_STYLE);
+ if (value != null && value.length() > 0) {
+ Properties styleAttributes = MarkupParser.parseAttributes(value);
+ if (styleAttributes.size() == 0) {
+ attributes.put(MarkupTags.HTML_ATTR_STYLE, value);
+ }
+ else {
+ fontname = (String)styleAttributes.remove(MarkupTags.CSS_KEY_FONTFAMILY);
+ if (fontname != null) {
+ String tmp;
+ while (fontname.indexOf(",") != -1) {
+ tmp = fontname.substring(0, fontname.indexOf(","));
+ if (isRegistered(tmp)) {
+ fontname = tmp;
+ }
+ else {
+ fontname = fontname.substring(fontname.indexOf(",") + 1);
+ }
+ }
+ }
+ if ((value = (String)styleAttributes.remove(MarkupTags.CSS_KEY_FONTSIZE)) != null) {
+ size = MarkupParser.parseLength(value);
+ }
+ if ((value = (String)styleAttributes.remove(MarkupTags.CSS_KEY_FONTWEIGHT)) != null) {
+ style |= Font.getStyleValue(value);
+ }
+ if ((value = (String)styleAttributes.remove(MarkupTags.CSS_KEY_FONTSTYLE)) != null) {
+ style |= Font.getStyleValue(value);
+ }
+ if ((value = (String)styleAttributes.remove(MarkupTags.CSS_KEY_COLOR)) != null) {
+ color = MarkupParser.decodeColor(value);
+ }
+ attributes.putAll(styleAttributes);
+ for (Enumeration e = styleAttributes.keys(); e.hasMoreElements();) {
+ Object o = e.nextElement();
+ attributes.put(o, styleAttributes.get(o));
+ }
+ }
+ }
+ if ((value = (String)attributes.remove(ElementTags.ENCODING)) != null) {
+ encoding = value;
+ }
+ if ("true".equals(attributes.remove(ElementTags.EMBEDDED))) {
+ embedded = true;
+ }
+ if ((value = (String)attributes.remove(ElementTags.FONT)) != null) {
+ fontname = value;
+ }
+ if ((value = (String)attributes.remove(ElementTags.SIZE)) != null) {
+ size = Float.valueOf(value + "f").floatValue();
+ }
+ if ((value = (String)attributes.remove(MarkupTags.HTML_ATTR_STYLE)) != null) {
+ style |= Font.getStyleValue(value);
+ }
+ if ((value = (String)attributes.remove(ElementTags.STYLE)) != null) {
+ style |= Font.getStyleValue(value);
+ }
+ String r = (String)attributes.remove(ElementTags.RED);
+ String g = (String)attributes.remove(ElementTags.GREEN);
+ String b = (String)attributes.remove(ElementTags.BLUE);
+ if (r != null || g != null || b != null) {
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+ if (r != null) red = Integer.parseInt(r);
+ if (g != null) green = Integer.parseInt(g);
+ if (b != null) blue = Integer.parseInt(b);
+ color = new Color(red, green, blue);
+ }
+ else if ((value = (String)attributes.remove(ElementTags.COLOR)) != null) {
+ color = MarkupParser.decodeColor(value);
+ }
+ if (fontname == null) {
+ return getFont(null, encoding, embedded, size, style, color);
+ }
+ return getFont(fontname, encoding, embedded, size, style, color);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @param size the size of this font
+ * @param style the style of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public Font getFont(String fontname, String encoding, boolean embedded, float size, int style) {
+ return getFont(fontname, encoding, embedded, size, style, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @param size the size of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public Font getFont(String fontname, String encoding, boolean embedded, float size) {
+ return getFont(fontname, encoding, embedded, size, Font.UNDEFINED, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @return the Font constructed based on the parameters
+ */
+
+ public Font getFont(String fontname, String encoding, boolean embedded) {
+ return getFont(fontname, encoding, embedded, Font.UNDEFINED, Font.UNDEFINED, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param size the size of this font
+ * @param style the style of this font
+ * @param color the Color
of this font.
+ * @return the Font constructed based on the parameters
+ */
+
+ public Font getFont(String fontname, String encoding, float size, int style, Color color) {
+ return getFont(fontname, encoding, defaultEmbedding, size, style, color);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param size the size of this font
+ * @param style the style of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public Font getFont(String fontname, String encoding, float size, int style) {
+ return getFont(fontname, encoding, defaultEmbedding, size, style, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @param size the size of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public Font getFont(String fontname, String encoding, float size) {
+ return getFont(fontname, encoding, defaultEmbedding, size, Font.UNDEFINED, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param encoding the encoding of the font
+ * @return the Font constructed based on the parameters
+ */
+
+ public Font getFont(String fontname, String encoding) {
+ return getFont(fontname, encoding, defaultEmbedding, Font.UNDEFINED, Font.UNDEFINED, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param size the size of this font
+ * @param style the style of this font
+ * @param color the Color
of this font.
+ * @return the Font constructed based on the parameters
+ */
+
+ public Font getFont(String fontname, float size, int style, Color color) {
+ return getFont(fontname, defaultEncoding, defaultEmbedding, size, style, color);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param size the size of this font
+ * @param style the style of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public Font getFont(String fontname, float size, int style) {
+ return getFont(fontname, defaultEncoding, defaultEmbedding, size, style, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @param size the size of this font
+ * @return the Font constructed based on the parameters
+ */
+
+ public Font getFont(String fontname, float size) {
+ return getFont(fontname, defaultEncoding, defaultEmbedding, size, Font.UNDEFINED, null);
+ }
+
+/**
+ * Constructs a Font
-object.
+ *
+ * @param fontname the name of the font
+ * @return the Font constructed based on the parameters
+ */
+
+ public Font getFont(String fontname) {
+ return getFont(fontname, defaultEncoding, defaultEmbedding, Font.UNDEFINED, Font.UNDEFINED, null);
+ }
+
+ /**
+ * Register a font by giving explicitly the font family and name.
+ * @param familyName the font family
+ * @param fullName the font name
+ * @param path the font path
+ */
+ public void registerFamily(String familyName, String fullName, String path) {
+ if (path != null)
+ trueTypeFonts.setProperty(fullName, path);
+ ArrayList tmp = (ArrayList) fontFamilies.get(familyName);
+ if (tmp == null) {
+ tmp = new ArrayList();
+ tmp.add(fullName);
+ fontFamilies.put(familyName, tmp);
+ }
+ else {
+ int fullNameLength = fullName.length();
+ boolean inserted = false;
+ for (int j = 0; j < tmp.size(); ++j) {
+ if (((String)tmp.get(j)).length() >= fullNameLength) {
+ tmp.add(j, fullName);
+ inserted = true;
+ break;
+ }
+ }
+ if (!inserted)
+ tmp.add(fullName);
+ }
+ }
+
+/**
+ * Register a ttf- or a ttc-file.
+ *
+ * @param path the path to a ttf- or ttc-file
+ */
+
+ public void register(String path) {
+ register(path, null);
+ }
+
+/**
+ * Register a font file and use an alias for the font contained in it.
+ *
+ * @param path the path to a font file
+ * @param alias the alias you want to use for the font
+ */
+
+ public void register(String path, String alias) {
+ try {
+ if (path.toLowerCase().endsWith(".ttf") || path.toLowerCase().endsWith(".otf") || path.toLowerCase().indexOf(".ttc,") > 0) {
+ Object allNames[] = BaseFont.getAllFontNames(path, BaseFont.WINANSI, null);
+ trueTypeFonts.setProperty(((String)allNames[0]).toLowerCase(), path);
+ if (alias != null) {
+ trueTypeFonts.setProperty(alias.toLowerCase(), path);
+ }
+ // register all the font names with all the locales
+ String[][] names = (String[][])allNames[2]; //full name
+ for (int i = 0; i < names.length; i++) {
+ trueTypeFonts.setProperty(names[i][3].toLowerCase(), path);
+ }
+ String fullName = null;
+ String familyName = null;
+ names = (String[][])allNames[1]; //family name
+ for (int k = 0; k < TTFamilyOrder.length; k += 3) {
+ for (int i = 0; i < names.length; i++) {
+ if (TTFamilyOrder[k].equals(names[i][0]) && TTFamilyOrder[k + 1].equals(names[i][1]) && TTFamilyOrder[k + 2].equals(names[i][2])) {
+ familyName = names[i][3].toLowerCase();
+ k = TTFamilyOrder.length;
+ break;
+ }
+ }
+ }
+ if (familyName != null) {
+ String lastName = "";
+ names = (String[][])allNames[2]; //full name
+ for (int i = 0; i < names.length; i++) {
+ for (int k = 0; k < TTFamilyOrder.length; k += 3) {
+ if (TTFamilyOrder[k].equals(names[i][0]) && TTFamilyOrder[k + 1].equals(names[i][1]) && TTFamilyOrder[k + 2].equals(names[i][2])) {
+ fullName = names[i][3];
+ if (fullName.equals(lastName))
+ continue;
+ lastName = fullName;
+ registerFamily(familyName, fullName, null);
+ break;
+ }
+ }
+ }
+ }
+ }
+ else if (path.toLowerCase().endsWith(".ttc")) {
+ if (alias != null)
+ System.err.println("class FontFactory: You can't define an alias for a true type collection.");
+ String[] names = BaseFont.enumerateTTCNames(path);
+ for (int i = 0; i < names.length; i++) {
+ register(path + "," + i);
+ }
+ }
+ else if (path.toLowerCase().endsWith(".afm") || path.toLowerCase().endsWith(".pfm")) {
+ BaseFont bf = BaseFont.createFont(path, BaseFont.CP1252, false);
+ String fullName = (bf.getFullFontName()[0][3]).toLowerCase();
+ String familyName = (bf.getFamilyFontName()[0][3]).toLowerCase();
+ String psName = bf.getPostscriptFontName().toLowerCase();
+ registerFamily(familyName, fullName, null);
+ trueTypeFonts.setProperty(psName, path);
+ trueTypeFonts.setProperty(fullName, path);
+ }
+ }
+ catch(DocumentException de) {
+ // this shouldn't happen
+ throw new ExceptionConverter(de);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+ /** Register all the fonts in a directory.
+ * @param dir the directory
+ * @return the number of fonts registered
+ */
+ public int registerDirectory(String dir) {
+ int count = 0;
+ try {
+ File file = new File(dir);
+ if (!file.exists() || !file.isDirectory())
+ return 0;
+ String files[] = file.list();
+ if (files == null)
+ return 0;
+ for (int k = 0; k < files.length; ++k) {
+ try {
+ file = new File(dir, files[k]);
+ String name = file.getPath().toLowerCase();
+ if (name.endsWith(".ttf") || name.endsWith(".otf") || name.endsWith(".afm") || name.endsWith(".pfm") || name.endsWith(".ttc")) {
+ register(file.getPath(), null);
+ ++count;
+ }
+ }
+ catch (Exception e) {
+ //empty on purpose
+ }
+ }
+ }
+ catch (Exception e) {
+ //empty on purpose
+ }
+ return count;
+ }
+
+ /** Register fonts in some probable directories. It usually works in Windows,
+ * Linux and Solaris.
+ * @return the number of fonts registered
+ */
+ public int registerDirectories() {
+ int count = 0;
+ count += registerDirectory("c:/windows/fonts");
+ count += registerDirectory("c:/winnt/fonts");
+ count += registerDirectory("d:/windows/fonts");
+ count += registerDirectory("d:/winnt/fonts");
+ count += registerDirectory("/usr/X/lib/X11/fonts/TrueType");
+ count += registerDirectory("/usr/openwin/lib/X11/fonts/TrueType");
+ count += registerDirectory("/usr/share/fonts/default/TrueType");
+ count += registerDirectory("/usr/X11R6/lib/X11/fonts/ttf");
+ count += registerDirectory("/Library/Fonts");
+ count += registerDirectory("/System/LIbrary/Fonts");
+ return count;
+ }
+
+/**
+ * Gets a set of registered fontnames.
+ * @return a set of registered fonts
+ */
+
+ public Set getRegisteredFonts() {
+ return Chunk.getKeySet(trueTypeFonts);
+ }
+
+/**
+ * Gets a set of registered fontnames.
+ * @return a set of registered font families
+ */
+
+ public Set getRegisteredFamilies() {
+ return Chunk.getKeySet(fontFamilies);
+ }
+
+/**
+ * Checks if a certain font is registered.
+ *
+ * @param fontname the name of the font that has to be checked.
+ * @return true if the font is found
+ */
+ public boolean isRegistered(String fontname) {
+ return trueTypeFonts.containsKey(fontname.toLowerCase());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/Graphic.java b/src/main/java/com/lowagie/text/Graphic.java
new file mode 100644
index 0000000..5d65c42
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Graphic.java
@@ -0,0 +1,277 @@
+/*
+ * $Id: Graphic.java,v 1.57 2004/12/14 12:33:47 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashMap;
+
+import com.lowagie.text.pdf.PdfContentByte;
+
+/**
+ * A Graphic
element can contain several geometric figures (curves, lines,...).
+ * Element
, please read the Sections 8.4 and 8.5 of
+ * the PDF Reference Manual version 1.3 first.
+ *
+ * @see Element
+ */
+
+public class Graphic extends PdfContentByte implements Element {
+
+/** This is a type of Graphic. */
+ public static final String HORIZONTAL_LINE = "HORIZONTAL";
+
+/** This is a type of Graphic. */
+ public static final String BORDER = "BORDER";
+
+/** Contains some of the attributes for this Graphic. */
+ private HashMap attributes;
+
+ // constructor
+
+/**
+ * Constructs a Graphic
-object.
+ */
+
+ public Graphic() {
+ super(null);
+ }
+
+ // implementation of the Element interface
+
+/**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener an ElementListener
+ * true
if the element was processed successfully
+ * @return true if processing this object succeeded
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ return listener.add(this);
+ }
+ catch(DocumentException de) {
+ return false;
+ }
+ }
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.GRAPHIC;
+ }
+
+/**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ return new ArrayList();
+ }
+
+ /**
+ * Orders this graphic to draw a horizontal, centered line.
+ * @param linewidth the line width
+ * @param percentage the percentage horizontal width in relation to the margins or if negative, an absolute value
+ */
+
+ public void setHorizontalLine(float linewidth, float percentage) {
+ if (attributes == null) attributes = new HashMap();
+ attributes.put(HORIZONTAL_LINE, new Object[]{new Float(linewidth), new Float(percentage), Color.black, new Integer(Element.ALIGN_CENTER)});
+ }
+
+ /**
+ * Orders this graphic to draw a horizontal line with some alignment.
+ * @param linewidth the line width
+ * @param percentage the percentage horizontal width in relation to the margins or if negative, an absolute value
+ * @param align the line alignment
+ */
+ public void setHorizontalLine(float linewidth, float percentage, int align) {
+ if (attributes == null) attributes = new HashMap();
+ attributes.put(HORIZONTAL_LINE, new Object[]{new Float(linewidth), new Float(percentage), Color.black, new Integer(align)});
+ }
+
+ /**
+ * Orders this graphic to draw a horizontal, centered line.
+ * @param linewidth the line width
+ * @param percentage the percentage horizontal width in relation to the margins or if negative, an absolute value
+ * @param color the color of the line
+ */
+
+ public void setHorizontalLine(float linewidth, float percentage, Color color) {
+ if (attributes == null) attributes = new HashMap();
+ attributes.put(HORIZONTAL_LINE, new Object[]{new Float(linewidth), new Float(percentage), color, new Integer(Element.ALIGN_CENTER)});
+ }
+
+ /**
+ * Orders this graphic to draw a horizontal, centered line.
+ * @param linewidth the line width
+ * @param percentage the percentage horizontal width in relation to the margins or if negative, an absolute value
+ * @param color the color of the line
+ * @param align the line alignment
+ */
+ public void setHorizontalLine(float linewidth, float percentage, Color color, int align) {
+ if (attributes == null) attributes = new HashMap();
+ attributes.put(HORIZONTAL_LINE, new Object[]{new Float(linewidth), new Float(percentage), color, new Integer(align)});
+ }
+
+/**
+ * draws a horizontal line.
+ * @param lineWidth width of the line
+ * @param color color of the line
+ * @param x1 start position of the line
+ * @param x2 end position of the line
+ * @param y y-coordinate of the line
+ */
+
+ public void drawHorizontalLine(float lineWidth, Color color, float x1, float x2, float y) {
+ setLineWidth(lineWidth);
+ setColorStroke(color);
+ moveTo(x1, y);
+ lineTo(x2, y);
+ stroke();
+ resetRGBColorStroke();
+ }
+
+/**
+ * Orders this graphic to draw a horizontal line.
+ * @param linewidth linewidth of the border
+ * @param extraSpace extraspace needed as marging on the page
+ */
+
+ public void setBorder(float linewidth, float extraSpace) {
+ if (attributes == null) attributes = new HashMap();
+ attributes.put(BORDER, new Object[]{new Float(linewidth), new Float(extraSpace), new Color(0, 0, 0)});
+ }
+
+/**
+ * Orders this graphic to draw a horizontal line.
+ * @param linewidth linewidth of the border
+ * @param extraSpace extraspace needed as marging on the page
+ * @param color color of the borderbox
+ */
+
+ public void setBorder(float linewidth, float extraSpace, Color color) {
+ if (attributes == null) attributes = new HashMap();
+ attributes.put(BORDER, new Object[]{new Float(linewidth), new Float(extraSpace), color});
+ }
+
+/**
+ * Draws a border
+ * @param lineWidth linewidth of the border
+ * @param color color of the borderbox
+ * @param llx lower left x coordinate
+ * @param lly lower left y coordinate
+ * @param urx upper right x coordinate
+ * @param ury upper right y coordinate
+ */
+ public void drawBorder(float lineWidth, Color color, float llx, float lly, float urx, float ury) {
+ setLineWidth(lineWidth);
+ setColorStroke(color);
+ rectangle(llx, lly, urx - llx, ury - lly);
+ stroke();
+ resetRGBColorStroke();
+ }
+
+/**
+ * Processes the attributes of this object.
+ * @param llx lower left x coordinate
+ * @param lly lower left y coordinate
+ * @param urx upper right x coordinate
+ * @param ury upper right y coordinate
+ * @param y
+ */
+
+ public void processAttributes(float llx, float lly, float urx, float ury, float y) {
+ if (attributes == null) return;
+ String attribute;
+ Object[] o;
+ for (Iterator i = attributes.keySet().iterator(); i.hasNext(); ) {
+ attribute = (String) i.next();
+ o = (Object[]) attributes.get(attribute);
+ if (HORIZONTAL_LINE.equals(attribute)) {
+ float p = ((Float)o[1]).floatValue();
+ float w;
+ if (p < 0)
+ w = -p;
+ else
+ w = (urx - llx) * p / 100.0f;
+ int align = ((Integer)o[3]).intValue();
+ float s;
+ switch (align) {
+ case Element.ALIGN_LEFT:
+ s = 0;
+ break;
+ case Element.ALIGN_RIGHT:
+ s = urx - llx - w;
+ break;
+ default:
+ s = (urx - llx - w) / 2;
+ }
+ drawHorizontalLine(((Float)o[0]).floatValue(), (Color)o[2], s + llx, s + w + llx, y);
+ }
+ if (BORDER.equals(attribute)) {
+ float extra = ((Float)o[1]).floatValue();
+ drawBorder(((Float)o[0]).floatValue(), (Color)o[2], llx - extra, lly - extra, urx + extra, ury + extra);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/GreekList.java b/src/main/java/com/lowagie/text/GreekList.java
new file mode 100644
index 0000000..5cebe91
--- /dev/null
+++ b/src/main/java/com/lowagie/text/GreekList.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2003 by Michael Niedermair.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text;
+
+/**
+ *
+ * A special-version of LIST
whitch use greek-letters.
+ *
+ * @see com.lowagie.text.List
+ * @version 2003-06-22
+ * @author Michael Niedermair
+ */
+
+public class GreekList extends List {
+
+ /**
+ * UpperCase or LowerCase
+ */
+ protected boolean greeklower;
+
+ /**
+ * Initialisierung
+ *
+ * @param symbolIndent indent
+ */
+ public GreekList(int symbolIndent) {
+ super(true, symbolIndent);
+ setGreekFont();
+ }
+
+ /**
+ * Initialisierung
+ * @param greeklower greek-char in lowercase
+ * @param symbolIndent indent
+ */
+ public GreekList(boolean greeklower, int symbolIndent) {
+ super(true, symbolIndent);
+ this.greeklower = greeklower;
+ setGreekFont();
+ }
+
+ /**
+ * change the font to SYMBOL
+ */
+ protected void setGreekFont() {
+ float fontsize = symbol.font().size();
+ symbol.setFont(FontFactory.getFont(FontFactory.SYMBOL, fontsize, Font.NORMAL));
+ }
+
+ /**
+ * set the greek-letters to lowercase otherwise to uppercase
+ *
+ * @param greeklower
+ */
+ public void setGreekLower(boolean greeklower) {
+ this.greeklower = greeklower;
+ }
+
+ /**
+ * Checks if the list is greek-letter with lowercase
+ *
+ * @return true
if the greek-letter is lowercase, false
otherwise.
+ */
+ public boolean isGreekLower() {
+ return greeklower;
+ }
+
+ /**
+ * Adds an Object
to the List
.
+ *
+ * @param o the object to add.
+ * @return true if adding the object succeeded
+ */
+ public boolean add(Object o) {
+ if (o instanceof ListItem) {
+ ListItem item = (ListItem) o;
+ Chunk chunk;
+ if (greeklower)
+ chunk = SpecialSymbol.get((char) (first + list.size() + 944), symbol.font());
+ else
+ chunk = SpecialSymbol.get((char) (first + list.size() + 912), symbol.font());
+ chunk.append(".");
+ item.setListSymbol(chunk);
+ item.setIndentationLeft(symbolIndent);
+ item.setIndentationRight(0);
+ list.add(item);
+ } else if (o instanceof List) {
+ List nested = (List) o;
+ nested.setIndentationLeft(nested.indentationLeft() + symbolIndent);
+ first--;
+ return list.add(nested);
+ } else if (o instanceof String) {
+ return this.add(new ListItem((String) o));
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/Header.java b/src/main/java/com/lowagie/text/Header.java
new file mode 100644
index 0000000..6ccea55
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Header.java
@@ -0,0 +1,98 @@
+/*
+ * $Id: Header.java,v 1.61 2005/05/04 14:31:10 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+/**
+ * This is an Element
that contains
+ * some userdefined meta information about the document.
+ *
+ *
+ * @see Element
+ * @see Meta
+ */
+
+public class Header extends Meta implements Element {
+
+ // membervariables
+
+/** This is the content of this chunk of text. */
+ private StringBuffer name;
+
+ // constructors
+
+/**
+ * Constructs a
+ * Header header = new Header("inspired by", "William Shakespeare");
+ *
Meta
.
+ *
+ * @param name the name of the meta-information
+ * @param content the content
+ */
+
+ public Header(String name, String content) {
+ super(Element.HEADER, content);
+ this.name = new StringBuffer(name);
+ }
+
+ // methods to retrieve information
+
+/**
+ * Returns the name of the meta information.
+ *
+ * @return a String
+ */
+
+ public String name() {
+ return name.toString();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/HeaderFooter.java b/src/main/java/com/lowagie/text/HeaderFooter.java
new file mode 100644
index 0000000..4e4182a
--- /dev/null
+++ b/src/main/java/com/lowagie/text/HeaderFooter.java
@@ -0,0 +1,206 @@
+/*
+ * $Id: HeaderFooter.java,v 1.68 2005/05/04 14:31:18 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.Phrase;
+
+/**
+ * A HeaderFooter
-object is a Rectangle
with text
+ * that can be put above and/or below every page.
+ *
+ */
+
+public class HeaderFooter extends Rectangle implements MarkupAttributes {
+
+ // membervariables
+
+/** Does the page contain a pagenumber? */
+ private boolean numbered;
+
+/** This is the
+ * HeaderFooter header = new HeaderFooter(new Phrase("This is a header."), false);
+ * HeaderFooter footer = new HeaderFooter(new Phrase("This is page "), new Phrase("."));
+ * document.setHeader(header);
+ * document.setFooter(footer);
+ *
Phrase
that comes before the pagenumber. */
+ private Phrase before = null;
+
+/** This is number of the page. */
+ private int pageN;
+
+/** This is the Phrase
that comes after the pagenumber. */
+ private Phrase after = null;
+
+/** This is alignment of the header/footer. */
+ private int alignment;
+
+ // constructors
+
+/**
+ * Constructs a HeaderFooter
-object.
+ *
+ * @param before the Phrase
before the pagenumber
+ * @param after the Phrase
before the pagenumber
+ */
+
+ public HeaderFooter(Phrase before, Phrase after) {
+ super(0, 0, 0, 0);
+ setBorder(TOP + BOTTOM);
+ setBorderWidth(1);
+
+ numbered = true;
+ this.before = before;
+ this.after = after;
+ }
+
+/**
+ * Constructs a Header
-object with a pagenumber at the end.
+ *
+ * @param before the Phrase
before the pagenumber
+ * @param numbered true
if the page has to be numbered
+ */
+
+ public HeaderFooter(Phrase before, boolean numbered) {
+ super(0, 0, 0, 0);
+ setBorder(TOP + BOTTOM);
+ setBorderWidth(1);
+
+ this.numbered = numbered;
+ this.before = before;
+ }
+
+ // methods
+
+/**
+ * Checks if the HeaderFooter contains a page number.
+ *
+ * @return true if the page has to be numbered
+ */
+
+ public boolean isNumbered() {
+ return numbered;
+ }
+
+/**
+ * Gets the part that comes before the pageNumber.
+ *
+ * @return a Phrase
+ */
+
+ public Phrase getBefore() {
+ return before;
+ }
+
+/**
+ * Gets the part that comes after the pageNumber.
+ *
+ * @return a Phrase
+ */
+
+ public Phrase getAfter() {
+ return after;
+ }
+
+/**
+ * Sets the page number.
+ *
+ * @param pageN the new page number
+ */
+
+ public void setPageNumber(int pageN) {
+ this.pageN = pageN;
+ }
+
+/**
+ * Sets the alignment.
+ *
+ * @param alignment the new alignment
+ */
+
+ public void setAlignment(int alignment) {
+ this.alignment = alignment;
+ }
+
+ // methods to retrieve the membervariables
+
+/**
+ * Gets the Paragraph
that can be used as header or footer.
+ *
+ * @return a Paragraph
+ */
+
+ public Paragraph paragraph() {
+ Paragraph paragraph = new Paragraph(before.leading());
+ paragraph.add(before);
+ if (numbered) {
+ paragraph.addSpecial(new Chunk(String.valueOf(pageN), before.font()));
+ }
+ if (after != null) {
+ paragraph.addSpecial(after);
+ }
+ paragraph.setAlignment(alignment);
+ return paragraph;
+ }
+
+ /**
+ * Gets the alignment of this HeaderFooter.
+ *
+ * @return alignment
+ */
+
+ public int alignment() {
+ return alignment;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/Image.java b/src/main/java/com/lowagie/text/Image.java
new file mode 100644
index 0000000..788e7ed
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Image.java
@@ -0,0 +1,2134 @@
+/*
+ * $Id: Image.java,v 1.115 2006/02/16 16:17:59 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.awt.Graphics2D;
+import java.awt.color.ICC_Profile;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Properties;
+import java.util.Set;
+
+import com.lowagie.text.pdf.PRIndirectReference;
+import com.lowagie.text.pdf.PRTokeniser;
+import com.lowagie.text.pdf.PdfArray;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfDictionary;
+import com.lowagie.text.pdf.PdfIndirectReference;
+import com.lowagie.text.pdf.PdfName;
+import com.lowagie.text.pdf.PdfNumber;
+import com.lowagie.text.pdf.PdfOCG;
+import com.lowagie.text.pdf.PdfObject;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfTemplate;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.text.pdf.RandomAccessFileOrArray;
+import com.lowagie.text.pdf.codec.BmpImage;
+import com.lowagie.text.pdf.codec.CCITTG4Encoder;
+import com.lowagie.text.pdf.codec.GifImage;
+import com.lowagie.text.pdf.codec.PngImage;
+import com.lowagie.text.pdf.codec.TiffImage;
+
+/**
+ * An Image
is the representation of a graphic element (JPEG, PNG
+ * or GIF) that has to be inserted into the document
+ *
+ * @see Element
+ * @see Rectangle
+ */
+
+public abstract class Image extends Rectangle implements Element,
+ MarkupAttributes {
+
+ // static membervariables
+
+ /** this is a kind of image alignment. */
+ public static final int DEFAULT = 0;
+
+ /** this is a kind of image alignment. */
+ public static final int RIGHT = 2;
+
+ /** this is a kind of image alignment. */
+ public static final int LEFT = 0;
+
+ /** this is a kind of image alignment. */
+ public static final int MIDDLE = 1;
+
+ /** this is a kind of image alignment. */
+ public static final int TEXTWRAP = 4;
+
+ /** this is a kind of image alignment. */
+ public static final int UNDERLYING = 8;
+
+ /** This represents a coordinate in the transformation matrix. */
+ public static final int AX = 0;
+
+ /** This represents a coordinate in the transformation matrix. */
+ public static final int AY = 1;
+
+ /** This represents a coordinate in the transformation matrix. */
+ public static final int BX = 2;
+
+ /** This represents a coordinate in the transformation matrix. */
+ public static final int BY = 3;
+
+ /** This represents a coordinate in the transformation matrix. */
+ public static final int CX = 4;
+
+ /** This represents a coordinate in the transformation matrix. */
+ public static final int CY = 5;
+
+ /** This represents a coordinate in the transformation matrix. */
+ public static final int DX = 6;
+
+ /** This represents a coordinate in the transformation matrix. */
+ public static final int DY = 7;
+
+ /** type of image */
+ public static final int ORIGINAL_NONE = 0;
+
+ /** type of image */
+ public static final int ORIGINAL_JPEG = 1;
+
+ /** type of image */
+ public static final int ORIGINAL_PNG = 2;
+
+ /** type of image */
+ public static final int ORIGINAL_GIF = 3;
+
+ /** type of image */
+ public static final int ORIGINAL_BMP = 4;
+
+ /** type of image */
+ public static final int ORIGINAL_TIFF = 5;
+
+ /** type of image */
+ public static final int ORIGINAL_WMF = 6;
+
+ /** type of image */
+ public static final int ORIGINAL_PS = 7;
+
+ /** Image color inversion */
+ protected boolean invert = false;
+
+ /** The imagetype. */
+ protected int type;
+
+ /** The URL of the image. */
+ protected URL url;
+
+ /** The raw data of the image. */
+ protected byte rawData[];
+
+ /** The template to be treated as an image. */
+ protected PdfTemplate template[] = new PdfTemplate[1];
+
+ /** The alignment of the Image. */
+ protected int alignment;
+
+ /** Text that can be shown instead of the image. */
+ protected String alt;
+
+ /** This is the absolute X-position of the image. */
+ protected float absoluteX = Float.NaN;
+
+ /** This is the absolute Y-position of the image. */
+ protected float absoluteY = Float.NaN;
+
+ /** This is the width of the image without rotation. */
+ protected float plainWidth;
+
+ /** This is the width of the image without rotation. */
+ protected float plainHeight;
+
+ /** This is the scaled width of the image taking rotation into account. */
+ protected float scaledWidth;
+
+ /** This is the original height of the image taking rotation into account. */
+ protected float scaledHeight;
+
+ /** This is the rotation of the image. */
+ protected float rotation;
+
+ /** this is the colorspace of a jpeg-image. */
+ protected int colorspace = -1;
+
+ /**
+ * this is the bits per component of the raw image. It also flags a CCITT
+ * image.
+ */
+ protected int bpc = 1;
+
+ /** this is the transparency information of the raw image */
+ protected int transparency[];
+
+ // for the moment these variables are only used for Images in class Table
+ // code contributed by Pelikan Stephan
+ /** the indentation to the left. */
+ protected float indentationLeft = 0;
+
+ /** the indentation to the right. */
+ protected float indentationRight = 0;
+
+ // serial stamping
+
+ protected Long mySerialId = getSerialId();
+
+ static long serialId = 0;
+
+ /** Holds value of property dpiX. */
+ protected int dpiX = 0;
+
+ /** Holds value of property dpiY. */
+ protected int dpiY = 0;
+
+ protected boolean mask = false;
+
+ protected Image imageMask;
+
+ /** Holds value of property interpolation. */
+ protected boolean interpolation;
+
+ /** if the annotation is not null the image will be clickable. */
+ protected Annotation annotation = null;
+
+ /** Contains extra markupAttributes */
+ protected Properties markupAttributes;
+
+ /** ICC Profile attached */
+ protected ICC_Profile profile = null;
+
+ /** Holds value of property deflated. */
+ protected boolean deflated = false;
+
+ private PdfDictionary additional = null;
+
+ /** Holds value of property smask. */
+ private boolean smask;
+
+ /** Holds value of property XYRatio. */
+ private float XYRatio = 0;
+
+ /** Holds value of property originalType. */
+ protected int originalType = ORIGINAL_NONE;
+
+ /** Holds value of property originalData. */
+ protected byte[] originalData;
+
+ /** The spacing before the image. */
+ protected float spacingBefore;
+
+ /** The spacing after the image. */
+ protected float spacingAfter;
+
+ /**
+ * Holds value of property widthPercentage.
+ */
+ private float widthPercentage = 100;
+
+ protected PdfOCG layer;
+
+ /**
+ * Holds value of property initialRotation.
+ */
+ private float initialRotation;
+
+ // constructors
+
+ /**
+ * Constructs an Image
-object, using an url .
+ *
+ * @param url
+ * the URL
where the image can be found.
+ */
+
+ public Image(URL url) {
+ super(0, 0);
+ this.url = url;
+ this.alignment = DEFAULT;
+ rotation = 0;
+ }
+
+ /**
+ * Constructs an Image
-object, using an url .
+ *
+ * @param image
+ * another Image object.
+ */
+
+ protected Image(Image image) {
+ super(image);
+ this.type = image.type;
+ this.url = image.url;
+ this.alignment = image.alignment;
+ this.alt = image.alt;
+ this.absoluteX = image.absoluteX;
+ this.absoluteY = image.absoluteY;
+ this.plainWidth = image.plainWidth;
+ this.plainHeight = image.plainHeight;
+ this.scaledWidth = image.scaledWidth;
+ this.scaledHeight = image.scaledHeight;
+ this.rotation = image.rotation;
+ this.colorspace = image.colorspace;
+ this.rawData = image.rawData;
+ this.template = image.template;
+ this.bpc = image.bpc;
+ this.transparency = image.transparency;
+ this.mySerialId = image.mySerialId;
+ this.invert = image.invert;
+ this.dpiX = image.dpiX;
+ this.dpiY = image.dpiY;
+ this.mask = image.mask;
+ this.imageMask = image.imageMask;
+ this.interpolation = image.interpolation;
+ this.annotation = image.annotation;
+ this.markupAttributes = image.markupAttributes;
+ this.profile = image.profile;
+ this.deflated = image.deflated;
+ this.additional = image.additional;
+ this.smask = image.smask;
+ this.XYRatio = image.XYRatio;
+ this.originalData = image.originalData;
+ this.originalType = image.originalType;
+ this.spacingAfter = image.spacingAfter;
+ this.spacingBefore = image.spacingBefore;
+ this.widthPercentage = image.widthPercentage;
+ this.layer = image.layer;
+ this.initialRotation = image.initialRotation;
+ this.directReference = image.directReference;
+ }
+
+ /**
+ * gets an instance of an Image
+ *
+ * @param image
+ * an Image object
+ * @return a new Image object
+ */
+
+ public static Image getInstance(Image image) {
+ if (image == null)
+ return null;
+ try {
+ Class cs = image.getClass();
+ Constructor constructor = cs
+ .getDeclaredConstructor(new Class[] { Image.class });
+ return (Image) constructor.newInstance(new Object[] { image });
+ } catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Gets an instance of an Image.
+ *
+ * @param url
+ * an URL
+ * @return an Image
+ * @throws BadElementException
+ * @throws MalformedURLException
+ * @throws IOException
+ */
+
+ public static Image getInstance(URL url) throws BadElementException,
+ MalformedURLException, IOException {
+ InputStream is = null;
+ try {
+ is = url.openStream();
+ int c1 = is.read();
+ int c2 = is.read();
+ int c3 = is.read();
+ int c4 = is.read();
+ is.close();
+
+ is = null;
+ if (c1 == 'G' && c2 == 'I' && c3 == 'F') {
+ GifImage gif = new GifImage(url);
+ Image img = gif.getImage(1);
+ return img;
+ }
+ if (c1 == 0xFF && c2 == 0xD8) {
+ return new Jpeg(url);
+ }
+ if (c1 == PngImage.PNGID[0] && c2 == PngImage.PNGID[1]
+ && c3 == PngImage.PNGID[2] && c4 == PngImage.PNGID[3]) {
+ return PngImage.getImage(url);
+ }
+ if (c1 == '%' && c2 == '!' && c3 == 'P' && c4 == 'S') {
+ return new ImgPostscript(url);
+ }
+ if (c1 == 0xD7 && c2 == 0xCD) {
+ return new ImgWMF(url);
+ }
+ if (c1 == 'B' && c2 == 'M') {
+ return BmpImage.getImage(url);
+ }
+ if ((c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42)
+ || (c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0)) {
+ RandomAccessFileOrArray ra = null;
+ try {
+ if (url.getProtocol().equals("file")) {
+ String file = url.getFile();
+ file = unEscapeURL(file);
+ ra = new RandomAccessFileOrArray(file);
+ } else
+ ra = new RandomAccessFileOrArray(url);
+ Image img = TiffImage.getTiffImage(ra, 1);
+ img.url = url;
+ return img;
+ } finally {
+ if (ra != null)
+ ra.close();
+ }
+
+ }
+ throw new IOException(url.toString()
+ + " is not a recognized imageformat.");
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ /**
+ * gets an instance of an Image
+ *
+ * @param imgb
+ * raw image date
+ * @return an Image object
+ * @throws BadElementException
+ * @throws MalformedURLException
+ * @throws IOException
+ */
+ public static Image getInstance(byte imgb[]) throws BadElementException,
+ MalformedURLException, IOException {
+ InputStream is = null;
+ try {
+ is = new java.io.ByteArrayInputStream(imgb);
+ int c1 = is.read();
+ int c2 = is.read();
+ int c3 = is.read();
+ int c4 = is.read();
+ is.close();
+
+ is = null;
+ if (c1 == 'G' && c2 == 'I' && c3 == 'F') {
+ GifImage gif = new GifImage(imgb);
+ return gif.getImage(1);
+ }
+ if (c1 == 0xFF && c2 == 0xD8) {
+ return new Jpeg(imgb);
+ }
+ if (c1 == PngImage.PNGID[0] && c2 == PngImage.PNGID[1]
+ && c3 == PngImage.PNGID[2] && c4 == PngImage.PNGID[3]) {
+ return PngImage.getImage(imgb);
+ }
+ if (c1 == '%' && c2 == '!' && c3 == 'P' && c4 == 'S') {
+ return new ImgPostscript(imgb);
+ }
+ if (c1 == 0xD7 && c2 == 0xCD) {
+ return new ImgWMF(imgb);
+ }
+ if (c1 == 'B' && c2 == 'M') {
+ return BmpImage.getImage(imgb);
+ }
+ if ((c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42)
+ || (c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0)) {
+ RandomAccessFileOrArray ra = null;
+ try {
+ ra = new RandomAccessFileOrArray(imgb);
+ Image img = TiffImage.getTiffImage(ra, 1);
+ if (img.getOriginalData() == null)
+ img.setOriginalData(imgb);
+ return img;
+ } finally {
+ if (ra != null)
+ ra.close();
+ }
+
+ }
+ throw new IOException(
+ "The byte array is not a recognized imageformat.");
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ /**
+ * Gets an instance of an Image from a java.awt.Image.
+ *
+ * @param image
+ * the java.awt.Image
to convert
+ * @param color
+ * if different from null
the transparency pixels
+ * are replaced by this color
+ * @param forceBW
+ * if true
the image is treated as black and white
+ * @return an object of type ImgRaw
+ * @throws BadElementException
+ * on error
+ * @throws IOException
+ * on error
+ */
+
+ public static Image getInstance(java.awt.Image image, java.awt.Color color,
+ boolean forceBW) throws BadElementException, IOException {
+ java.awt.image.PixelGrabber pg = new java.awt.image.PixelGrabber(image,
+ 0, 0, -1, -1, true);
+ try {
+ pg.grabPixels();
+ } catch (InterruptedException e) {
+ throw new IOException(
+ "java.awt.Image Interrupted waiting for pixels!");
+ }
+ if ((pg.getStatus() & java.awt.image.ImageObserver.ABORT) != 0) {
+ throw new IOException("java.awt.Image fetch aborted or errored");
+ }
+ int w = pg.getWidth();
+ int h = pg.getHeight();
+ int[] pixels = (int[]) pg.getPixels();
+ if (forceBW) {
+ int byteWidth = (w / 8) + ((w & 7) != 0 ? 1 : 0);
+ byte[] pixelsByte = new byte[byteWidth * h];
+
+ int index = 0;
+ int size = h * w;
+ int transColor = 1;
+ if (color != null) {
+ transColor = (color.getRed() + color.getGreen()
+ + color.getBlue() < 384) ? 0 : 1;
+ }
+ int transparency[] = null;
+ int cbyte = 0x80;
+ int wMarker = 0;
+ int currByte = 0;
+ if (color != null) {
+ for (int j = 0; j < size; j++) {
+ int alpha = (pixels[j] >> 24) & 0xff;
+ if (alpha < 250) {
+ if (transColor == 1)
+ currByte |= cbyte;
+ } else {
+ if ((pixels[j] & 0x888) != 0)
+ currByte |= cbyte;
+ }
+ cbyte >>= 1;
+ if (cbyte == 0 || wMarker + 1 >= w) {
+ pixelsByte[index++] = (byte) currByte;
+ cbyte = 0x80;
+ currByte = 0;
+ }
+ ++wMarker;
+ if (wMarker >= w)
+ wMarker = 0;
+ }
+ } else {
+ for (int j = 0; j < size; j++) {
+ if (transparency == null) {
+ int alpha = (pixels[j] >> 24) & 0xff;
+ if (alpha == 0) {
+ transparency = new int[2];
+ transparency[0] = transparency[1] = ((pixels[j] & 0x888) != 0) ? 1
+ : 0;
+ }
+ }
+ if ((pixels[j] & 0x888) != 0)
+ currByte |= cbyte;
+ cbyte >>= 1;
+ if (cbyte == 0 || wMarker + 1 >= w) {
+ pixelsByte[index++] = (byte) currByte;
+ cbyte = 0x80;
+ currByte = 0;
+ }
+ ++wMarker;
+ if (wMarker >= w)
+ wMarker = 0;
+ }
+ }
+ return Image.getInstance(w, h, 1, 1, pixelsByte, transparency);
+ } else {
+ byte[] pixelsByte = new byte[w * h * 3];
+ byte[] smask = null;
+
+ int index = 0;
+ int size = h * w;
+ int red = 255;
+ int green = 255;
+ int blue = 255;
+ if (color != null) {
+ red = color.getRed();
+ green = color.getGreen();
+ blue = color.getBlue();
+ }
+ int transparency[] = null;
+ if (color != null) {
+ for (int j = 0; j < size; j++) {
+ int alpha = (pixels[j] >> 24) & 0xff;
+ if (alpha < 250) {
+ pixelsByte[index++] = (byte) red;
+ pixelsByte[index++] = (byte) green;
+ pixelsByte[index++] = (byte) blue;
+ } else {
+ pixelsByte[index++] = (byte) ((pixels[j] >> 16) & 0xff);
+ pixelsByte[index++] = (byte) ((pixels[j] >> 8) & 0xff);
+ pixelsByte[index++] = (byte) ((pixels[j]) & 0xff);
+ }
+ }
+ } else {
+ int transparentPixel = 0;
+ smask = new byte[w * h];
+ boolean shades = false;
+ for (int j = 0; j < size; j++) {
+ byte alpha = smask[j] = (byte) ((pixels[j] >> 24) & 0xff);
+ /* bugfix by Chris Nokleberg */
+ if (!shades) {
+ if (alpha != 0 && alpha != -1) {
+ shades = true;
+ } else if (transparency == null) {
+ if (alpha == 0) {
+ transparentPixel = pixels[j] & 0xffffff;
+ transparency = new int[6];
+ transparency[0] = transparency[1] = (transparentPixel >> 16) & 0xff;
+ transparency[2] = transparency[3] = (transparentPixel >> 8) & 0xff;
+ transparency[4] = transparency[5] = transparentPixel & 0xff;
+ }
+ } else if ((pixels[j] & 0xffffff) != transparentPixel) {
+ shades = true;
+ }
+ }
+ pixelsByte[index++] = (byte) ((pixels[j] >> 16) & 0xff);
+ pixelsByte[index++] = (byte) ((pixels[j] >> 8) & 0xff);
+ pixelsByte[index++] = (byte) ((pixels[j]) & 0xff);
+ }
+ if (shades)
+ transparency = null;
+ else
+ smask = null;
+ }
+ Image img = Image.getInstance(w, h, 3, 8, pixelsByte, transparency);
+ if (smask != null) {
+ Image sm = Image.getInstance(w, h, 1, 8, smask);
+ try {
+ sm.makeMask();
+ img.setImageMask(sm);
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+ return img;
+ }
+ }
+
+ /**
+ * Gets an instance of an Image from a java.awt.Image.
+ *
+ * @param image
+ * the java.awt.Image
to convert
+ * @param color
+ * if different from null
the transparency pixels
+ * are replaced by this color
+ * @return an object of type ImgRaw
+ * @throws BadElementException
+ * on error
+ * @throws IOException
+ * on error
+ */
+ public static Image getInstance(java.awt.Image image, java.awt.Color color)
+ throws BadElementException, IOException {
+ return Image.getInstance(image, color, false);
+ }
+
+ /**
+ * Gets an instance of a Image from a java.awt.Image.
+ * The image is added as a JPEG with a userdefined quality.
+ *
+ * @param writer
+ * the PdfWriter
object to which the image will be added
+ * @param awtImage
+ * the java.awt.Image
to convert
+ * @param quality
+ * a float value between 0 and 1
+ * @return an object of type PdfTemplate
+ * @throws BadElementException
+ * on error
+ * @throws IOException
+ */
+ public static Image getInstance(PdfWriter writer, java.awt.Image awtImage, float quality) throws BadElementException, IOException {
+ return getInstance(new PdfContentByte(writer), awtImage, quality);
+ }
+
+ /**
+ * Gets an instance of a Image from a java.awt.Image.
+ * The image is added as a JPEG with a userdefined quality.
+ *
+ * @param cb
+ * the PdfContentByte
object to which the image will be added
+ * @param awtImage
+ * the java.awt.Image
to convert
+ * @param quality
+ * a float value between 0 and 1
+ * @return an object of type PdfTemplate
+ * @throws BadElementException
+ * on error
+ * @throws IOException
+ */
+ public static Image getInstance(PdfContentByte cb, java.awt.Image awtImage, float quality) throws BadElementException, IOException {
+ java.awt.image.PixelGrabber pg = new java.awt.image.PixelGrabber(awtImage,
+ 0, 0, -1, -1, true);
+ try {
+ pg.grabPixels();
+ } catch (InterruptedException e) {
+ throw new IOException(
+ "java.awt.Image Interrupted waiting for pixels!");
+ }
+ if ((pg.getStatus() & java.awt.image.ImageObserver.ABORT) != 0) {
+ throw new IOException("java.awt.Image fetch aborted or errored");
+ }
+ int w = pg.getWidth();
+ int h = pg.getHeight();
+ PdfTemplate tp = cb.createTemplate(w, h);
+ Graphics2D g2d = tp.createGraphics(w, h, true, quality);
+ g2d.drawImage(awtImage, 0, 0, null);
+ g2d.dispose();
+ return getInstance(tp);
+ }
+
+ /**
+ * Gets an instance of an Image.
+ *
+ * @param filename
+ * a filename
+ * @return an object of type Gif
,Jpeg
or
+ * Png
+ * @throws BadElementException
+ * @throws MalformedURLException
+ * @throws IOException
+ */
+
+ public static Image getInstance(String filename)
+ throws BadElementException, MalformedURLException, IOException {
+ return getInstance(toURL(filename));
+ }
+
+ /**
+ * Gets an instance of an Image in raw mode.
+ *
+ * @param width
+ * the width of the image in pixels
+ * @param height
+ * the height of the image in pixels
+ * @param components
+ * 1,3 or 4 for GrayScale, RGB and CMYK
+ * @param data
+ * the image data
+ * @param bpc
+ * bits per component
+ * @return an object of type ImgRaw
+ * @throws BadElementException
+ * on error
+ */
+
+ public static Image getInstance(int width, int height, int components,
+ int bpc, byte data[]) throws BadElementException {
+ return Image.getInstance(width, height, components, bpc, data, null);
+ }
+
+ /**
+ * Reuses an existing image.
+ * @param ref the reference to the image dictionary
+ * @throws BadElementException on error
+ * @return the image
+ */
+ public static Image getInstance(PRIndirectReference ref) throws BadElementException {
+ PdfDictionary dic = (PdfDictionary)PdfReader.getPdfObjectRelease(ref);
+ int width = ((PdfNumber)PdfReader.getPdfObjectRelease(dic.get(PdfName.WIDTH))).intValue();
+ int height = ((PdfNumber)PdfReader.getPdfObjectRelease(dic.get(PdfName.HEIGHT))).intValue();
+ Image imask = null;
+ PdfObject obj = dic.get(PdfName.SMASK);
+ if (obj != null && obj.isIndirect()) {
+ imask = getInstance((PRIndirectReference)obj);
+ }
+ else {
+ obj = dic.get(PdfName.MASK);
+ if (obj != null && obj.isIndirect()) {
+ PdfObject obj2 = PdfReader.getPdfObjectRelease(obj);
+ if (obj2 instanceof PdfDictionary)
+ imask = getInstance((PRIndirectReference)obj);
+ }
+ }
+ Image img = new ImgRaw(width, height, 1, 1, null);
+ img.imageMask = imask;
+ img.directReference = ref;
+ return img;
+ }
+
+ /**
+ * gets an instance of an Image
+ *
+ * @param template
+ * a PdfTemplate that has to be wrapped in an Image object
+ * @return an Image object
+ * @throws BadElementException
+ */
+ public static Image getInstance(PdfTemplate template)
+ throws BadElementException {
+ return new ImgTemplate(template);
+ }
+
+ /**
+ * Creates an Image with CCITT G3 or G4 compression. It assumes that the
+ * data bytes are already compressed.
+ *
+ * @param width
+ * the exact width of the image
+ * @param height
+ * the exact height of the image
+ * @param reverseBits
+ * reverses the bits in data
. Bit 0 is swapped
+ * with bit 7 and so on
+ * @param typeCCITT
+ * the type of compression in data
. It can be
+ * CCITTG4, CCITTG31D, CCITTG32D
+ * @param parameters
+ * parameters associated with this stream. Possible values are
+ * CCITT_BLACKIS1, CCITT_ENCODEDBYTEALIGN, CCITT_ENDOFLINE and
+ * CCITT_ENDOFBLOCK or a combination of them
+ * @param data
+ * the image data
+ * @return an Image object
+ * @throws BadElementException
+ * on error
+ */
+ public static Image getInstance(int width, int height, boolean reverseBits,
+ int typeCCITT, int parameters, byte[] data)
+ throws BadElementException {
+ return Image.getInstance(width, height, reverseBits, typeCCITT,
+ parameters, data, null);
+ }
+
+ /**
+ * Creates an Image with CCITT G3 or G4 compression. It assumes that the
+ * data bytes are already compressed.
+ *
+ * @param width
+ * the exact width of the image
+ * @param height
+ * the exact height of the image
+ * @param reverseBits
+ * reverses the bits in data
. Bit 0 is swapped
+ * with bit 7 and so on
+ * @param typeCCITT
+ * the type of compression in data
. It can be
+ * CCITTG4, CCITTG31D, CCITTG32D
+ * @param parameters
+ * parameters associated with this stream. Possible values are
+ * CCITT_BLACKIS1, CCITT_ENCODEDBYTEALIGN, CCITT_ENDOFLINE and
+ * CCITT_ENDOFBLOCK or a combination of them
+ * @param data
+ * the image data
+ * @param transparency
+ * transparency information in the Mask format of the image
+ * dictionary
+ * @return an Image object
+ * @throws BadElementException
+ * on error
+ */
+ public static Image getInstance(int width, int height, boolean reverseBits,
+ int typeCCITT, int parameters, byte[] data, int transparency[])
+ throws BadElementException {
+ if (transparency != null && transparency.length != 2)
+ throw new BadElementException(
+ "Transparency length must be equal to 2 with CCITT images");
+ Image img = new ImgCCITT(width, height, reverseBits, typeCCITT,
+ parameters, data);
+ img.transparency = transparency;
+ return img;
+ }
+
+ /**
+ * Gets an instance of an Image in raw mode.
+ *
+ * @param width
+ * the width of the image in pixels
+ * @param height
+ * the height of the image in pixels
+ * @param components
+ * 1,3 or 4 for GrayScale, RGB and CMYK
+ * @param data
+ * the image data
+ * @param bpc
+ * bits per component
+ * @param transparency
+ * transparency information in the Mask format of the image
+ * dictionary
+ * @return an object of type ImgRaw
+ * @throws BadElementException
+ * on error
+ */
+
+ public static Image getInstance(int width, int height, int components,
+ int bpc, byte data[], int transparency[])
+ throws BadElementException {
+ if (transparency != null && transparency.length != components * 2)
+ throw new BadElementException(
+ "Transparency length must be equal to (componentes * 2)");
+ if (components == 1 && bpc == 1) {
+ byte g4[] = CCITTG4Encoder.compress(data, width, height);
+ return Image.getInstance(width, height, false, Image.CCITTG4,
+ Image.CCITT_BLACKIS1, g4, transparency);
+ }
+ Image img = new ImgRaw(width, height, components, bpc, data);
+ img.transparency = transparency;
+ return img;
+ }
+
+ /**
+ * Returns an Image
that has been constructed taking in
+ * account the value of some attributes .
+ *
+ * @param attributes
+ * Some attributes
+ * @return an Image
+ * @throws BadElementException
+ * @throws MalformedURLException
+ * @throws IOException
+ */
+
+ public static Image getInstance(Properties attributes)
+ throws BadElementException, MalformedURLException, IOException {
+ String value = (String) attributes.remove(ElementTags.URL);
+ if (value == null)
+ throw new MalformedURLException("The URL of the image is missing.");
+ Image image = Image.getInstance(value);
+ int align = 0;
+ if ((value = (String) attributes.remove(ElementTags.ALIGN)) != null) {
+ if (ElementTags.ALIGN_LEFT.equalsIgnoreCase(value))
+ align |= Image.LEFT;
+ else if (ElementTags.ALIGN_RIGHT.equalsIgnoreCase(value))
+ align |= Image.RIGHT;
+ else if (ElementTags.ALIGN_MIDDLE.equalsIgnoreCase(value))
+ align |= Image.MIDDLE;
+ }
+ if ((value = (String) attributes.remove(ElementTags.UNDERLYING)) != null) {
+ if (new Boolean(value).booleanValue())
+ align |= Image.UNDERLYING;
+ }
+ if ((value = (String) attributes.remove(ElementTags.TEXTWRAP)) != null) {
+ if (new Boolean(value).booleanValue())
+ align |= Image.TEXTWRAP;
+ }
+ image.setAlignment(align);
+ if ((value = (String) attributes.remove(ElementTags.ALT)) != null) {
+ image.setAlt(value);
+ }
+ String x;
+ String y;
+ if (((x = (String) attributes.remove(ElementTags.ABSOLUTEX)) != null)
+ && ((y = (String) attributes.remove(ElementTags.ABSOLUTEY)) != null)) {
+ image.setAbsolutePosition(Float.valueOf(x + "f").floatValue(),
+ Float.valueOf(y + "f").floatValue());
+ }
+ if ((value = (String) attributes.remove(ElementTags.PLAINWIDTH)) != null) {
+ image.scaleAbsoluteWidth(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String) attributes.remove(ElementTags.PLAINHEIGHT)) != null) {
+ image.scaleAbsoluteHeight(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String) attributes.remove(ElementTags.ROTATION)) != null) {
+ image.setRotation(Float.valueOf(value + "f").floatValue());
+ }
+ if (attributes.size() > 0)
+ image.setMarkupAttributes(attributes);
+ return image;
+ }
+
+ // methods to set information
+
+ /**
+ * Sets the alignment for the image.
+ *
+ * @param alignment
+ * the alignment
+ */
+
+ public void setAlignment(int alignment) {
+ this.alignment = alignment;
+ }
+
+ /**
+ * Sets the alternative information for the image.
+ *
+ * @param alt
+ * the alternative information
+ */
+
+ public void setAlt(String alt) {
+ this.alt = alt;
+ }
+
+ /**
+ * Sets the absolute position of the Image
.
+ *
+ * @param absoluteX
+ * @param absoluteY
+ */
+
+ public void setAbsolutePosition(float absoluteX, float absoluteY) {
+ this.absoluteX = absoluteX;
+ this.absoluteY = absoluteY;
+ }
+
+ /**
+ * Scale the image to an absolute width and an absolute height.
+ *
+ * @param newWidth
+ * the new width
+ * @param newHeight
+ * the new height
+ */
+
+ public void scaleAbsolute(float newWidth, float newHeight) {
+ plainWidth = newWidth;
+ plainHeight = newHeight;
+ float[] matrix = matrix();
+ scaledWidth = matrix[DX] - matrix[CX];
+ scaledHeight = matrix[DY] - matrix[CY];
+ }
+
+ /**
+ * Scale the image to an absolute width.
+ *
+ * @param newWidth
+ * the new width
+ */
+
+ public void scaleAbsoluteWidth(float newWidth) {
+ plainWidth = newWidth;
+ float[] matrix = matrix();
+ scaledWidth = matrix[DX] - matrix[CX];
+ scaledHeight = matrix[DY] - matrix[CY];
+ }
+
+ /**
+ * Scale the image to an absolute height.
+ *
+ * @param newHeight
+ * the new height
+ */
+
+ public void scaleAbsoluteHeight(float newHeight) {
+ plainHeight = newHeight;
+ float[] matrix = matrix();
+ scaledWidth = matrix[DX] - matrix[CX];
+ scaledHeight = matrix[DY] - matrix[CY];
+ }
+
+ /**
+ * Scale the image to a certain percentage.
+ *
+ * @param percent
+ * the scaling percentage
+ */
+
+ public void scalePercent(float percent) {
+ scalePercent(percent, percent);
+ }
+
+ /**
+ * Scale the width and height of an image to a certain percentage.
+ *
+ * @param percentX
+ * the scaling percentage of the width
+ * @param percentY
+ * the scaling percentage of the height
+ */
+
+ public void scalePercent(float percentX, float percentY) {
+ plainWidth = (width() * percentX) / 100f;
+ plainHeight = (height() * percentY) / 100f;
+ float[] matrix = matrix();
+ scaledWidth = matrix[DX] - matrix[CX];
+ scaledHeight = matrix[DY] - matrix[CY];
+ }
+
+ /**
+ * Scales the image so that it fits a certain width and height.
+ *
+ * @param fitWidth
+ * the width to fit
+ * @param fitHeight
+ * the height to fit
+ */
+
+ public void scaleToFit(float fitWidth, float fitHeight) {
+ scalePercent(100);
+ float percentX = (fitWidth * 100) / scaledWidth();
+ float percentY = (fitHeight * 100) / scaledHeight();
+ scalePercent(percentX < percentY ? percentX : percentY);
+ }
+
+ /**
+ * Gets the current image rotation in radians.
+ * @return the current image rotation in radians
+ */
+ public float getImageRotation() {
+ float rot = (float) ((rotation - initialRotation) % (2.0 * Math.PI));
+ if (rot < 0) {
+ rot += 2.0 * Math.PI; //__IDS__
+ }
+ return rot;
+ }
+
+ /**
+ * Sets the rotation of the image in radians.
+ *
+ * @param r
+ * rotation in radians
+ */
+
+ public void setRotation(float r) {
+ double d = Math.PI; //__IDS__
+ rotation = (float) ((r + initialRotation) % (2.0 * d)); //__IDS__
+ if (rotation < 0) {
+ rotation += 2.0 * d; //__IDS__
+ }
+ float[] matrix = matrix();
+ scaledWidth = matrix[DX] - matrix[CX];
+ scaledHeight = matrix[DY] - matrix[CY];
+ }
+
+ /**
+ * Sets the rotation of the image in degrees.
+ *
+ * @param deg
+ * rotation in degrees
+ */
+
+ public void setRotationDegrees(float deg) {
+ double d = Math.PI; //__IDS__
+ setRotation(deg / 180 * (float) d); //__IDS__
+ }
+
+ /**
+ * Sets the annotation of this Image.
+ *
+ * @param annotation
+ * the annotation
+ */
+
+ public void setAnnotation(Annotation annotation) {
+ this.annotation = annotation;
+ }
+
+ /**
+ * Gets the annotation.
+ *
+ * @return the annotation that is linked to this image
+ */
+
+ public Annotation annotation() {
+ return annotation;
+ }
+
+ // methods to retrieve information
+
+ /**
+ * Gets the bpc for the image.
+ * RawImage
+ *
.
+ *
+ * @return a bpc value
+ */
+
+ public int bpc() {
+ return bpc;
+ }
+
+ /**
+ * Gets the raw data for the image.
+ * RawImage
+ *
.
+ *
+ * @return the raw data
+ */
+
+ public byte[] rawData() {
+ return rawData;
+ }
+
+ /**
+ * Gets the template to be used as an image.
+ * ImgTemplate
+ *
.
+ *
+ * @return the template
+ */
+
+ public PdfTemplate templateData() {
+ return template[0];
+ }
+
+ /**
+ * Sets data from a PdfTemplate
+ *
+ * @param template
+ * the template with the content
+ */
+ public void setTemplateData(PdfTemplate template) {
+ this.template[0] = template;
+ }
+
+ /**
+ * Checks if the Images
has to be added at an absolute
+ * position.
+ *
+ * @return a boolean
+ */
+
+ public boolean hasAbsolutePosition() {
+ return !Float.isNaN(absoluteY);
+ }
+
+ /**
+ * Checks if the Images
has to be added at an absolute X
+ * position.
+ *
+ * @return a boolean
+ */
+
+ public boolean hasAbsoluteX() {
+ return !Float.isNaN(absoluteX);
+ }
+
+ /**
+ * Returns the absolute X position.
+ *
+ * @return a position
+ */
+
+ public float absoluteX() {
+ return absoluteX;
+ }
+
+ /**
+ * Returns the absolute Y position.
+ *
+ * @return a position
+ */
+
+ public float absoluteY() {
+ return absoluteY;
+ }
+
+ /**
+ * Returns the type.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return type;
+ }
+
+ /**
+ * Returns true
if the image is a Jpeg
+ * -object.
+ *
+ * @return a boolean
+ */
+
+ public boolean isJpeg() {
+ return type == JPEG;
+ }
+
+ /**
+ * Returns true
if the image is a ImgRaw
+ * -object.
+ *
+ * @return a boolean
+ */
+
+ public boolean isImgRaw() {
+ return type == IMGRAW;
+ }
+
+ /**
+ * Returns true
if the image is an ImgTemplate
+ * -object.
+ *
+ * @return a boolean
+ */
+
+ public boolean isImgTemplate() {
+ return type == IMGTEMPLATE;
+ }
+
+ /**
+ * Gets the String
-representation of the reference to the
+ * image.
+ *
+ * @return a String
+ */
+
+ public URL url() {
+ return url;
+ }
+
+ /**
+ * Gets the alignment for the image.
+ *
+ * @return a value
+ */
+
+ public int alignment() {
+ return alignment;
+ }
+
+ /**
+ * Gets the alternative text for the image.
+ *
+ * @return a String
+ */
+
+ public String alt() {
+ return alt;
+ }
+
+ /**
+ * Gets the scaled width of the image.
+ *
+ * @return a value
+ */
+
+ public float scaledWidth() {
+ return scaledWidth;
+ }
+
+ /**
+ * Gets the scaled height of the image.
+ *
+ * @return a value
+ */
+
+ public float scaledHeight() {
+ return scaledHeight;
+ }
+
+ /**
+ * Gets the colorspace for the image.
+ * Jpeg
.
+ *
+ * @return a colorspace value
+ */
+
+ public int colorspace() {
+ return colorspace;
+ }
+
+ /**
+ * Returns the transformation matrix of the image.
+ *
+ * @return an array [AX, AY, BX, BY, CX, CY, DX, DY]
+ */
+
+ public float[] matrix() {
+ float[] matrix = new float[8];
+ float cosX = (float) Math.cos(rotation);
+ float sinX = (float) Math.sin(rotation);
+ matrix[AX] = plainWidth * cosX;
+ matrix[AY] = plainWidth * sinX;
+ matrix[BX] = (-plainHeight) * sinX;
+ matrix[BY] = plainHeight * cosX;
+ if (rotation < Math.PI / 2f) {
+ matrix[CX] = matrix[BX];
+ matrix[CY] = 0;
+ matrix[DX] = matrix[AX];
+ matrix[DY] = matrix[AY] + matrix[BY];
+ } else if (rotation < Math.PI) {
+ matrix[CX] = matrix[AX] + matrix[BX];
+ matrix[CY] = matrix[BY];
+ matrix[DX] = 0;
+ matrix[DY] = matrix[AY];
+ } else if (rotation < Math.PI * 1.5f) {
+ matrix[CX] = matrix[AX];
+ matrix[CY] = matrix[AY] + matrix[BY];
+ matrix[DX] = matrix[BX];
+ matrix[DY] = 0;
+ } else {
+ matrix[CX] = 0;
+ matrix[CY] = matrix[AY];
+ matrix[DX] = matrix[AX] + matrix[BX];
+ matrix[DY] = matrix[BY];
+ }
+ return matrix;
+ }
+
+ /**
+ * This method is an alternative for the InputStream.skip()
+ * -method that doesn't seem to work properly for big values of size
+ *
.
+ *
+ * @param is
+ * the InputStream
+ * @param size
+ * the number of bytes to skip
+ * @throws IOException
+ */
+
+ static public void skip(InputStream is, int size) throws IOException {
+ long n;
+ while (size > 0) {
+ n = is.skip(size);
+ if (n <= 0)
+ break;
+ size -= n;
+ }
+ }
+
+ private static String excUri = " <>#%\"{}[]|\\\u005E\u0060";
+ private static String[] excUriEsc = {"%20", "%3C", "%3E", "%23", "%25", "%22", "%7B", "%7D", "%5B", "%5D", "%7C", "%5C", "%5E", "%60"};
+
+ /**
+ * Holds value of property directReference.
+ */
+ private PdfIndirectReference directReference;
+
+ /**
+ * This method makes a valid URL from a given filename.
+ * true
if this Image
has the
+ * requisites to be a mask.
+ *
+ * @return true
if this Image
can be a mask
+ */
+ public boolean isMaskCandidate() {
+ if (type == IMGRAW) {
+ if (bpc > 0xff)
+ return true;
+ }
+ return colorspace == 1;
+ }
+
+ /**
+ * Make this Image
a mask.
+ *
+ * @throws DocumentException
+ * if this Image
can not be a mask
+ */
+ public void makeMask() throws DocumentException {
+ if (!isMaskCandidate())
+ throw new DocumentException("This image can not be an image mask.");
+ mask = true;
+ }
+
+ /**
+ * Sets the explicit masking.
+ *
+ * @param mask
+ * the mask to be applied
+ * @throws DocumentException
+ * on error
+ */
+ public void setImageMask(Image mask) throws DocumentException {
+ if (this.mask)
+ throw new DocumentException(
+ "An image mask cannot contain another image mask.");
+ if (!mask.mask)
+ throw new DocumentException(
+ "The image mask is not a mask. Did you do makeMask()?");
+ imageMask = mask;
+ smask = (mask.bpc > 1 && mask.bpc <= 8);
+ }
+
+ /**
+ * Gets the explicit masking.
+ *
+ * @return the explicit masking
+ */
+ public Image getImageMask() {
+ return imageMask;
+ }
+
+ /**
+ * Returns true
if this Image
is a mask.
+ *
+ * @return true
if this Image
is a mask
+ */
+ public boolean isMask() {
+ return mask;
+ }
+
+ /**
+ * Inverts the meaning of the bits of a mask.
+ *
+ * @param invert
+ * true
to invert the meaning of the bits of a
+ * mask
+ */
+ public void setInvertMask(boolean invert) {
+ this.invert = invert;
+ }
+
+ /**
+ * Returns true
if the bits are to be inverted in the mask.
+ *
+ * @return true
if the bits are to be inverted in the mask
+ */
+ public boolean isInvertMask() {
+ return invert;
+ }
+
+ /**
+ * Getter for the inverted value
+ *
+ * @return true if the image is inverted
+ */
+ public boolean isInverted() {
+ return invert;
+ }
+
+ /**
+ * Sets inverted true or false
+ *
+ * @param invert
+ * true or false
+ */
+ public void setInverted(boolean invert) {
+ this.invert = invert;
+ }
+
+ /**
+ * Getter for property interpolation.
+ *
+ * @return Value of property interpolation.
+ */
+ public boolean isInterpolation() {
+ return interpolation;
+ }
+
+ /**
+ * Sets the image interpolation. Image interpolation attempts to produce a
+ * smooth transition between adjacent sample values.
+ *
+ * @param interpolation
+ * New value of property interpolation.
+ */
+ public void setInterpolation(boolean interpolation) {
+ this.interpolation = interpolation;
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttribute(java.lang.String,
+ * java.lang.String)
+ */
+ public void setMarkupAttribute(String name, String value) {
+ if (markupAttributes == null) markupAttributes = new Properties();
+ markupAttributes.put(name, value);
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttributes(java.util.Properties)
+ */
+ public void setMarkupAttributes(Properties markupAttributes) {
+ this.markupAttributes = markupAttributes;
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttribute(java.lang.String)
+ */
+ public String getMarkupAttribute(String name) {
+ return (markupAttributes == null) ? null : String
+ .valueOf(markupAttributes.get(name));
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributeNames()
+ */
+ public Set getMarkupAttributeNames() {
+ return Chunk.getKeySet(markupAttributes);
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributes()
+ */
+ public Properties getMarkupAttributes() {
+ return markupAttributes;
+ }
+
+ /**
+ * Tags this image with an ICC profile.
+ *
+ * @param profile
+ * the profile
+ */
+ public void tagICC(ICC_Profile profile) {
+ this.profile = profile;
+ }
+
+ /**
+ * Checks is the image has an ICC profile.
+ *
+ * @return the ICC profile or null
+ */
+ public boolean hasICCProfile() {
+ return (this.profile != null);
+ }
+
+ /**
+ * Gets the images ICC profile.
+ *
+ * @return the ICC profile
+ */
+ public ICC_Profile getICCProfile() {
+ return profile;
+ }
+
+ /**
+ * Getter for property deflated.
+ *
+ * @return Value of property deflated.
+ *
+ */
+ public boolean isDeflated() {
+ return this.deflated;
+ }
+
+ /**
+ * Setter for property deflated.
+ *
+ * @param deflated
+ * New value of property deflated.
+ *
+ */
+ public void setDeflated(boolean deflated) {
+ this.deflated = deflated;
+ }
+
+ /**
+ * Getter for property indexed.
+ *
+ * @return Value of property indexed.
+ *
+ */
+ public PdfDictionary getAdditional() {
+ return this.additional;
+ }
+
+ /**
+ * Sets the /Colorspace key.
+ *
+ * @param additional
+ * New value of property indexed.
+ */
+ public void setAdditional(PdfDictionary additional) {
+ this.additional = additional;
+ }
+
+ /**
+ * Getter for property smask.
+ *
+ * @return Value of property smask.
+ *
+ */
+ public boolean isSmask() {
+ return this.smask;
+ }
+
+ /**
+ * Setter for property smask.
+ *
+ * @param smask
+ * New value of property smask.
+ *
+ */
+ public void setSmask(boolean smask) {
+ this.smask = smask;
+ }
+
+ /**
+ * Gets the X/Y pixel dimensionless aspect ratio.
+ *
+ * @return the X/Y pixel dimensionless aspect ratio
+ */
+ public float getXYRatio() {
+ return this.XYRatio;
+ }
+
+ /**
+ * Sets the X/Y pixel dimensionless aspect ratio.
+ *
+ * @param XYRatio
+ * the X/Y pixel dimensionless aspect ratio
+ */
+ public void setXYRatio(float XYRatio) {
+ this.XYRatio = XYRatio;
+ }
+
+ /**
+ * Gets the left indentation.
+ *
+ * @return the left indentation
+ */
+ public float indentationLeft() {
+ return indentationLeft;
+ }
+
+ /**
+ * Gets the right indentation.
+ *
+ * @return the right indentation
+ */
+ public float indentationRight() {
+ return indentationRight;
+ }
+
+ /**
+ * Sets the left indentation.
+ *
+ * @param f
+ */
+ public void setIndentationLeft(float f) {
+ indentationLeft = f;
+ }
+
+ /**
+ * Sets the right indentation.
+ *
+ * @param f
+ */
+ public void setIndentationRight(float f) {
+ indentationRight = f;
+ }
+
+ /**
+ * Getter for property originalType.
+ *
+ * @return Value of property originalType.
+ *
+ */
+ public int getOriginalType() {
+ return this.originalType;
+ }
+
+ /**
+ * Setter for property originalType.
+ *
+ * @param originalType
+ * New value of property originalType.
+ *
+ */
+ public void setOriginalType(int originalType) {
+ this.originalType = originalType;
+ }
+
+ /**
+ * Getter for property originalData.
+ *
+ * @return Value of property originalData.
+ *
+ */
+ public byte[] getOriginalData() {
+ return this.originalData;
+ }
+
+ /**
+ * Setter for property originalData.
+ *
+ * @param originalData
+ * New value of property originalData.
+ *
+ */
+ public void setOriginalData(byte[] originalData) {
+ this.originalData = originalData;
+ }
+
+ /**
+ * Sets the url of the image
+ *
+ * @param url
+ * the url of the image
+ */
+ public void setUrl(URL url) {
+ this.url = url;
+ }
+
+ /**
+ * Sets the spacing before this image.
+ *
+ * @param spacing
+ * the new spacing
+ */
+
+ public void setSpacingBefore(float spacing) {
+ this.spacingBefore = spacing;
+ }
+
+ /**
+ * Sets the spacing after this image.
+ *
+ * @param spacing
+ * the new spacing
+ */
+
+ public void setSpacingAfter(float spacing) {
+ this.spacingAfter = spacing;
+ }
+
+ /**
+ * Gets the spacing before this image.
+ *
+ * @return the spacing
+ */
+
+ public float spacingBefore() {
+ return spacingBefore;
+ }
+
+ /**
+ * Gets the spacing before this image.
+ *
+ * @return the spacing
+ */
+
+ public float spacingAfter() {
+ return spacingAfter;
+ }
+
+ /**
+ * Getter for property widthPercentage.
+ *
+ * @return Value of property widthPercentage.
+ */
+ public float getWidthPercentage() {
+ return this.widthPercentage;
+ }
+
+ /**
+ * Setter for property widthPercentage.
+ *
+ * @param widthPercentage
+ * New value of property widthPercentage.
+ */
+ public void setWidthPercentage(float widthPercentage) {
+ this.widthPercentage = widthPercentage;
+ }
+
+ /**
+ * Gets the layer this image belongs to.
+ *
+ * @return the layer this image belongs to or null
for no
+ * layer defined
+ */
+ public PdfOCG getLayer() {
+ return layer;
+ }
+
+ /**
+ * Sets the layer this image belongs to.
+ *
+ * @param layer
+ * the layer this image belongs to
+ */
+ public void setLayer(PdfOCG layer) {
+ this.layer = layer;
+ }
+
+ private PdfObject simplifyColorspace(PdfObject obj) {
+ if (obj == null || !obj.isArray())
+ return obj;
+ PdfObject first = (PdfObject)(((PdfArray)obj).getArrayList().get(0));
+ if (PdfName.CALGRAY.equals(first))
+ return PdfName.DEVICEGRAY;
+ else if (PdfName.CALRGB.equals(first))
+ return PdfName.DEVICERGB;
+ else
+ return obj;
+ }
+
+ /**
+ * Replaces CalRGB and CalGray colorspaces with DeviceRGB and DeviceGray.
+ */
+ public void simplifyColorspace() {
+ if (additional == null)
+ return;
+ PdfObject value = additional.get(PdfName.COLORSPACE);
+ if (value == null || !value.isArray())
+ return;
+ PdfObject cs = simplifyColorspace(value);
+ if (cs.isName())
+ value = cs;
+ else {
+ PdfObject first = (PdfObject)(((PdfArray)value).getArrayList().get(0));
+ if (PdfName.INDEXED.equals(first)) {
+ ArrayList array = ((PdfArray)value).getArrayList();
+ if (array.size() >= 2 && ((PdfObject)array.get(1)).isArray()) {
+ array.set(1, simplifyColorspace((PdfObject)array.get(1)));
+ }
+ }
+ }
+ additional.put(PdfName.COLORSPACE, value);
+ }
+
+ /**
+ * Getter for property initialRotation.
+ * @return Value of property initialRotation.
+ */
+ public float getInitialRotation() {
+ return this.initialRotation;
+ }
+
+ /**
+ * Some image formats, like TIFF may present the images rotated that have
+ * to be compensated.
+ * @param initialRotation New value of property initialRotation.
+ */
+ public void setInitialRotation(float initialRotation) {
+ float old_rot = rotation - this.initialRotation;
+ this.initialRotation = initialRotation;
+ setRotation(old_rot);
+ }
+
+ /**
+ * Getter for property directReference.
+ * @return Value of property directReference.
+ */
+ public PdfIndirectReference getDirectReference() {
+ return this.directReference;
+ }
+
+ /**
+ * Setter for property directReference.
+ * @param directReference New value of property directReference.
+ */
+ public void setDirectReference(PdfIndirectReference directReference) {
+ this.directReference = directReference;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/ImgCCITT.java b/src/main/java/com/lowagie/text/ImgCCITT.java
new file mode 100644
index 0000000..91fb50a
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ImgCCITT.java
@@ -0,0 +1,146 @@
+/*
+ * $Id: ImgCCITT.java,v 1.47 2005/05/04 14:31:06 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2000, 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.net.URL;
+
+/**
+ * CCITT Image data that has to be inserted into the document
+ *
+ * @see Element
+ * @see Image
+ *
+ * @author Paulo Soares
+ */
+
+public class ImgCCITT extends Image implements Element {
+ /** A table to do fast bit reversal.
+ */
+ static final byte bitReversal[] = {
+ (byte)0x00, (byte)0x80, (byte)0x40, (byte)0xc0, (byte)0x20, (byte)0xa0, (byte)0x60, (byte)0xe0,
+ (byte)0x10, (byte)0x90, (byte)0x50, (byte)0xd0, (byte)0x30, (byte)0xb0, (byte)0x70, (byte)0xf0,
+ (byte)0x08, (byte)0x88, (byte)0x48, (byte)0xc8, (byte)0x28, (byte)0xa8, (byte)0x68, (byte)0xe8,
+ (byte)0x18, (byte)0x98, (byte)0x58, (byte)0xd8, (byte)0x38, (byte)0xb8, (byte)0x78, (byte)0xf8,
+ (byte)0x04, (byte)0x84, (byte)0x44, (byte)0xc4, (byte)0x24, (byte)0xa4, (byte)0x64, (byte)0xe4,
+ (byte)0x14, (byte)0x94, (byte)0x54, (byte)0xd4, (byte)0x34, (byte)0xb4, (byte)0x74, (byte)0xf4,
+ (byte)0x0c, (byte)0x8c, (byte)0x4c, (byte)0xcc, (byte)0x2c, (byte)0xac, (byte)0x6c, (byte)0xec,
+ (byte)0x1c, (byte)0x9c, (byte)0x5c, (byte)0xdc, (byte)0x3c, (byte)0xbc, (byte)0x7c, (byte)0xfc,
+ (byte)0x02, (byte)0x82, (byte)0x42, (byte)0xc2, (byte)0x22, (byte)0xa2, (byte)0x62, (byte)0xe2,
+ (byte)0x12, (byte)0x92, (byte)0x52, (byte)0xd2, (byte)0x32, (byte)0xb2, (byte)0x72, (byte)0xf2,
+ (byte)0x0a, (byte)0x8a, (byte)0x4a, (byte)0xca, (byte)0x2a, (byte)0xaa, (byte)0x6a, (byte)0xea,
+ (byte)0x1a, (byte)0x9a, (byte)0x5a, (byte)0xda, (byte)0x3a, (byte)0xba, (byte)0x7a, (byte)0xfa,
+ (byte)0x06, (byte)0x86, (byte)0x46, (byte)0xc6, (byte)0x26, (byte)0xa6, (byte)0x66, (byte)0xe6,
+ (byte)0x16, (byte)0x96, (byte)0x56, (byte)0xd6, (byte)0x36, (byte)0xb6, (byte)0x76, (byte)0xf6,
+ (byte)0x0e, (byte)0x8e, (byte)0x4e, (byte)0xce, (byte)0x2e, (byte)0xae, (byte)0x6e, (byte)0xee,
+ (byte)0x1e, (byte)0x9e, (byte)0x5e, (byte)0xde, (byte)0x3e, (byte)0xbe, (byte)0x7e, (byte)0xfe,
+ (byte)0x01, (byte)0x81, (byte)0x41, (byte)0xc1, (byte)0x21, (byte)0xa1, (byte)0x61, (byte)0xe1,
+ (byte)0x11, (byte)0x91, (byte)0x51, (byte)0xd1, (byte)0x31, (byte)0xb1, (byte)0x71, (byte)0xf1,
+ (byte)0x09, (byte)0x89, (byte)0x49, (byte)0xc9, (byte)0x29, (byte)0xa9, (byte)0x69, (byte)0xe9,
+ (byte)0x19, (byte)0x99, (byte)0x59, (byte)0xd9, (byte)0x39, (byte)0xb9, (byte)0x79, (byte)0xf9,
+ (byte)0x05, (byte)0x85, (byte)0x45, (byte)0xc5, (byte)0x25, (byte)0xa5, (byte)0x65, (byte)0xe5,
+ (byte)0x15, (byte)0x95, (byte)0x55, (byte)0xd5, (byte)0x35, (byte)0xb5, (byte)0x75, (byte)0xf5,
+ (byte)0x0d, (byte)0x8d, (byte)0x4d, (byte)0xcd, (byte)0x2d, (byte)0xad, (byte)0x6d, (byte)0xed,
+ (byte)0x1d, (byte)0x9d, (byte)0x5d, (byte)0xdd, (byte)0x3d, (byte)0xbd, (byte)0x7d, (byte)0xfd,
+ (byte)0x03, (byte)0x83, (byte)0x43, (byte)0xc3, (byte)0x23, (byte)0xa3, (byte)0x63, (byte)0xe3,
+ (byte)0x13, (byte)0x93, (byte)0x53, (byte)0xd3, (byte)0x33, (byte)0xb3, (byte)0x73, (byte)0xf3,
+ (byte)0x0b, (byte)0x8b, (byte)0x4b, (byte)0xcb, (byte)0x2b, (byte)0xab, (byte)0x6b, (byte)0xeb,
+ (byte)0x1b, (byte)0x9b, (byte)0x5b, (byte)0xdb, (byte)0x3b, (byte)0xbb, (byte)0x7b, (byte)0xfb,
+ (byte)0x07, (byte)0x87, (byte)0x47, (byte)0xc7, (byte)0x27, (byte)0xa7, (byte)0x67, (byte)0xe7,
+ (byte)0x17, (byte)0x97, (byte)0x57, (byte)0xd7, (byte)0x37, (byte)0xb7, (byte)0x77, (byte)0xf7,
+ (byte)0x0f, (byte)0x8f, (byte)0x4f, (byte)0xcf, (byte)0x2f, (byte)0xaf, (byte)0x6f, (byte)0xef,
+ (byte)0x1f, (byte)0x9f, (byte)0x5f, (byte)0xdf, (byte)0x3f, (byte)0xbf, (byte)0x7f, (byte)0xff
+ };
+
+ /** Reverses the bits in the array.
+ * @param data data to reverse bits
+ */
+ public static void ReverseBits(byte data[]) {
+ for (int k = 0; k < data.length; ++k)
+ data[k] = bitReversal[(int)data[k] & 0xff];
+ }
+
+ ImgCCITT(Image image) {
+ super(image);
+ }
+
+ /** Creates an Image with CCITT compression.
+ *
+ * @param width the exact width of the image
+ * @param height the exact height of the image
+ * @param reverseBits reverses the bits in data
.
+ * Bit 0 is swapped with bit 7 and so on
+ * @param typeCCITT the type of compression in data
. It can be
+ * CCITTG4, CCITTG31D, CCITTG32D
+ * @param parameters parameters associated with this stream. Possible values are
+ * CCITT_BLACKIS1, CCITT_ENCODEDBYTEALIGN, CCITT_ENDOFLINE and CCITT_ENDOFBLOCK or a
+ * combination of them
+ * @param data the image data
+ * @throws BadElementException on error
+ */
+
+ public ImgCCITT(int width, int height, boolean reverseBits, int typeCCITT, int parameters, byte[] data) throws BadElementException{
+ super((URL)null);
+ if (typeCCITT != CCITTG4 && typeCCITT != CCITTG3_1D && typeCCITT != CCITTG3_2D)
+ throw new BadElementException("The CCITT compression type must be CCITTG4, CCITTG3_1D or CCITTG3_2D");
+ if (reverseBits)
+ ReverseBits(data);
+ type = IMGRAW;
+ scaledHeight = height;
+ setTop(scaledHeight);
+ scaledWidth = width;
+ setRight(scaledWidth);
+ colorspace = parameters;
+ bpc = typeCCITT;
+ rawData = data;
+ plainWidth = width();
+ plainHeight = height();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/ImgPostscript.java b/src/main/java/com/lowagie/text/ImgPostscript.java
new file mode 100644
index 0000000..3d6d902
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ImgPostscript.java
@@ -0,0 +1,234 @@
+/*
+ * $Id: ImgPostscript.java,v 1.6 2006/04/22 16:56:40 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.io.*;
+import java.net.*;
+import com.lowagie.text.pdf.*;
+import com.lowagie.text.pdf.codec.postscript.*;
+import java.util.StringTokenizer;
+
+/**
+ * An ImgPostscript
is the representation of an EPS
+ * that has to be inserted into the document
+ *
+ * @see Element
+ * @see Image
+ */
+
+public class ImgPostscript
+extends Image
+implements Element {
+
+ // Constructors
+
+ ImgPostscript(Image image) {
+ super(image);
+ }
+
+ public ImgPostscript(byte[] content,float width,float height) throws
+ BadElementException, IOException {
+ super( (URL)null);
+ rawData = content;
+ originalData = content;
+ processParameters();
+ this.urx=width;
+ this.ury=height;
+ }
+ /**
+ * Constructs an ImgPostscript
-object, using an url.
+ *
+ * @param url the URL
where the image can be found
+ * @throws BadElementException on error
+ * @throws IOException on error
+ */
+
+ public ImgPostscript(URL url) throws BadElementException, IOException {
+ super(url);
+ processParameters();
+ }
+
+ /**
+ * Constructs an ImgPostscript
-object, using a filename.
+ *
+ * @param filename a String
-representation of the file that contains the image.
+ * @throws BadElementException on error
+ * @throws MalformedURLException on error
+ * @throws IOException on error
+ */
+
+ public ImgPostscript(String filename) throws BadElementException,
+ MalformedURLException, IOException {
+ this(Image.toURL(filename));
+ }
+
+ /**
+ * Constructs an ImgPostscript
-object from memory.
+ *
+ * @param img the memory image
+ * @throws BadElementException on error
+ * @throws IOException on error
+ */
+
+ public ImgPostscript(byte[] img) throws BadElementException, IOException {
+ super( (URL)null);
+ rawData = img;
+ originalData = img;
+ processParameters();
+ }
+
+
+ /**
+ * This method checks if the image is a valid Postscript and processes some parameters.
+ * @throws BadElementException
+ * @throws IOException
+ */
+
+ private void processParameters() throws BadElementException, IOException {
+ type = IMGTEMPLATE;
+ originalType = ORIGINAL_PS;
+ InputStream is = null;
+ try {
+ if (rawData == null) {
+ is = url.openStream();
+ }
+ else {
+ is = new java.io.ByteArrayInputStream(rawData);
+ }
+ String boundingbox=null;
+ String templatebox=null;
+ Reader r = new BufferedReader(new InputStreamReader(is));
+ // StreamTokenizer st = new StreamTokenizer(r);
+ while (r.ready()) {
+ char c;
+ StringBuffer sb = new StringBuffer();
+ while ( (c = ( (char) r.read())) != '\n'&&c!='\r') {
+ sb.append(c);
+ }
+ //System.out.println("<<" + sb.toString() + ">>");
+ if (sb.toString().startsWith("%%BoundingBox:")) {
+ boundingbox = sb.toString();
+
+ }
+ if (sb.toString().startsWith("%%TemplateBox:")) {
+ templatebox = sb.toString();
+ }
+ if (sb.toString().startsWith("%%EndComments")) {
+ break;
+ }
+ if ((!sb.toString().startsWith("%%"))&&(!sb.toString().startsWith("%!"))) {
+ break;
+ }
+
+ }
+ if(boundingbox==null){
+ scaledHeight=PageSize.A4.height();
+ setTop(scaledHeight);
+ scaledWidth=PageSize.A4.width();
+ setRight(scaledWidth);
+ return;
+ }
+ StringTokenizer st=new StringTokenizer(boundingbox,": \r\n");
+ st.nextElement();
+ String xx1=st.nextToken();
+ String yy1=st.nextToken();
+ String xx2=st.nextToken();
+ String yy2=st.nextToken();
+
+ int left = Integer.parseInt(xx1);
+ int bottom = Integer.parseInt(yy1);
+ int right = Integer.parseInt(xx2);
+ int top = Integer.parseInt(yy2);
+ int inch = 1;
+ dpiX = 72;
+ dpiY = 72;
+ scaledHeight = (float) (top-bottom ) / inch *1f;
+ setTop(top);
+ scaledWidth = (float) (right - left) / inch * 1f;
+ setRight(right);
+ }
+ finally {
+ if (is != null) {
+ is.close();
+ }
+ plainWidth = width();
+ plainHeight = height();
+ }
+ }
+
+ /** Reads the Postscript into a template.
+ * @param template the template to read to
+ * @throws IOException on error
+ * @throws DocumentException on error
+ */
+ public void readPostscript(PdfTemplate template) throws IOException,
+ DocumentException {
+ setTemplateData(template);
+ template.setWidth(width());
+ template.setHeight(height());
+ InputStream is = null;
+ try {
+ if (rawData == null) {
+ is = url.openStream();
+ }
+ else {
+ is = new java.io.ByteArrayInputStream(rawData);
+ }
+ MetaDoPS meta = new MetaDoPS(is, template);
+ meta.readAll();
+ }
+ finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/ImgRaw.java b/src/main/java/com/lowagie/text/ImgRaw.java
new file mode 100644
index 0000000..813e8ae
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ImgRaw.java
@@ -0,0 +1,97 @@
+/*
+ * $Id: ImgRaw.java,v 1.61 2005/05/04 14:31:11 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2000, 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.net.URL;
+
+/**
+ * Raw Image data that has to be inserted into the document
+ *
+ * @see Element
+ * @see Image
+ *
+ * @author Paulo Soares
+ */
+
+public class ImgRaw extends Image implements Element {
+
+ ImgRaw(Image image) {
+ super(image);
+ }
+
+/** Creats an Image in raw mode.
+ *
+ * @param width the exact width of the image
+ * @param height the exact height of the image
+ * @param components 1,3 or 4 for GrayScale, RGB and CMYK
+ * @param bpc bits per component. Must be 1,2,4 or 8
+ * @param data the image data
+ * @throws BadElementException on error
+ */
+
+ public ImgRaw(int width, int height, int components, int bpc, byte[] data) throws BadElementException{
+ super((URL)null);
+ type = IMGRAW;
+ scaledHeight = height;
+ setTop(scaledHeight);
+ scaledWidth = width;
+ setRight(scaledWidth);
+ if (components != 1 && components != 3 && components != 4)
+ throw new BadElementException("Components must be 1, 3, or 4.");
+ if (bpc != 1 && bpc != 2 && bpc != 4 && bpc != 8)
+ throw new BadElementException("Bits-per-component must be 1, 2, 4, or 8.");
+ colorspace = components;
+ this.bpc = bpc;
+ rawData = data;
+ plainWidth = width();
+ plainHeight = height();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/ImgTemplate.java b/src/main/java/com/lowagie/text/ImgTemplate.java
new file mode 100644
index 0000000..5cc01bf
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ImgTemplate.java
@@ -0,0 +1,91 @@
+/*
+ * $Id: ImgTemplate.java,v 1.52 2005/05/04 14:31:10 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2000, 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.net.URL;
+import com.lowagie.text.pdf.PdfTemplate;
+
+/**
+ * PdfTemplate that has to be inserted into the document
+ *
+ * @see Element
+ * @see Image
+ *
+ * @author Paulo Soares
+ */
+
+public class ImgTemplate extends Image implements Element {
+
+ ImgTemplate(Image image) {
+ super(image);
+ }
+
+ /** Creats an Image from a PdfTemplate.
+ *
+ * @param template the PdfTemplate
+ * @throws BadElementException on error
+ */
+ public ImgTemplate(PdfTemplate template) throws BadElementException{
+ super((URL)null);
+ if (template == null)
+ throw new BadElementException("The template can not be null.");
+ if (template.getType() == PdfTemplate.TYPE_PATTERN)
+ throw new BadElementException("A pattern can not be used as a template to create an image.");
+ type = IMGTEMPLATE;
+ scaledHeight = template.getHeight();
+ setTop(scaledHeight);
+ scaledWidth = template.getWidth();
+ setRight(scaledWidth);
+ setTemplateData(template);
+ plainWidth = width();
+ plainHeight = height();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/ImgWMF.java b/src/main/java/com/lowagie/text/ImgWMF.java
new file mode 100644
index 0000000..25b989c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ImgWMF.java
@@ -0,0 +1,190 @@
+/*
+ * $Id: ImgWMF.java,v 1.47 2005/05/04 14:31:10 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import com.lowagie.text.pdf.codec.wmf.InputMeta;
+import com.lowagie.text.pdf.codec.wmf.MetaDo;
+import com.lowagie.text.pdf.*;
+
+/**
+ * An ImgWMF
is the representation of a windows metafile
+ * that has to be inserted into the document
+ *
+ * @see Element
+ * @see Image
+ */
+
+public class ImgWMF extends Image implements Element {
+
+ // Constructors
+
+ ImgWMF(Image image) {
+ super(image);
+ }
+
+ /**
+ * Constructs an ImgWMF
-object, using an url.
+ *
+ * @param url the URL
where the image can be found
+ * @throws BadElementException on error
+ * @throws IOException on error
+ */
+
+ public ImgWMF(URL url) throws BadElementException, IOException {
+ super(url);
+ processParameters();
+ }
+
+ /**
+ * Constructs an ImgWMF
-object, using a filename.
+ *
+ * @param filename a String
-representation of the file that contains the image.
+ * @throws BadElementException on error
+ * @throws MalformedURLException on error
+ * @throws IOException on error
+ */
+
+ public ImgWMF(String filename) throws BadElementException, MalformedURLException, IOException {
+ this(Image.toURL(filename));
+ }
+
+ /**
+ * Constructs an ImgWMF
-object from memory.
+ *
+ * @param img the memory image
+ * @throws BadElementException on error
+ * @throws IOException on error
+ */
+
+ public ImgWMF(byte[] img) throws BadElementException, IOException {
+ super((URL)null);
+ rawData = img;
+ originalData = img;
+ processParameters();
+ }
+
+/**
+ * This method checks if the image is a valid WMF and processes some parameters.
+ * @throws BadElementException
+ * @throws IOException
+ */
+
+ private void processParameters() throws BadElementException, IOException {
+ type = IMGTEMPLATE;
+ originalType = ORIGINAL_WMF;
+ InputStream is = null;
+ try {
+ String errorID;
+ if (rawData == null){
+ is = url.openStream();
+ errorID = url.toString();
+ }
+ else{
+ is = new java.io.ByteArrayInputStream(rawData);
+ errorID = "Byte array";
+ }
+ InputMeta in = new InputMeta(is);
+ if (in.readInt() != 0x9AC6CDD7) {
+ throw new BadElementException(errorID + " is not a valid placeable windows metafile.");
+ }
+ in.readWord();
+ int left = in.readShort();
+ int top = in.readShort();
+ int right = in.readShort();
+ int bottom = in.readShort();
+ int inch = in.readWord();
+ dpiX = 72;
+ dpiY = 72;
+ scaledHeight = (float)(bottom - top) / inch * 72f;
+ setTop(scaledHeight);
+ scaledWidth = (float)(right - left) / inch * 72f;
+ setRight(scaledWidth);
+ }
+ finally {
+ if (is != null) {
+ is.close();
+ }
+ plainWidth = width();
+ plainHeight = height();
+ }
+ }
+
+ /** Reads the WMF into a template.
+ * @param template the template to read to
+ * @throws IOException on error
+ * @throws DocumentException on error
+ */
+ public void readWMF(PdfTemplate template) throws IOException, DocumentException {
+ setTemplateData(template);
+ template.setWidth(width());
+ template.setHeight(height());
+ InputStream is = null;
+ try {
+ if (rawData == null){
+ is = url.openStream();
+ }
+ else{
+ is = new java.io.ByteArrayInputStream(rawData);
+ }
+ MetaDo meta = new MetaDo(is, template);
+ meta.readAll();
+ }
+ finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/Jpeg.java b/src/main/java/com/lowagie/text/Jpeg.java
new file mode 100644
index 0000000..1b95486
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Jpeg.java
@@ -0,0 +1,350 @@
+/*
+ * $Id: Jpeg.java,v 1.59 2004/12/14 12:33:47 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * An Jpeg
is the representation of a graphic element (JPEG)
+ * that has to be inserted into the document
+ *
+ * @see Element
+ * @see Image
+ */
+
+public class Jpeg extends Image implements Element {
+
+ // public static final membervariables
+
+ /** This is a type of marker. */
+ public static final int NOT_A_MARKER = -1;
+
+ /** This is a type of marker. */
+ public static final int VALID_MARKER = 0;
+
+ /** Acceptable Jpeg markers. */
+ public static final int[] VALID_MARKERS = {0xC0, 0xC1, 0xC2};
+
+ /** This is a type of marker. */
+ public static final int UNSUPPORTED_MARKER = 1;
+
+ /** Unsupported Jpeg markers. */
+ public static final int[] UNSUPPORTED_MARKERS = {0xC3, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF};
+
+ /** This is a type of marker. */
+ public static final int NOPARAM_MARKER = 2;
+
+ /** Jpeg markers without additional parameters. */
+ public static final int[] NOPARAM_MARKERS = {0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0x01};
+
+ /** Marker value */
+ public static final int M_APP0 = 0xE0;
+ /** Marker value */
+ public static final int M_APPE = 0xEE;
+
+ /** sequence that is used in all Jpeg files */
+ public static final byte JFIF_ID[] = {0x4A, 0x46, 0x49, 0x46, 0x00};
+ // Constructors
+
+ Jpeg(Image image) {
+ super(image);
+ }
+
+ /**
+ * Constructs a Jpeg
-object, using an url.
+ *
+ * @param url the URL
where the image can be found
+ * @throws BadElementException
+ * @throws IOException
+ */
+
+ public Jpeg(URL url) throws BadElementException, IOException {
+ super(url);
+ processParameters();
+ }
+
+ /**
+ * Constructs a Jpeg
-object, using an url.
+ *
+ * @param url the URL
where the image can be found.
+ * @param width new width of the Jpeg
+ * @param height new height of the Jpeg
+ * @throws BadElementException
+ * @throws IOException
+ * @deprecated use Image.getInstance(...) to create an Image
+ */
+
+ public Jpeg(URL url, float width, float height) throws BadElementException, IOException {
+ this(url);
+ scaledWidth = width;
+ scaledHeight = height;
+ }
+
+ /**
+ * Constructs a Jpeg
-object, using a filename.
+ *
+ * @param filename a String
-representation of the file that contains the Image.
+ * @throws BadElementException
+ * @throws MalformedURLException
+ * @throws IOException
+ * @deprecated use Image.getInstance(...) to create an Image
+ */
+
+ public Jpeg(String filename) throws BadElementException, MalformedURLException, IOException {
+ this(Image.toURL(filename));
+ }
+
+ /**
+ * Constructs a Jpeg
-object, using a filename.
+ *
+ * @param filename a String
-representation of the file that contains the Image.
+ * @param width new width of the Jpeg
+ * @param height new height of the Jpeg
+ * @throws BadElementException
+ * @throws MalformedURLException
+ * @throws IOException
+ * @deprecated use Image.getInstance(...) to create an Image
+ */
+
+ public Jpeg(String filename, float width, float height) throws BadElementException, MalformedURLException, IOException {
+ this(Image.toURL(filename), width, height);
+ }
+
+ /**
+ * Constructs a Jpeg
-object from memory.
+ *
+ * @param img the memory image
+ * @throws BadElementException
+ * @throws IOException
+ */
+
+ public Jpeg(byte[] img) throws BadElementException, IOException {
+ super((URL)null);
+ rawData = img;
+ originalData = img;
+ processParameters();
+ }
+
+ /**
+ * Constructs a Jpeg
-object from memory.
+ *
+ * @param img the memory image.
+ * @param width the width you want the image to have
+ * @param height the height you want the image to have
+ * @throws BadElementException
+ * @throws IOException
+ */
+
+ public Jpeg(byte[] img, float width, float height) throws BadElementException, IOException {
+ this(img);
+ scaledWidth = width;
+ scaledHeight = height;
+ }
+
+ // private static methods
+
+ /**
+ * Reads a short from the InputStream
.
+ *
+ * @param is the InputStream
+ * @return an int
+ * @throws IOException
+ */
+
+ private static final int getShort(InputStream is) throws IOException {
+ return (is.read() << 8) + is.read();
+ }
+
+ /**
+ * Returns a type of marker.
+ *
+ * @param marker an int
+ * @return a type: VALID_MARKERList
contains several ListItem
s.
+ *
+ *
+ * The result of this code looks like this:
+ *
+ * List list = new List(true, 20);
+ * list.add(new ListItem("First line"));
+ * list.add(new ListItem("The second line is longer to see what happens once the end of the line is reached. Will it start on a new line?"));
+ * list.add(new ListItem("Third line"));
+ *
+ *
+ *
+ * Example 2:
+ *
+ *
+ * The result of this code looks like this:
+ *
+ * List overview = new List(false, 10);
+ * overview.add(new ListItem("This is an item"));
+ * overview.add("This is another item");
+ *
+ *
+ *
+ * @see Element
+ * @see ListItem
+ */
+
+public class List implements TextElementArray, MarkupAttributes {
+
+ // membervariables
+ /** a possible value for the numbered parameter */
+ public static final boolean ORDERED = true;
+ /** a possible value for the numbered parameter */
+ public static final boolean UNORDERED = false;
+ /** a possible value for the lettered parameter */
+ public static final boolean NUMBERICAL = false;
+ /** a possible value for the lettered parameter */
+ public static final boolean ALPHABETICAL = true;
+
+
+/** This is the ArrayList
containing the different ListItem
s. */
+ protected ArrayList list = new ArrayList();
+
+/** This variable indicates if the list has to be numbered. */
+ protected boolean numbered;
+ protected boolean lettered;
+
+/** This variable indicates the first number of a numbered list. */
+ protected int first = 1;
+ protected char firstCh = 'A';
+ protected char lastCh = 'Z';
+
+/** This is the listsymbol of a list that is not numbered. */
+ protected Chunk symbol = new Chunk("-");
+
+/** The indentation of this list on the left side. */
+ protected float indentationLeft = 0;
+
+/** The indentation of this list on the right side. */
+ protected float indentationRight = 0;
+
+/** The indentation of the listitems. */
+ protected float symbolIndent;
+
+/** Contains extra markupAttributes */
+ protected Properties markupAttributes;
+
+ // constructors
+
+/**
+ * Constructs a List
.
+ * List
that has been constructed taking in account
+ * the value of some attributes.
+ *
+ * @param attributes Some attributes
+ */
+
+ public List(Properties attributes) {
+ String value= (String)attributes.remove(ElementTags.LISTSYMBOL);
+ if (value == null) {
+ value = "-";
+ }
+ symbol = new Chunk(value, FontFactory.getFont(attributes));
+
+ this.numbered = false;
+ if ((value = (String)attributes.remove(ElementTags.NUMBERED)) != null) {
+ this.numbered = new Boolean(value).booleanValue();
+ if ( this.lettered && this.numbered )
+ this.lettered = false;
+ }
+ if ((value = (String)attributes.remove(ElementTags.LETTERED)) != null) {
+ this.lettered = new Boolean(value).booleanValue();
+ if ( this.numbered && this.lettered )
+ this.numbered = false;
+ }
+ this.symbolIndent = 0;
+ if ((value = (String)attributes.remove(ElementTags.SYMBOLINDENT)) != null) {
+ this.symbolIndent = Float.parseFloat(value);
+ }
+
+ if ((value = (String)attributes.remove(ElementTags.FIRST)) != null) {
+ char khar = value.charAt(0);
+ if ( Character.isLetter( khar ) ) {
+ setFirst( khar );
+ }
+ else {
+ setFirst(Integer.parseInt(value));
+ }
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENTATIONLEFT)) != null) {
+ setIndentationLeft(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENTATIONRIGHT)) != null) {
+ setIndentationRight(Float.valueOf(value + "f").floatValue());
+ }
+ if (attributes.size() > 0) setMarkupAttributes(attributes);
+ }
+
+ // implementation of the Element-methods
+
+/**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener an ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ for (Iterator i = list.iterator(); i.hasNext(); ) {
+ listener.add((Element) i.next());
+ }
+ return true;
+ }
+ catch(DocumentException de) {
+ return false;
+ }
+ }
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.LIST;
+ }
+
+/**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ ArrayList tmp = new ArrayList();
+ for (Iterator i = list.iterator(); i.hasNext(); ) {
+ tmp.addAll(((Element) i.next()).getChunks());
+ }
+ return tmp;
+ }
+
+ // methods to set the membervariables
+
+/**
+ * Adds an Object
to the List
.
+ *
+ * @param o the object to add.
+ * @return true if adding the object succeeded
+ */
+
+ public boolean add(Object o) {
+ if (o instanceof ListItem) {
+ ListItem item = (ListItem) o;
+ if (numbered || lettered) {
+ Chunk chunk;
+ if ( lettered )
+ chunk = new Chunk(nextLetter(), symbol.font());
+ else
+ chunk = new Chunk(String.valueOf(first + list.size()), symbol.font());
+ chunk.append(".");
+ item.setListSymbol(chunk);
+ }
+ else {
+ item.setListSymbol(symbol);
+ }
+ item.setIndentationLeft(symbolIndent);
+ item.setIndentationRight(0);
+ list.add(item);
+ }
+ else if (o instanceof List) {
+ List nested = (List) o;
+ nested.setIndentationLeft(nested.indentationLeft() + symbolIndent);
+ first--;
+ return list.add(nested);
+ }
+ else if (o instanceof String) {
+ return this.add(new ListItem((String) o));
+ }
+ return false;
+ }
+
+/**
+ * Sets the indentation of this paragraph on the left side.
+ *
+ * @param indentation the new indentation
+ */
+
+ public void setIndentationLeft(float indentation) {
+ this.indentationLeft = indentation;
+ }
+
+/**
+ * Sets the indentation of this paragraph on the right side.
+ *
+ * @param indentation the new indentation
+ */
+
+ public void setIndentationRight(float indentation) {
+ this.indentationRight = indentation;
+ }
+
+/**
+ * Sets the number that has to come first in the list.
+ *
+ * @param first a number
+ */
+
+ public void setFirst(int first) {
+ this.first = first;
+ }
+
+
+/**
+ * Sets the Letter that has to come first in the list.
+ *
+ * @param first a letter
+ */
+
+ public void setFirst(char first) {
+ this.firstCh = first;
+ if ( Character.isLowerCase( this.firstCh )) {
+ this.lastCh = 'z';
+ }
+ else {
+ this.lastCh = 'Z';
+ }
+ }
+
+/**
+ * Sets the listsymbol.
+ *
+ * @param symbol a Chunk
+ */
+
+ public void setListSymbol(Chunk symbol) {
+ this.symbol = symbol;
+ }
+
+/**
+ * Sets the listsymbol.
+ * setListSymbol(Chunk symbol)
.
+ *
+ * @param symbol a String
+ */
+
+ public void setListSymbol(String symbol) {
+ this.symbol = new Chunk(symbol);
+ }
+
+ // methods to retrieve information
+
+/**
+ * Gets all the items in the list.
+ *
+ * @return an ArrayList
containing ListItem
s.
+ */
+
+ public ArrayList getItems() {
+ return list;
+ }
+
+/**
+ * Gets the size of the list.
+ *
+ * @return a size
+ */
+
+ public int size() {
+ return list.size();
+ }
+
+/**
+ * Gets the leading of the first listitem.
+ *
+ * @return a leading
+ */
+
+ public float leading() {
+ if (list.size() < 1) {
+ return -1;
+ }
+ ListItem item = (ListItem) list.get(0);
+ return item.leading();
+ }
+
+/**
+ * Checks if the list is numbered.
+ *
+ * @return true
if the list is numbered, false
otherwise.
+ */
+
+ public boolean isNumbered() {
+ return numbered;
+ }
+
+/**
+ * Gets the symbol indentation.
+ * @return the symbol indentation
+ */
+
+ public float symbolIndent() {
+ return symbolIndent;
+ }
+
+/**
+ * Gets the Chunk containing the symbol.
+ * @return a Chunk with a symbol
+ */
+
+ public Chunk symbol() {
+ return symbol;
+ }
+
+/**
+ * Gets the first number .
+ * @return a number
+ */
+
+ public int first() {
+ return first;
+ }
+
+/**
+ * Gets the indentation of this paragraph on the left side.
+ *
+ * @return the indentation
+ */
+
+ public float indentationLeft() {
+ return indentationLeft;
+ }
+
+/**
+ * Gets the indentation of this paragraph on the right side.
+ *
+ * @return the indentation
+ */
+
+ public float indentationRight() {
+ return indentationRight;
+ }
+
+/**
+ * Checks if a given tag corresponds with the listsymbol tag of this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isSymbol(String tag) {
+ return ElementTags.LISTSYMBOL.equals(tag);
+ }
+
+/**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.LIST.equals(tag);
+ }
+
+/**
+ * Retrieves the next letter in the sequence
+ *
+ * @return String contains the next character (A-Z or a-z)
+ */
+ private String nextLetter() {
+ int num_in_list = listItemsInList(); //list.size();
+ int max_ival = (lastCh + 0);
+ int ival = (firstCh + num_in_list);
+ while ( ival > max_ival ) {
+ ival -= 26;
+ }
+ char[] new_char = new char[1];
+ new_char[0] = (char) ival;
+ String ret = new String( new_char );
+ return ret;
+ }
+
+ /**
+ * Counts the number of ListItems in the list ommiting nested lists
+ *
+ * @return Integer number of ListItems in the list
+ */
+ private int listItemsInList() {
+ int result = 0;
+ for (Iterator i = list.iterator(); i.hasNext(); ) {
+ if (!(i.next() instanceof List)) result++;
+ }
+ return result;
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttribute(java.lang.String, java.lang.String)
+ */
+ public void setMarkupAttribute(String name, String value) {
+ if (markupAttributes == null) markupAttributes = new Properties();
+ markupAttributes.put(name, value);
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttributes(java.util.Properties)
+ */
+ public void setMarkupAttributes(Properties markupAttributes) {
+ this.markupAttributes = markupAttributes;
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttribute(java.lang.String)
+ */
+ public String getMarkupAttribute(String name) {
+ return (markupAttributes == null) ? null : String.valueOf(markupAttributes.get(name));
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributeNames()
+ */
+ public Set getMarkupAttributeNames() {
+ return Chunk.getKeySet(markupAttributes);
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributes()
+ */
+ public Properties getMarkupAttributes() {
+ return markupAttributes;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/ListItem.java b/src/main/java/com/lowagie/text/ListItem.java
new file mode 100644
index 0000000..b16848b
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ListItem.java
@@ -0,0 +1,293 @@
+/*
+ * $Id: ListItem.java,v 1.79 2006/04/12 11:41:05 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.util.Properties;
+
+import com.lowagie.text.markup.MarkupTags;
+import com.lowagie.text.markup.MarkupParser;
+
+/**
+ * A ListItem
is a Paragraph
+ * that can be added to a List
.
+ *
+ *
+ * The result of this code looks like this:
+ *
+ * List list = new List(true, 20);
+ * list.add(new ListItem("First line"));
+ * list.add(new ListItem("The second line is longer to see what happens once the end of the line is reached. Will it start on a new line?"));
+ * list.add(new ListItem("Third line"));
+ *
+ *
+ *
+ * Example 2:
+ *
+ *
+ * The result of this code looks like this:
+ *
+ * List overview = new List(false, 10);
+ * overview.add(new ListItem("This is an item"));
+ * overview.add("This is another item");
+ *
+ *
+ *
+ * @see Element
+ * @see List
+ * @see Paragraph
+ */
+
+public class ListItem extends Paragraph implements TextElementArray, MarkupAttributes {
+
+ // membervariables
+
+/** this is the symbol that wil proceed the listitem. */
+ private Chunk symbol;
+
+ // constructors
+
+/**
+ * Constructs a ListItem
.
+ */
+
+ public ListItem() {
+ super();
+ }
+
+/**
+ * Constructs a ListItem
with a certain leading.
+ *
+ * @param leading the leading
+ */
+
+ public ListItem(float leading) {
+ super(leading);
+ }
+
+/**
+ * Constructs a ListItem
with a certain Chunk
.
+ *
+ * @param chunk a Chunk
+ */
+
+ public ListItem(Chunk chunk) {
+ super(chunk);
+ }
+
+/**
+ * Constructs a ListItem
with a certain String
.
+ *
+ * @param string a String
+ */
+
+ public ListItem(String string) {
+ super(string);
+ }
+
+/**
+ * Constructs a ListItem
with a certain String
+ * and a certain Font
.
+ *
+ * @param string a String
+ * @param font a String
+ */
+
+ public ListItem(String string, Font font) {
+ super(string, font);
+ }
+
+/**
+ * Constructs a ListItem
with a certain Chunk
+ * and a certain leading.
+ *
+ * @param leading the leading
+ * @param chunk a Chunk
+ */
+
+ public ListItem(float leading, Chunk chunk) {
+ super(leading, chunk);
+ }
+
+/**
+ * Constructs a ListItem
with a certain String
+ * and a certain leading.
+ *
+ * @param leading the leading
+ * @param string a String
+ */
+
+ public ListItem(float leading, String string) {
+ super(leading, string);
+ }
+
+/**
+ * Constructs a ListItem
with a certain leading, String
+ * and Font
.
+ *
+ * @param leading the leading
+ * @param string a String
+ * @param font a Font
+ */
+
+ public ListItem(float leading, String string, Font font) {
+ super(leading, string, font);
+ }
+
+/**
+ * Constructs a ListItem
with a certain Phrase
.
+ *
+ * @param phrase a Phrase
+ */
+
+ public ListItem(Phrase phrase) {
+ super(phrase);
+ }
+
+ /**
+ * Returns a ListItem
that has been constructed taking in account
+ * the value of some attributes.
+ *
+ * @param attributes Some attributes
+ */
+
+ public ListItem(Properties attributes) {
+ super("", FontFactory.getFont(attributes));
+ String value;
+ if ((value = (String)attributes.remove(ElementTags.ITEXT)) != null) {
+ add(new Chunk(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.LEADING)) != null) {
+ setLeading(Float.valueOf(value + "f").floatValue());
+ }
+ else if ((value = (String)attributes.remove(MarkupTags.CSS_KEY_LINEHEIGHT)) != null) {
+ setLeading(MarkupParser.parseLength(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENTATIONLEFT)) != null) {
+ setIndentationLeft(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENTATIONRIGHT)) != null) {
+ setIndentationRight(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.ALIGN)) != null) {
+ setAlignment(value);
+ }
+ if (attributes.size() > 0) setMarkupAttributes(attributes);
+ }
+
+ // implementation of the Element-methods
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.LISTITEM;
+ }
+
+ // methods
+
+/**
+ * Sets the listsymbol.
+ *
+ * @param symbol a Chunk
+ */
+
+ public void setListSymbol(Chunk symbol) {
+ if (this.symbol == null) {
+ this.symbol = symbol;
+ if (this.symbol.font().isStandardFont()) {
+ this.symbol.setFont(font);
+ }
+ }
+ }
+
+ // methods to retrieve information
+
+/**
+ * Returns the listsymbol.
+ *
+ * @return a Chunk
+ */
+
+ public Chunk listSymbol() {
+ return symbol;
+ }
+
+/**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.LISTITEM.equals(tag);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/MarkupAttributes.java b/src/main/java/com/lowagie/text/MarkupAttributes.java
new file mode 100644
index 0000000..53c13f7
--- /dev/null
+++ b/src/main/java/com/lowagie/text/MarkupAttributes.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2002 by Matt Benson.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+
+import java.util.Set;
+import java.util.Properties;
+
+
+/**
+ * Defines the interface for an Element
with markup attributes--
+ * that is, random String-to-String properties for representation in markup
+ * languages such as HTML and XML.
+ *
+ * @author Matt Benson
+ */
+public interface MarkupAttributes extends com.lowagie.text.Element {
+
+/**
+ * Sets the specified attribute.
+ *
+ * @param name String
attribute name.
+ * @param value String
attribute value.
+ */
+ public void setMarkupAttribute(String name, String value);
+
+/**
+ * Sets the markupAttributes.
+ *
+ * @param markupAttributes a Properties
-object containing markupattributes
+ */
+ public void setMarkupAttributes(Properties markupAttributes);
+
+/**
+ * Returns the value of the specified attribute.
+ *
+ * @param name String
attribute name.
+ * @return String
.
+ */
+ public String getMarkupAttribute(String name);
+
+/**
+ * Returns a Set
of String
attribute names for the
+ * MarkupAttributes
implementor.
+ *
+ * @return Set
.
+ */
+ public Set getMarkupAttributeNames();
+
+/**
+ * Return a Properties
-object containing all the markupAttributes.
+ *
+ * @return Properties
+ */
+ public Properties getMarkupAttributes();
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/Meta.java b/src/main/java/com/lowagie/text/Meta.java
new file mode 100644
index 0000000..d34c1d3
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Meta.java
@@ -0,0 +1,263 @@
+/*
+ * $Id: Meta.java,v 1.65 2005/04/13 09:17:11 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.util.ArrayList;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * This is an Element
that contains
+ * some meta information about the document.
+ * Meta
can not be constructed by the user.
+ * Userdefined meta information should be placed in a Header
-object.
+ * Meta
is reserved for: Subject, Keywords, Author, Title, Producer
+ * and Creationdate information.
+ *
+ * @see Element
+ * @see Header
+ */
+
+public class Meta implements Element, MarkupAttributes {
+
+ // membervariables
+
+/** This is the type of Meta-information this object contains. */
+ private int type;
+
+/** This is the content of the Meta-information. */
+ private StringBuffer content;
+
+/** Contains extra markupAttributes */
+ protected Properties markupAttributes;
+
+ // constructors
+
+/**
+ * Constructs a Meta
.
+ *
+ * @param type the type of meta-information
+ * @param content the content
+ */
+
+ Meta(int type, String content) {
+ this.type = type;
+ this.content = new StringBuffer(content);
+ }
+
+/**
+ * Constructs a Meta
.
+ *
+ * @param tag the tagname of the meta-information
+ * @param content the content
+ */
+
+ public Meta(String tag, String content) {
+ this.type = Meta.getType(tag);
+ this.content = new StringBuffer(content);
+ }
+
+ // implementation of the Element-methods
+
+/**
+ * Processes the element by adding it (or the different parts) to a
+ * ElementListener
.
+ *
+ * @param listener the ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ return listener.add(this);
+ }
+ catch(DocumentException de) {
+ return false;
+ }
+ }
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return type;
+ }
+
+/**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ return new ArrayList();
+ }
+
+ // methods
+
+/**
+ * appends some text to this Meta
.
+ *
+ * @param string a String
+ * @return a StringBuffer
+ */
+
+ public StringBuffer append(String string) {
+ return content.append(string);
+ }
+
+ // methods to retrieve information
+
+/**
+ * Returns the content of the meta information.
+ *
+ * @return a String
+ */
+
+ public String content() {
+ return content.toString();
+ }
+
+/**
+ * Returns the name of the meta information.
+ *
+ * @return a String
+ */
+
+ public String name() {
+ switch (type) {
+ case Element.SUBJECT:
+ return ElementTags.SUBJECT;
+ case Element.KEYWORDS:
+ return ElementTags.KEYWORDS;
+ case Element.AUTHOR:
+ return ElementTags.AUTHOR;
+ case Element.TITLE:
+ return ElementTags.TITLE;
+ case Element.PRODUCER:
+ return ElementTags.PRODUCER;
+ case Element.CREATIONDATE:
+ return ElementTags.CREATIONDATE;
+ default:
+ return ElementTags.UNKNOWN;
+ }
+ }
+
+/**
+ * Returns the name of the meta information.
+ *
+ * @param tag iText tag for meta information
+ * @return the Element value corresponding with the given tag
+ */
+
+ public static int getType(String tag) {
+ if (ElementTags.SUBJECT.equals(tag)) {
+ return Element.SUBJECT;
+ }
+ if (ElementTags.KEYWORDS.equals(tag)) {
+ return Element.KEYWORDS;
+ }
+ if (ElementTags.AUTHOR.equals(tag)) {
+ return Element.AUTHOR;
+ }
+ if (ElementTags.TITLE.equals(tag)) {
+ return Element.TITLE;
+ }
+ if (ElementTags.PRODUCER.equals(tag)) {
+ return Element.PRODUCER;
+ }
+ if (ElementTags.CREATIONDATE.equals(tag)) {
+ return Element.CREATIONDATE;
+ }
+ return Element.HEADER;
+ }
+
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttribute(java.lang.String, java.lang.String)
+ */
+ public void setMarkupAttribute(String name, String value) {
+ if (markupAttributes == null) markupAttributes = new Properties();
+ markupAttributes.put(name, value);
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttributes(java.util.Properties)
+ */
+ public void setMarkupAttributes(Properties markupAttributes) {
+ this.markupAttributes = markupAttributes;
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttribute(java.lang.String)
+ */
+ public String getMarkupAttribute(String name) {
+ return (markupAttributes == null) ? null : String.valueOf(markupAttributes.get(name));
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributeNames()
+ */
+ public Set getMarkupAttributeNames() {
+ return Chunk.getKeySet(markupAttributes);
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributes()
+ */
+ public Properties getMarkupAttributes() {
+ return markupAttributes;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/PageSize.java b/src/main/java/com/lowagie/text/PageSize.java
new file mode 100644
index 0000000..f89c319
--- /dev/null
+++ b/src/main/java/com/lowagie/text/PageSize.java
@@ -0,0 +1,152 @@
+/*
+ * $Id: PageSize.java,v 1.58 2005/05/04 14:31:08 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+/**
+ * The PageSize
-object contains a number of rectangles representing the most common papersizes.
+ *
+ * @see Rectangle
+ */
+
+public class PageSize {
+
+ // membervariables
+
+/** This is the letter format */
+ public static final Rectangle LETTER = new Rectangle(612,792);
+
+/** This is the note format */
+ public static final Rectangle NOTE = new Rectangle(540,720);
+
+/** This is the legal format */
+ public static final Rectangle LEGAL = new Rectangle(612,1008);
+
+/** This is the a0 format */
+ public static final Rectangle A0 = new Rectangle(2384,3370);
+
+/** This is the a1 format */
+ public static final Rectangle A1 = new Rectangle(1684,2384);
+
+/** This is the a2 format */
+ public static final Rectangle A2 = new Rectangle(1190,1684);
+
+/** This is the a3 format */
+ public static final Rectangle A3 = new Rectangle(842,1190);
+
+/** This is the a4 format */
+ public static final Rectangle A4 = new Rectangle(595,842);
+
+/** This is the a5 format */
+ public static final Rectangle A5 = new Rectangle(421,595);
+
+/** This is the a6 format */
+ public static final Rectangle A6 = new Rectangle(297,421);
+
+/** This is the a7 format */
+ public static final Rectangle A7 = new Rectangle(210,297);
+
+/** This is the a8 format */
+ public static final Rectangle A8 = new Rectangle(148,210);
+
+/** This is the a9 format */
+ public static final Rectangle A9 = new Rectangle(105,148);
+
+/** This is the a10 format */
+ public static final Rectangle A10 = new Rectangle(74,105);
+
+/** This is the b0 format */
+ public static final Rectangle B0 = new Rectangle(2836,4008);
+
+/** This is the b1 format */
+ public static final Rectangle B1 = new Rectangle(2004,2836);
+
+/** This is the b2 format */
+ public static final Rectangle B2 = new Rectangle(1418,2004);
+
+/** This is the b3 format */
+ public static final Rectangle B3 = new Rectangle(1002,1418);
+
+/** This is the b4 format */
+ public static final Rectangle B4 = new Rectangle(709,1002);
+
+/** This is the b5 format */
+ public static final Rectangle B5 = new Rectangle(501,709);
+
+/** This is the archE format */
+ public static final Rectangle ARCH_E = new Rectangle(2592,3456);
+
+/** This is the archD format */
+ public static final Rectangle ARCH_D = new Rectangle(1728,2592);
+
+/** This is the archC format */
+ public static final Rectangle ARCH_C = new Rectangle(1296,1728);
+
+/** This is the archB format */
+ public static final Rectangle ARCH_B = new Rectangle(864,1296);
+
+/** This is the archA format */
+ public static final Rectangle ARCH_A = new Rectangle(648,864);
+
+/** This is the flsa format */
+ public static final Rectangle FLSA = new Rectangle(612,936);
+
+/** This is the flse format */
+ public static final Rectangle FLSE = new Rectangle(612,936);
+
+/** This is the halfletter format */
+ public static final Rectangle HALFLETTER = new Rectangle(396,612);
+
+/** This is the 11x17 format */
+ public static final Rectangle _11X17 = new Rectangle(792,1224);
+
+/** This is the ledger format */
+ public static final Rectangle LEDGER = new Rectangle(1224,792);
+}
diff --git a/src/main/java/com/lowagie/text/Paragraph.java b/src/main/java/com/lowagie/text/Paragraph.java
new file mode 100644
index 0000000..32031cc
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Paragraph.java
@@ -0,0 +1,516 @@
+/*
+ * $Id: Paragraph.java,v 1.83 2005/05/03 13:03:48 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.util.Properties;
+
+import com.lowagie.text.markup.MarkupTags;
+import com.lowagie.text.markup.MarkupParser;
+
+/**
+ * A Paragraph
is a series of Chunk
s and/or Phrases
.
+ * Paragraph
has the same qualities of a Phrase
, but also
+ * some additional layout-parameters:
+ *
+ *
+ *
+ * Example:
+ *
+ *
+ * @see Element
+ * @see Phrase
+ * @see ListItem
+ */
+
+public class Paragraph extends Phrase implements TextElementArray, MarkupAttributes {
+
+ // membervariables
+
+/** The alignment of the text. */
+ protected int alignment = Element.ALIGN_UNDEFINED;
+
+/** The indentation of this paragraph on the left side. */
+ protected float indentationLeft;
+
+/** The indentation of this paragraph on the right side. */
+ protected float indentationRight;
+
+/** The spacing before the paragraph. */
+ protected float spacingBefore;
+
+/** The spacing after the paragraph. */
+ protected float spacingAfter;
+
+/** Does the paragraph has to be kept together on 1 page. */
+ protected boolean keeptogether = false;
+
+ /** The text leading that is multiplied by the biggest font size in the line. */
+ protected float multipliedLeading = 0;
+
+ /**
+ * Holds value of property firstLineIndent.
+ */
+ private float firstLineIndent = 0;
+
+ /**
+ * Holds value of property extraParagraphSpace.
+ */
+ private float extraParagraphSpace = 0;
+
+ // constructors
+
+/**
+ * Constructs a
+ * Paragraph p = new Paragraph("This is a paragraph",
+ * FontFactory.getFont(FontFactory.HELVETICA, 18, Font.BOLDITALIC, new Color(0, 0, 255)));
+ *
Paragraph
.
+ */
+
+ public Paragraph() {
+ super();
+ }
+
+/**
+ * Constructs a Paragraph
with a certain leading.
+ *
+ * @param leading the leading
+ */
+
+ public Paragraph(float leading) {
+ super(leading);
+ }
+
+/**
+ * Constructs a Paragraph
with a certain Chunk
.
+ *
+ * @param chunk a Chunk
+ */
+
+ public Paragraph(Chunk chunk) {
+ super(chunk);
+ }
+
+/**
+ * Constructs a Paragraph
with a certain Chunk
+ * and a certain leading.
+ *
+ * @param leading the leading
+ * @param chunk a Chunk
+ */
+
+ public Paragraph(float leading, Chunk chunk) {
+ super(leading, chunk);
+ }
+
+/**
+ * Constructs a Paragraph
with a certain String
.
+ *
+ * @param string a String
+ */
+
+ public Paragraph(String string) {
+ super(string);
+ }
+
+/**
+ * Constructs a Paragraph
with a certain String
+ * and a certain Font
.
+ *
+ * @param string a String
+ * @param font a Font
+ */
+
+ public Paragraph(String string, Font font) {
+ super(string, font);
+ }
+
+/**
+ * Constructs a Paragraph
with a certain String
+ * and a certain leading.
+ *
+ * @param leading the leading
+ * @param string a String
+ */
+
+ public Paragraph(float leading, String string) {
+ super(leading, string);
+ }
+
+/**
+ * Constructs a Paragraph
with a certain leading, String
+ * and Font
.
+ *
+ * @param leading the leading
+ * @param string a String
+ * @param font a Font
+ */
+
+ public Paragraph(float leading, String string, Font font) {
+ super(leading, string, font);
+ }
+
+/**
+ * Constructs a Paragraph
with a certain Phrase
.
+ *
+ * @param phrase a Phrase
+ */
+
+ public Paragraph(Phrase phrase) {
+ super(phrase.leading, "", phrase.font());
+ super.add(phrase);
+ }
+
+/**
+ * Returns a Paragraph
that has been constructed taking in account
+ * the value of some attributes.
+ *
+ * @param attributes Some attributes
+ */
+
+ public Paragraph(Properties attributes) {
+ this("", FontFactory.getFont(attributes));
+ String value;
+ if ((value = (String)attributes.remove(ElementTags.ITEXT)) != null) {
+ Chunk chunk = new Chunk(value);
+ if ((value = (String)attributes.remove(ElementTags.GENERICTAG)) != null) {
+ chunk.setGenericTag(value);
+ }
+ add(chunk);
+ }
+ if ((value = (String)attributes.remove(ElementTags.ALIGN)) != null) {
+ setAlignment(value);
+ }
+ if ((value = (String)attributes.remove(ElementTags.LEADING)) != null) {
+ setLeading(Float.valueOf(value + "f").floatValue());
+ }
+ else if ((value = (String)attributes.remove(MarkupTags.CSS_KEY_LINEHEIGHT)) != null) {
+ setLeading(MarkupParser.parseLength(value));
+ }
+ else {
+ setLeading(16);
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENTATIONLEFT)) != null) {
+ setIndentationLeft(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENTATIONRIGHT)) != null) {
+ setIndentationRight(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.KEEPTOGETHER)) != null) {
+ keeptogether = new Boolean(value).booleanValue();
+ }
+ if (attributes.size() > 0) setMarkupAttributes(attributes);
+ }
+
+ // implementation of the Element-methods
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.PARAGRAPH;
+ }
+
+ // methods
+
+/**
+ * Adds an Object
to the Paragraph
.
+ *
+ * @param o object the object to add.
+ * @return true is adding the object succeeded
+ */
+
+ public boolean add(Object o) {
+ if (o instanceof List) {
+ List list = (List) o;
+ list.setIndentationLeft(list.indentationLeft() + indentationLeft);
+ list.setIndentationRight(indentationRight);
+ return super.add(list);
+ }
+ else if (o instanceof Image) {
+ super.addSpecial((Image) o);
+ return true;
+ }
+ else if (o instanceof Paragraph) {
+ super.add(o);
+ super.add(Chunk.NEWLINE);
+ return true;
+ }
+ return super.add(o);
+ }
+
+ // setting the membervariables
+
+/**
+ * Sets the alignment of this paragraph.
+ *
+ * @param alignment the new alignment
+ */
+
+ public void setAlignment(int alignment) {
+ this.alignment = alignment;
+ }
+
+/**
+ * Sets the alignment of this paragraph.
+ *
+ * @param alignment the new alignment as a String
+ */
+
+ public void setAlignment(String alignment) {
+ if (ElementTags.ALIGN_CENTER.equalsIgnoreCase(alignment)) {
+ this.alignment = Element.ALIGN_CENTER;
+ return;
+ }
+ if (ElementTags.ALIGN_RIGHT.equalsIgnoreCase(alignment)) {
+ this.alignment = Element.ALIGN_RIGHT;
+ return;
+ }
+ if (ElementTags.ALIGN_JUSTIFIED.equalsIgnoreCase(alignment)) {
+ this.alignment = Element.ALIGN_JUSTIFIED;
+ return;
+ }
+ if (ElementTags.ALIGN_JUSTIFIED_ALL.equalsIgnoreCase(alignment)) {
+ this.alignment = Element.ALIGN_JUSTIFIED_ALL;
+ return;
+ }
+ this.alignment = Element.ALIGN_LEFT;
+ }
+
+/**
+ * Sets the indentation of this paragraph on the left side.
+ *
+ * @param indentation the new indentation
+ */
+
+ public void setIndentationLeft(float indentation) {
+ this.indentationLeft = indentation;
+ }
+
+/**
+ * Sets the indentation of this paragraph on the right side.
+ *
+ * @param indentation the new indentation
+ */
+
+ public void setIndentationRight(float indentation) {
+ this.indentationRight = indentation;
+ }
+
+/**
+ * Sets the spacing before this paragraph.
+ *
+ * @param spacing the new spacing
+ */
+
+ public void setSpacingBefore(float spacing) {
+ this.spacingBefore = spacing;
+ }
+
+/**
+ * Sets the spacing after this paragraph.
+ *
+ * @param spacing the new spacing
+ */
+
+ public void setSpacingAfter(float spacing) {
+ this.spacingAfter = spacing;
+ }
+
+/**
+ * Indicates that the paragraph has to be kept together on one page.
+ *
+ * @param keeptogether true of the paragraph may not be split over 2 pages
+ */
+
+ public void setKeepTogether(boolean keeptogether) {
+ this.keeptogether = keeptogether;
+ }
+
+/**
+ * Checks if this paragraph has to be kept together on one page.
+ *
+ * @return true if the paragraph may not be split over 2 pages.
+ */
+
+ public boolean getKeepTogether() {
+ return keeptogether;
+ }
+
+ // methods to retrieve information
+
+/**
+ * Gets the alignment of this paragraph.
+ *
+ * @return alignment
+ */
+
+ public int alignment() {
+ return alignment;
+ }
+
+/**
+ * Gets the indentation of this paragraph on the left side.
+ *
+ * @return the indentation
+ */
+
+ public float indentationLeft() {
+ return indentationLeft;
+ }
+
+/**
+ * Gets the indentation of this paragraph on the right side.
+ *
+ * @return the indentation
+ */
+
+ public float indentationRight() {
+ return indentationRight;
+ }
+
+/**
+ * Gets the spacing before this paragraph.
+ *
+ * @return the spacing
+ */
+
+ public float spacingBefore() {
+ return spacingBefore;
+ }
+
+/**
+ * Gets the spacing before this paragraph.
+ *
+ * @return the spacing
+ */
+
+ public float spacingAfter() {
+ return spacingAfter;
+ }
+
+/**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.PARAGRAPH.equals(tag);
+ }
+
+ /**
+ * Sets the leading fixed and variable. The resultant leading will be
+ * fixedLeading+multipliedLeading*maxFontSize where maxFontSize is the
+ * size of the bigest font in the line.
+ * @param fixedLeading the fixed leading
+ * @param multipliedLeading the variable leading
+ */
+ public void setLeading(float fixedLeading, float multipliedLeading) {
+ this.leading = fixedLeading;
+ this.multipliedLeading = multipliedLeading;
+ }
+
+ /**
+ * @see com.lowagie.text.Phrase#setLeading(float)
+ */
+ public void setLeading(float fixedLeading) {
+ this.leading = fixedLeading;
+ this.multipliedLeading = 0;
+ }
+
+ /**
+ * Gets the variable leading
+ * @return the leading
+ */
+ public float getMultipliedLeading() {
+ return multipliedLeading;
+ }
+
+ /**
+ * Getter for property firstLineIndent.
+ * @return Value of property firstLineIndent.
+ */
+ public float getFirstLineIndent() {
+ return this.firstLineIndent;
+ }
+
+ /**
+ * Setter for property firstLineIndent.
+ * @param firstLineIndent New value of property firstLineIndent.
+ */
+ public void setFirstLineIndent(float firstLineIndent) {
+ this.firstLineIndent = firstLineIndent;
+ }
+
+ /**
+ * Getter for property extraParagraphSpace.
+ * @return Value of property extraParagraphSpace.
+ */
+ public float getExtraParagraphSpace() {
+ return this.extraParagraphSpace;
+ }
+
+ /**
+ * Setter for property extraParagraphSpace.
+ * @param extraParagraphSpace New value of property extraParagraphSpace.
+ */
+ public void setExtraParagraphSpace(float extraParagraphSpace) {
+ this.extraParagraphSpace = extraParagraphSpace;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/Phrase.java b/src/main/java/com/lowagie/text/Phrase.java
new file mode 100644
index 0000000..7362163
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Phrase.java
@@ -0,0 +1,612 @@
+/*
+ * $Id: Phrase.java,v 1.106 2006/01/14 16:15:25 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+import com.lowagie.text.markup.MarkupTags;
+import com.lowagie.text.markup.MarkupParser;
+
+/**
+ * A Phrase
is a series of Chunk
s.
+ * Phrase
has a main Font
, but some chunks
+ * within the phrase can have a Font
that differs from the
+ * main Font
. All the Chunk
s in a Phrase
+ * have the same leading
.
+ *
+ *
+ * @see Element
+ * @see Chunk
+ * @see Paragraph
+ * @see Anchor
+ */
+
+public class Phrase extends ArrayList implements TextElementArray, MarkupAttributes {
+
+ // membervariables
+
+/** This is the leading of this phrase. */
+ protected float leading = Float.NaN;
+
+/** This is the font of this phrase. */
+ protected Font font = new Font();
+
+/** Contains extra markupAttributes */
+ protected Properties markupAttributes;
+
+ // constructors
+
+/**
+ * Constructs a Phrase that can be used in the static getInstance() method.
+ * @param dummy a dummy parameter
+ */
+ private Phrase(boolean dummy) {
+ }
+
+/**
+ * Constructs a
+ * // When no parameters are passed, the default leading = 16
+ * Phrase phrase0 = new Phrase();
+ * Phrase phrase1 = new Phrase("this is a phrase");
+ * // In this example the leading is passed as a parameter
+ * Phrase phrase2 = new Phrase(16, "this is a phrase with leading 16");
+ * // When a Font is passed (explicitely or embedded in a chunk), the default leading = 1.5 * size of the font
+ * Phrase phrase3 = new Phrase("this is a phrase with a red, normal font Courier, size 12", FontFactory.getFont(FontFactory.COURIER, 12, Font.NORMAL, new Color(255, 0, 0)));
+ * Phrase phrase4 = new Phrase(new Chunk("this is a phrase"));
+ * Phrase phrase5 = new Phrase(18, new Chunk("this is a phrase", FontFactory.getFont(FontFactory.HELVETICA, 16, Font.BOLD, new Color(255, 0, 0)));
+ *
Phrase
without specifying a leading.
+ */
+
+ public Phrase() {
+ this(16);
+ }
+
+/**
+ * Constructs a Phrase
with a certain leading.
+ *
+ * @param leading the leading
+ */
+
+ public Phrase(float leading) {
+ this.leading = leading;
+ }
+
+/**
+ * Constructs a Phrase
with a certain Chunk
.
+ *
+ * @param chunk a Chunk
+ */
+
+ public Phrase(Chunk chunk) {
+ super.add(chunk);
+ }
+
+/**
+ * Constructs a Phrase
with a certain Chunk
+ * and a certain leading.
+ *
+ * @param leading the leading
+ * @param chunk a Chunk
+ */
+
+ public Phrase(float leading, Chunk chunk) {
+ this(leading);
+ super.add(chunk);
+ }
+
+/**
+ * Constructs a Phrase
with a certain String
.
+ *
+ * @param string a String
+ */
+
+ public Phrase(String string) {
+ this(Float.NaN, string, new Font());
+ }
+
+/**
+ * Constructs a Phrase
with a certain String
and a certain Font
.
+ *
+ * @param string a String
+ * @param font a Font
+ */
+
+ public Phrase(String string, Font font) {
+ this(Float.NaN, string, font);
+ this.font = font;
+ }
+
+/**
+ * Constructs a Phrase
with a certain leading and a certain String
.
+ *
+ * @param leading the leading
+ * @param string a String
+ */
+
+ public Phrase(float leading, String string) {
+ this(leading, string, new Font());
+ }
+
+/**
+ * Constructs a Phrase
with a certain leading, a certain String
+ * and a certain Font
.
+ *
+ * @param leading the leading
+ * @param string a String
+ * @param font a Font
+ */
+
+ public Phrase(float leading, String string, Font font) {
+ this(leading);
+ this.font = font;
+ /* bugfix by August Detlefsen */
+ if (string != null && string.length() != 0) {
+ super.add(new Chunk(string, font));
+ }
+ }
+
+ /**
+ * Gets a special kind of Phrase that changes some characters into corresponding symbols.
+ * @param string
+ * @return a newly constructed Phrase
+ */
+ public static final Phrase getInstance(String string) {
+ return getInstance(16, string, new Font());
+ }
+
+ /**
+ * Gets a special kind of Phrase that changes some characters into corresponding symbols.
+ * @param leading
+ * @param string
+ * @return a newly constructed Phrase
+ */
+ public static final Phrase getInstance(int leading, String string) {
+ return getInstance(leading, string, new Font());
+ }
+
+ /**
+ * Gets a special kind of Phrase that changes some characters into corresponding symbols.
+ * @param leading
+ * @param string
+ * @param font
+ * @return a newly constructed Phrase
+ */
+ public static final Phrase getInstance(int leading, String string, Font font) {
+ Phrase p = new Phrase(true);
+ p.setLeading(leading);
+ p.font = font;
+ if (font.family() != Font.SYMBOL && font.family() != Font.ZAPFDINGBATS && font.getBaseFont() == null) {
+ int index;
+ while((index = SpecialSymbol.index(string)) > -1) {
+ if (index > 0) {
+ String firstPart = string.substring(0, index);
+ /* bugfix [ #461272 ] CODE CHANGE REQUIRED IN Phrase.java
+ by Arekh Nambiar */
+ ((ArrayList)p).add(new Chunk(firstPart, font));
+ string = string.substring(index);
+ }
+ Font symbol = new Font(Font.SYMBOL, font.size(), font.style(), font.color());
+ StringBuffer buf = new StringBuffer();
+ buf.append(SpecialSymbol.getCorrespondingSymbol(string.charAt(0)));
+ string = string.substring(1);
+ while (SpecialSymbol.index(string) == 0) {
+ buf.append(SpecialSymbol.getCorrespondingSymbol(string.charAt(0)));
+ string = string.substring(1);
+ }
+ ((ArrayList)p).add(new Chunk(buf.toString(), symbol));
+ }
+ }
+ /* bugfix by August Detlefsen */
+ if (string != null && string.length() != 0) {
+ ((ArrayList)p).add(new Chunk(string, font));
+ }
+ return p;
+ }
+
+/**
+ * Returns a Phrase
that has been constructed taking in account
+ * the value of some attributes.
+ *
+ * @param attributes Some attributes
+ */
+
+ public Phrase(Properties attributes) {
+ this("", FontFactory.getFont(attributes));
+ clear();
+ String value;
+ if ((value = (String)attributes.remove(ElementTags.LEADING)) != null) {
+ setLeading(Float.valueOf(value + "f").floatValue());
+ }
+ else if ((value = (String)attributes.remove(MarkupTags.CSS_KEY_LINEHEIGHT)) != null) {
+ setLeading(MarkupParser.parseLength(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.ITEXT)) != null) {
+ Chunk chunk = new Chunk(value);
+ if ((value = (String)attributes.remove(ElementTags.GENERICTAG)) != null) {
+ chunk.setGenericTag(value);
+ }
+ add(chunk);
+ }
+ if (attributes.size() > 0) setMarkupAttributes(attributes);
+ }
+
+ // implementation of the Element-methods
+
+/**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener an ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ for (Iterator i = iterator(); i.hasNext(); ) {
+ listener.add((Element) i.next());
+ }
+ return true;
+ }
+ catch(DocumentException de) {
+ return false;
+ }
+ }
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.PHRASE;
+ }
+
+/**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ ArrayList tmp = new ArrayList();
+ for (Iterator i = iterator(); i.hasNext(); ) {
+ tmp.addAll(((Element) i.next()).getChunks());
+ }
+ return tmp;
+ }
+
+ // overriding some of the ArrayList-methods
+
+/**
+ * Adds a Chunk
, an Anchor
or another Phrase
+ * to this Phrase
.
+ *
+ * @param index index at which the specified element is to be inserted
+ * @param o an object of type Chunk
, Anchor
or Phrase
+ * @throws ClassCastException when you try to add something that isn't a Chunk
, Anchor
or Phrase
+ */
+
+ public void add(int index, Object o) {
+ if (o == null) return;
+ try {
+ Element element = (Element) o;
+ if (element.type() == Element.CHUNK) {
+ Chunk chunk = (Chunk) element;
+ if (!font.isStandardFont()) {
+ chunk.setFont(font.difference(chunk.font()));
+ }
+ super.add(index, chunk);
+ }
+ else if (element.type() == Element.PHRASE ||
+ element.type() == Element.ANCHOR ||
+ element.type() == Element.ANNOTATION ||
+ element.type() == Element.TABLE || // line added by David Freels
+ element.type() == Element.GRAPHIC) {
+ super.add(index, element);
+ }
+ else {
+ throw new ClassCastException(String.valueOf(element.type()));
+ }
+ }
+ catch(ClassCastException cce) {
+ throw new ClassCastException("Insertion of illegal Element: " + cce.getMessage());
+ }
+ }
+
+/**
+ * Adds a Chunk
, Anchor
or another Phrase
+ * to this Phrase
.
+ *
+ * @param o an object of type Chunk
, Anchor
or Phrase
+ * @return a boolean
+ * @throws ClassCastException when you try to add something that isn't a Chunk
, Anchor
or Phrase
+ */
+
+ public boolean add(Object o) {
+ if (o == null) return false;
+ if (o instanceof String) {
+ return super.add(new Chunk((String) o, font));
+ }
+ try {
+ Element element = (Element) o;
+ switch(element.type()) {
+ case Element.CHUNK:
+ return addChunk((Chunk) o);
+ case Element.PHRASE:
+ case Element.PARAGRAPH:
+ Phrase phrase = (Phrase) o;
+ boolean success = true;
+ Element e;
+ for (Iterator i = phrase.iterator(); i.hasNext(); ) {
+ e = (Element) i.next();
+ if (e instanceof Chunk) {
+ success &= addChunk((Chunk)e);
+ }
+ else {
+ success &= this.add(e);
+ }
+ }
+ return success;
+ case Element.ANCHOR:
+ return super.add((Anchor) o);
+ case Element.ANNOTATION:
+ return super.add((Annotation) o);
+ case Element.TABLE: // case added by David Freels
+ return super.add((Table) o);
+ case Element.PTABLE: // case added by mr. Karen Vardanyan
+ // This will only work for PDF!!! Not for RTF/HTML
+ return super.add((com.lowagie.text.pdf.PdfPTable) o);
+ case Element.LIST:
+ return super.add((List) o);
+ case Element.GRAPHIC: // suggested by Steven Balthazor
+ return super.add((Graphic) o);
+ default:
+ throw new ClassCastException(String.valueOf(element.type()));
+ }
+ }
+ catch(ClassCastException cce) {
+ throw new ClassCastException("Insertion of illegal Element: " + cce.getMessage());
+ }
+ }
+
+/**
+ * Adds a Chunk.
+ * Chunk
s
+ * to this Phrase
.
+ *
+ * @param collection a collection of Chunk
s, Anchor
s and Phrase
s.
+ * @return true
if the action succeeded, false
if not.
+ * @throws ClassCastException when you try to add something that isn't a Chunk
, Anchor
or Phrase
+ */
+
+ public boolean addAll(Collection collection) {
+ for (Iterator iterator = collection.iterator(); iterator.hasNext(); ) {
+ this.add(iterator.next());
+ }
+ return true;
+ }
+
+/**
+ * Adds a Object
to the Paragraph
.
+ *
+ * @param object the object to add.
+ */
+
+ protected void addSpecial(Object object) {
+ super.add(object);
+ }
+
+ // methods
+
+/**
+ * Sets the leading of this phrase.
+ *
+ * @param leading the new leading
+ */
+
+ public void setLeading(float leading) {
+ this.leading = leading;
+ }
+
+ // methods to retrieve information
+
+/**
+ * Checks is this Phrase
contains no or 1 empty Chunk
.
+ *
+ * @return false
if the Phrase
+ * contains more than one or more non-emptyChunk
s.
+ */
+
+ public boolean isEmpty() {
+ switch(size()) {
+ case 0:
+ return true;
+ case 1:
+ Element element = (Element) get(0);
+ if (element.type() == Element.CHUNK && ((Chunk) element).isEmpty()) {
+ return true;
+ }
+ return false;
+ default:
+ return false;
+ }
+ }
+
+/**
+ * Checks you if the leading of this phrase is defined.
+ *
+ * @return true if the leading is defined
+ */
+
+ public boolean leadingDefined() {
+ if (Float.isNaN(leading)) {
+ return false;
+ }
+ return true;
+ }
+
+/**
+ * Gets the leading of this phrase.
+ *
+ * @return the linespacing
+ */
+
+ public float leading() {
+ if (Float.isNaN(leading)) {
+ return font.leading(1.5f);
+ }
+ return leading;
+ }
+
+/**
+ * Gets the font of the first Chunk
that appears in this Phrase
.
+ *
+ * @return a Font
+ */
+
+ public Font font() {
+ return font;
+ }
+
+/**
+ * Returns the content as a String object.
+ * This method differs from toString because toString will return an ArrayList with the toString value of the Chunks in this Phrase.
+ */
+ public String content() {
+ StringBuffer buf = new StringBuffer();
+ for (Iterator i = getChunks().iterator(); i.hasNext(); ) {
+ buf.append(i.next().toString());
+ }
+ return buf.toString();
+ }
+
+/**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.PHRASE.equals(tag);
+ }
+
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttribute(java.lang.String, java.lang.String)
+ */
+ public void setMarkupAttribute(String name, String value) {
+ if (markupAttributes == null) markupAttributes = new Properties();
+ markupAttributes.put(name, value);
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttributes(java.util.Properties)
+ */
+ public void setMarkupAttributes(Properties markupAttributes) {
+ this.markupAttributes = markupAttributes;
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttribute(java.lang.String)
+ */
+ public String getMarkupAttribute(String name) {
+ return (markupAttributes == null) ? null : String.valueOf(markupAttributes.get(name));
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributeNames()
+ */
+ public Set getMarkupAttributeNames() {
+ return Chunk.getKeySet(markupAttributes);
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributes()
+ */
+ public Properties getMarkupAttributes() {
+ return markupAttributes;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/Rectangle.java b/src/main/java/com/lowagie/text/Rectangle.java
new file mode 100644
index 0000000..801ace0
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Rectangle.java
@@ -0,0 +1,955 @@
+/*
+ * $Id: Rectangle.java,v 1.76 2005/12/12 08:31:40 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Properties;
+import java.util.Set;
+import com.lowagie.text.pdf.GrayColor;
+
+/**
+ * A Rectangle
is the representation of a geometric figure.
+ *
+ * Rectangles support constant width borders using
+ * {@link #setBorderWidth(float)}and {@link #setBorder(int)}. They also
+ * support borders that vary in width/color on each side using methods like
+ * {@link #setBorderWidthLeft(float)}or
+ * {@link #setBorderColorLeft(java.awt.Color)}.
+ *
+ * @see Element
+ * @see Table
+ * @see Cell
+ * @see HeaderFooter
+ */
+
+public class Rectangle implements Element, MarkupAttributes {
+
+ // static membervariables (concerning the presence of borders)
+
+ /** This is the value that will be used as undefined . */
+ public static final int UNDEFINED = -1;
+
+ /** This represents one side of the border of the Rectangle
. */
+ public static final int TOP = 1;
+
+ /** This represents one side of the border of the Rectangle
. */
+ public static final int BOTTOM = 2;
+
+ /** This represents one side of the border of the Rectangle
. */
+ public static final int LEFT = 4;
+
+ /** This represents one side of the border of the Rectangle
. */
+ public static final int RIGHT = 8;
+
+ /** This represents a rectangle without borders. */
+ public static final int NO_BORDER = 0;
+
+ /** This represents a type of border. */
+ public static final int BOX = TOP + BOTTOM + LEFT + RIGHT;
+
+ // membervariables
+
+ /** the lower left x-coordinate. */
+ protected float llx;
+
+ /** the lower left y-coordinate. */
+ protected float lly;
+
+ /** the upper right x-coordinate. */
+ protected float urx;
+
+ /** the upper right y-coordinate. */
+ protected float ury;
+
+ /** This represents the status of the 4 sides of the rectangle. */
+ protected int border = UNDEFINED;
+
+ /** This is the width of the border around this rectangle. */
+ protected float borderWidth = UNDEFINED;
+
+ /** The color of the border of this rectangle. */
+ protected Color color = null;
+
+ /** The color of the left border of this rectangle. */
+ protected Color borderColorLeft = null;
+
+ /** The color of the right border of this rectangle. */
+ protected Color borderColorRight = null;
+
+ /** The color of the top border of this rectangle. */
+ protected Color borderColorTop = null;
+
+ /** The color of the bottom border of this rectangle. */
+ protected Color borderColorBottom = null;
+
+ /** The width of the left border of this rectangle. */
+ protected float borderWidthLeft = UNDEFINED;
+
+ /** The width of the right border of this rectangle. */
+ protected float borderWidthRight = UNDEFINED;
+
+ /** The width of the top border of this rectangle. */
+ protected float borderWidthTop = UNDEFINED;
+
+ /** The width of the bottom border of this rectangle. */
+ protected float borderWidthBottom = UNDEFINED;
+
+ /** Whether variable width borders are used. */
+ protected boolean useVariableBorders = false;
+
+ /** This is the color of the background of this rectangle. */
+ protected Color background = null;
+
+ protected int rotation = 0;
+
+ /** Contains extra markupAttributes */
+ protected Properties markupAttributes;
+
+ // constructors
+
+ /**
+ * Constructs a Rectangle
-object.
+ *
+ * @param llx
+ * lower left x
+ * @param lly
+ * lower left y
+ * @param urx
+ * upper right x
+ * @param ury
+ * upper right y
+ */
+
+ public Rectangle(float llx, float lly, float urx, float ury) {
+ this.llx = llx;
+ this.lly = lly;
+ this.urx = urx;
+ this.ury = ury;
+ }
+
+ /**
+ * Constructs a Rectangle
-object starting from the origin
+ * (0, 0).
+ *
+ * @param urx
+ * upper right x
+ * @param ury
+ * upper right y
+ */
+
+ public Rectangle(float urx, float ury) {
+ this(0, 0, urx, ury);
+ }
+
+ /**
+ * Constructs a Rectangle
-object.
+ *
+ * @param rect
+ * another Rectangle
+ */
+
+ public Rectangle(Rectangle rect) {
+ this(rect.llx, rect.lly, rect.urx, rect.ury);
+ cloneNonPositionParameters(rect);
+ }
+
+ /**
+ * Copies all of the parameters from a Rectangle
object
+ * except the position.
+ *
+ * @param rect
+ * Rectangle
to copy from
+ */
+
+ public void cloneNonPositionParameters(Rectangle rect) {
+ this.rotation = rect.rotation;
+ this.border = rect.border;
+ this.borderWidth = rect.borderWidth;
+ this.color = rect.color;
+ this.background = rect.background;
+ this.borderColorLeft = rect.borderColorLeft;
+ this.borderColorRight = rect.borderColorRight;
+ this.borderColorTop = rect.borderColorTop;
+ this.borderColorBottom = rect.borderColorBottom;
+ this.borderWidthLeft = rect.borderWidthLeft;
+ this.borderWidthRight = rect.borderWidthRight;
+ this.borderWidthTop = rect.borderWidthTop;
+ this.borderWidthBottom = rect.borderWidthBottom;
+ this.useVariableBorders = rect.useVariableBorders;
+ }
+
+ /**
+ * Copies all of the parameters from a Rectangle
object
+ * except the position.
+ *
+ * @param rect
+ * Rectangle
to copy from
+ */
+
+ public void softCloneNonPositionParameters(Rectangle rect) {
+ if (rect.rotation != 0)
+ this.rotation = rect.rotation;
+ if (rect.border != UNDEFINED)
+ this.border = rect.border;
+ if (rect.borderWidth != UNDEFINED)
+ this.borderWidth = rect.borderWidth;
+ if (rect.color != null)
+ this.color = rect.color;
+ if (rect.background != null)
+ this.background = rect.background;
+ if (rect.borderColorLeft != null)
+ this.borderColorLeft = rect.borderColorLeft;
+ if (rect.borderColorRight != null)
+ this.borderColorRight = rect.borderColorRight;
+ if (rect.borderColorTop != null)
+ this.borderColorTop = rect.borderColorTop;
+ if (rect.borderColorBottom != null)
+ this.borderColorBottom = rect.borderColorBottom;
+ if (rect.borderWidthLeft != UNDEFINED)
+ this.borderWidthLeft = rect.borderWidthLeft;
+ if (rect.borderWidthRight != UNDEFINED)
+ this.borderWidthRight = rect.borderWidthRight;
+ if (rect.borderWidthTop != UNDEFINED)
+ this.borderWidthTop = rect.borderWidthTop;
+ if (rect.borderWidthBottom != UNDEFINED)
+ this.borderWidthBottom = rect.borderWidthBottom;
+ if (useVariableBorders)
+ this.useVariableBorders = rect.useVariableBorders;
+ }
+
+ // implementation of the Element interface
+
+ /**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener
+ * an ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ return listener.add(this);
+ } catch (DocumentException de) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.RECTANGLE;
+ }
+
+ /**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ return new ArrayList();
+ }
+
+ // methods
+
+ /**
+ * Switches lowerleft with upperright
+ */
+ public void normalize() {
+ if (llx > urx) {
+ float a = llx;
+ llx = urx;
+ urx = a;
+ }
+ if (lly > ury) {
+ float a = lly;
+ lly = ury;
+ ury = a;
+ }
+ }
+
+ /**
+ * Gets a Rectangle that is altered to fit on the page.
+ *
+ * @param top
+ * the top position
+ * @param bottom
+ * the bottom position
+ * @return a Rectangle
+ */
+
+ public Rectangle rectangle(float top, float bottom) {
+ Rectangle tmp = new Rectangle(this);
+ if (top() > top) {
+ tmp.setTop(top);
+ tmp.setBorder(border - (border & TOP));
+ }
+ if (bottom() < bottom) {
+ tmp.setBottom(bottom);
+ tmp.setBorder(border - (border & BOTTOM));
+ }
+ return tmp;
+ }
+
+ /**
+ * Swaps the values of urx and ury and of lly and llx in order to rotate the
+ * rectangle.
+ *
+ * @return a Rectangle
+ */
+
+ public Rectangle rotate() {
+ Rectangle rect = new Rectangle(lly, llx, ury, urx);
+ rect.rotation = rotation + 90;
+ rect.rotation %= 360;
+ return rect;
+ }
+
+ // methods to set the membervariables
+
+ /**
+ * Sets the lower left x-coordinate.
+ *
+ * @param value
+ * the new value
+ */
+
+ public void setLeft(float value) {
+ llx = value;
+ }
+
+ /**
+ * Sets the upper right x-coordinate.
+ *
+ * @param value
+ * the new value
+ */
+
+ public void setRight(float value) {
+ urx = value;
+ }
+
+ /**
+ * Sets the upper right y-coordinate.
+ *
+ * @param value
+ * the new value
+ */
+
+ public void setTop(float value) {
+ ury = value;
+ }
+
+ /**
+ * Sets the lower left y-coordinate.
+ *
+ * @param value
+ * the new value
+ */
+
+ public void setBottom(float value) {
+ lly = value;
+ }
+
+ /**
+ * Enables/Disables the border on the specified sides. The border is
+ * specified as an integer bitwise combination of the constants:
+ * LEFT, RIGHT, TOP, BOTTOM
.
+ *
+ * @see #enableBorderSide(int)
+ * @see #disableBorderSide(int)
+ * @param value
+ * the new value
+ */
+
+ public void setBorder(int value) {
+ border = value;
+ }
+
+ /**
+ * Enables the border on the specified side.
+ *
+ * @param side
+ * the side to enable. One of LEFT, RIGHT, TOP, BOTTOM
+ *
+ */
+ public void enableBorderSide(int side) {
+ if (border == UNDEFINED) {
+ border = 0;
+ }
+ border |= side;
+ }
+
+ /**
+ * Disables the border on the specified side.
+ *
+ * @param side
+ * the side to disable. One of LEFT, RIGHT, TOP, BOTTOM
+ *
+ */
+ public void disableBorderSide(int side) {
+ if (border == UNDEFINED) {
+ border = 0;
+ }
+ border &= ~side;
+ }
+
+ /**
+ * Sets the borderwidth of the table.
+ *
+ * @param value
+ * the new value
+ */
+
+ public void setBorderWidth(float value) {
+ borderWidth = value;
+ }
+
+ /**
+ * Sets the color of the border.
+ *
+ * @param value
+ * the new value
+ */
+
+ public void setBorderColor(Color value) {
+ color = value;
+ }
+
+ /**
+ * Sets the value of the border color
+ *
+ * @param value
+ * a color value
+ */
+ public void setBorderColorRight(Color value) {
+ borderColorRight = value;
+ }
+
+ /**
+ * Sets the value of the border color
+ *
+ * @param value
+ * a color value
+ */
+ public void setBorderColorLeft(Color value) {
+ borderColorLeft = value;
+ }
+
+ /**
+ * Sets the value of the border color
+ *
+ * @param value
+ * a color value
+ */
+ public void setBorderColorTop(Color value) {
+ borderColorTop = value;
+ }
+
+ /**
+ * Sets the value of the border color
+ *
+ * @param value
+ * a color value
+ */
+ public void setBorderColorBottom(Color value) {
+ borderColorBottom = value;
+ }
+
+ /**
+ * Sets the backgroundcolor of the rectangle.
+ *
+ * @param value
+ * the new value
+ */
+
+ public void setBackgroundColor(Color value) {
+ background = value;
+ }
+
+ /**
+ * Sets the grayscale of the rectangle.
+ *
+ * @param value
+ * the new value
+ */
+
+ public void setGrayFill(float value) {
+ background = new GrayColor(value);
+ }
+
+ // methods to get the membervariables
+
+ /**
+ * Returns the lower left x-coordinate.
+ *
+ * @return the lower left x-coordinate
+ */
+
+ public float left() {
+ return llx;
+ }
+
+ /**
+ * Returns the upper right x-coordinate.
+ *
+ * @return the upper right x-coordinate
+ */
+
+ public float right() {
+ return urx;
+ }
+
+ /**
+ * Returns the upper right y-coordinate.
+ *
+ * @return the upper right y-coordinate
+ */
+
+ public float top() {
+ return ury;
+ }
+
+ /**
+ * Returns the lower left y-coordinate.
+ *
+ * @return the lower left y-coordinate
+ */
+
+ public float bottom() {
+ return lly;
+ }
+
+ /**
+ * Returns the lower left x-coordinate, considering a given margin.
+ *
+ * @param margin
+ * a margin
+ * @return the lower left x-coordinate
+ */
+
+ public float left(float margin) {
+ return llx + margin;
+ }
+
+ /**
+ * Returns the upper right x-coordinate, considering a given margin.
+ *
+ * @param margin
+ * a margin
+ * @return the upper right x-coordinate
+ */
+
+ public float right(float margin) {
+ return urx - margin;
+ }
+
+ /**
+ * Returns the upper right y-coordinate, considering a given margin.
+ *
+ * @param margin
+ * a margin
+ * @return the upper right y-coordinate
+ */
+
+ public float top(float margin) {
+ return ury - margin;
+ }
+
+ /**
+ * Returns the lower left y-coordinate, considering a given margin.
+ *
+ * @param margin
+ * a margin
+ * @return the lower left y-coordinate
+ */
+
+ public float bottom(float margin) {
+ return lly + margin;
+ }
+
+ /**
+ * Returns the width of the rectangle.
+ *
+ * @return a width
+ */
+
+ public float width() {
+ return urx - llx;
+ }
+
+ /**
+ * Returns the height of the rectangle.
+ *
+ * @return a height
+ */
+
+ public float height() {
+ return ury - lly;
+ }
+
+ /**
+ * Indicates if the table has borders.
+ *
+ * @return a boolean
+ */
+
+ public boolean hasBorders() {
+ return (border > 0)
+ && ((borderWidth > 0) || (borderWidthLeft > 0)
+ || (borderWidthRight > 0) || (borderWidthTop > 0) || (borderWidthBottom > 0));
+ }
+
+ /**
+ * Indicates if the table has a some type of border.
+ *
+ * @param type
+ * the type of border
+ * @return a boolean
+ */
+
+ public boolean hasBorder(int type) {
+ return border != UNDEFINED && (border & type) == type;
+ }
+
+ /**
+ * Returns the exact type of the border.
+ *
+ * @return a value
+ */
+
+ public int border() {
+ return border;
+ }
+
+ /**
+ * Gets the borderwidth.
+ *
+ * @return a value
+ */
+
+ public float borderWidth() {
+ return borderWidth;
+ }
+
+ /**
+ * Gets the color of the border.
+ *
+ * @return a value
+ */
+
+ public Color borderColor() {
+ return color;
+ }
+
+ /**
+ * Gets the backgroundcolor.
+ *
+ * @return a value
+ */
+
+ public Color backgroundColor() {
+ return background;
+ }
+
+ /**
+ * Gets the grayscale.
+ *
+ * @return a value
+ */
+
+ public float grayFill() {
+ if (background instanceof GrayColor)
+ return ((GrayColor)background).getGray();
+ else
+ return 0;
+ }
+
+ /**
+ * Gets the rotation of the rectangle
+ *
+ * @return a rotation value
+ */
+ public int getRotation() {
+ return rotation;
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttribute(java.lang.String,
+ * java.lang.String)
+ */
+ public void setMarkupAttribute(String name, String value) {
+ if (markupAttributes == null)
+ markupAttributes = new Properties();
+ markupAttributes.put(name, value);
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttributes(java.util.Properties)
+ */
+ public void setMarkupAttributes(Properties markupAttributes) {
+ this.markupAttributes = markupAttributes;
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttribute(java.lang.String)
+ */
+ public String getMarkupAttribute(String name) {
+ return (markupAttributes == null) ? null : String
+ .valueOf(markupAttributes.get(name));
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributeNames()
+ */
+ public Set getMarkupAttributeNames() {
+ return Chunk.getKeySet(markupAttributes);
+ }
+
+ /**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributes()
+ */
+ public Properties getMarkupAttributes() {
+ return markupAttributes;
+ }
+
+ /**
+ * Gets the color of a border.
+ *
+ * @return a color value
+ */
+ public Color getBorderColorLeft() {
+ if (borderColorLeft == null) return color;
+ return borderColorLeft;
+ }
+
+ /**
+ * Gets the color of a border.
+ *
+ * @return a color value
+ */
+ public Color getBorderColorRight() {
+ if (borderColorRight == null) return color;
+ return borderColorRight;
+ }
+
+ /**
+ * Gets the color of a border.
+ *
+ * @return a color value
+ */
+ public Color getBorderColorTop() {
+ if (borderColorTop == null) return color;
+ return borderColorTop;
+ }
+
+ /**
+ * Gets the color of a border.
+ *
+ * @return a color value
+ */
+ public Color getBorderColorBottom() {
+ if (borderColorBottom == null) return color;
+ return borderColorBottom;
+ }
+
+ /**
+ * Gets the width of a border.
+ *
+ * @return a width
+ */
+ public float getBorderWidthLeft() {
+ return getVariableBorderWidth(borderWidthLeft, LEFT);
+ }
+
+ /**
+ * Sets the width of a border
+ *
+ * @param borderWidthLeft
+ * a width
+ */
+ public void setBorderWidthLeft(float borderWidthLeft) {
+ this.borderWidthLeft = borderWidthLeft;
+ updateBorderBasedOnWidth(borderWidthLeft, LEFT);
+ }
+
+ /**
+ * Gets the width of a border.
+ *
+ * @return a width
+ */
+ public float getBorderWidthRight() {
+ return getVariableBorderWidth(borderWidthRight, RIGHT);
+ }
+
+ /**
+ * Sets the width of a border
+ *
+ * @param borderWidthRight
+ * a width
+ */
+ public void setBorderWidthRight(float borderWidthRight) {
+ this.borderWidthRight = borderWidthRight;
+ updateBorderBasedOnWidth(borderWidthRight, RIGHT);
+ }
+
+ /**
+ * Gets the width of a border.
+ *
+ * @return a width
+ */
+ public float getBorderWidthTop() {
+ return getVariableBorderWidth(borderWidthTop, TOP);
+ }
+
+ /**
+ * Sets the width of a border
+ *
+ * @param borderWidthTop
+ * a width
+ */
+ public void setBorderWidthTop(float borderWidthTop) {
+ this.borderWidthTop = borderWidthTop;
+ updateBorderBasedOnWidth(borderWidthTop, TOP);
+ }
+
+ /**
+ * Gets the width of a border.
+ *
+ * @return a width
+ */
+ public float getBorderWidthBottom() {
+ return getVariableBorderWidth(borderWidthBottom, BOTTOM);
+ }
+
+ /**
+ * Sets the width of a border
+ *
+ * @param borderWidthBottom
+ * a width
+ */
+ public void setBorderWidthBottom(float borderWidthBottom) {
+ this.borderWidthBottom = borderWidthBottom;
+ updateBorderBasedOnWidth(borderWidthBottom, BOTTOM);
+ }
+
+ /**
+ * Updates the border flag for a side based on the specified width. A width
+ * of 0 will disable the border on that side. Any other width enables it.
+ *
+ * @param width
+ * width of border
+ * @param side
+ * border side constant
+ */
+
+ private void updateBorderBasedOnWidth(float width, int side) {
+ useVariableBorders = true;
+ if (width > 0) {
+ enableBorderSide(side);
+ } else {
+ disableBorderSide(side);
+ }
+ }
+
+ private float getVariableBorderWidth(float variableWidthValue, int side) {
+ if ((border & side) != 0) {
+ return variableWidthValue != UNDEFINED ? variableWidthValue
+ : borderWidth;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Indicates whether variable width borders are being used. Returns true if
+ * setBorderWidthLeft, setBorderWidthRight, setBorderWidthTop, or
+ * setBorderWidthBottom
has been called.
+ *
+ * @return true if variable width borders are in use
+ *
+ */
+ public boolean isUseVariableBorders() {
+ return useVariableBorders;
+ }
+
+ /**
+ * Sets a parameter indicating if the rectangle has variable borders
+ *
+ * @param useVariableBorders
+ * indication if the rectangle has variable borders
+ */
+ public void setUseVariableBorders(boolean useVariableBorders) {
+ this.useVariableBorders = useVariableBorders;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer("Rectangle: ");
+ buf.append(width());
+ buf.append("x");
+ buf.append(height());
+ buf.append(" (rot: ");
+ buf.append(rotation);
+ buf.append(" degrees)");
+ return buf.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/RomanList.java b/src/main/java/com/lowagie/text/RomanList.java
new file mode 100644
index 0000000..e4b9ed7
--- /dev/null
+++ b/src/main/java/com/lowagie/text/RomanList.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2003 by Michael Niedermair.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text;
+
+/**
+ *
+ * A special-version of LIST
which use roman-letters.
+ *
+ * @see com.lowagie.text.List
+ * @version 2003-06-22
+ * @author Michael Niedermair
+ */
+
+public class RomanList extends List {
+
+ /**
+ * UpperCase or LowerCase
+ */
+ protected boolean romanlower;
+
+ /**
+ * Initialization
+ *
+ * @param symbolIndent indent
+ */
+ public RomanList(int symbolIndent) {
+ super(true, symbolIndent);
+ }
+
+ /**
+ * Initialization
+ * @param romanlower roman-char in lowercase
+ * @param symbolIndent indent
+ */
+ public RomanList(boolean romanlower, int symbolIndent) {
+ super(true, symbolIndent);
+ this.romanlower = romanlower;
+ }
+
+ /**
+ * set the roman-letters to lowercase otherwise to uppercase
+ *
+ * @param romanlower
+ */
+ public void setRomanLower(boolean romanlower) {
+ this.romanlower = romanlower;
+ }
+
+ /**
+ * Checks if the list is roman-letter with lowercase
+ *
+ * @return true
if the roman-letter is lowercase, false
otherwise.
+ */
+ public boolean isRomanLower() {
+ return romanlower;
+ }
+
+ /**
+ * Adds an Object
to the List
.
+ *
+ * @param o the object to add.
+ * @return true if adding the object succeeded
+ */
+ public boolean add(Object o) {
+ if (o instanceof ListItem) {
+ ListItem item = (ListItem) o;
+ Chunk chunk;
+ if (romanlower)
+ chunk = new Chunk(toRomanLowerCase(first + list.size()), symbol.font());
+ else
+ chunk = new Chunk(toRomanUppercase(first + list.size()), symbol.font());
+ chunk.append(".");
+ item.setListSymbol(chunk);
+ item.setIndentationLeft(symbolIndent);
+ item.setIndentationRight(0);
+ list.add(item);
+ } else if (o instanceof List) {
+ List nested = (List) o;
+ nested.setIndentationLeft(nested.indentationLeft() + symbolIndent);
+ first--;
+ return list.add(nested);
+ } else if (o instanceof String) {
+ return this.add(new ListItem((String) o));
+ }
+ return false;
+ }
+
+ // ****************************************************************************************
+
+ /*
+ * Wandelt eine Integer-Zahl in römische Schreibweise um
+ *
+ * Regeln: http://de.wikipedia.org/wiki/R%F6mische_Ziffern
+ *
+ * 1. Die Ziffern werden addiert, wobei sie von groß nach klein sortiert sind:
+ *
+ * XVII = 10+5+1+1=17
+ *
+ * 2. Eine kleinere Ziffer, die links von einer größeren steht, wird abgezogen:
+ *
+ * IV = 5-1=4
+ * CM = 1000-100=900
+ *
+ * 3. Maximal drei gleiche Ziffern stehen nebeneinander (Ausnahme: IIII auf Zifferblaettern von Uhren):
+ *
+ * XL = 40 (und nicht XXXX)
+ * IX = 9 (und nicht VIIII)
+ * Diese "Subtraktionsschreibweise" ist erst im Mittelalter allgemein gebräuchlich geworden.
+ * Vorher wurde oft "IIII" für "4" geshrieben.
+ *
+ * 4. Bei mehreren möglichen Schreibweisen wird in der Regel der kürzesten der Vorzug gegeben:
+ *
+ * IC = 99 (auch LXLIX)
+ * IL = 49 (auch XLIX oder sogar XLVIV)
+ * Andererseits gibt es die Vorschrift, nach der ein Symbol, das einen Wert von 10n darstellt,
+ * nicht einem Symbol, das einen Wert von 10(n+1) darstellt, direkt voranstehen darf.
+ * Nach dieser Regel wäre die Schreibweise "XCIX" für "99" der Schreibweise "IC" vorzuziehen.
+ *
+ * 5. Die römischen Zahlen V, L und D können nicht größeren Zahlen voran gestellt werden:
+ *
+ * XCV = 95 (nicht VC)
+ *
+ * Zahlen über 3000 werden dargestellt durch Einkastung der Tausender: |IX|LIV=9054
+ *
+ *
+ * Zahlen größer als 3.000.000 werden durch Doppelstrich etc. dargestellt.
+ */
+
+ /**
+ * Array with Roman digits.
+ */
+ private static final RomanDigit[] roman =
+ {
+ new RomanDigit('m', 1000, false),
+ new RomanDigit('d', 500, false),
+ new RomanDigit('c', 100, true),
+ new RomanDigit('l', 50, false),
+ new RomanDigit('x', 10, true),
+ new RomanDigit('v', 5, false),
+ new RomanDigit('i', 1, true)};
+
+ /**
+ * changes an int into a lower case roman number.
+ * @param number the original number
+ * @return the roman number (lower case)
+ */
+ public static String toRoman(int number) {
+ return toRomanLowerCase(number);
+ }
+
+ /**
+ * Changes an int into an upper case roman number.
+ * @param number the original number
+ * @return the roman number (upper case)
+ */
+ public static String toRomanUppercase(int number) {
+ return toRomanLowerCase(number).toUpperCase();
+ }
+
+ /**
+ * Changes an int into a lower case roman number.
+ * @param number the original number
+ * @return the roman number (lower case)
+ */
+ public static String toRomanLowerCase(int number) {
+
+ // Buffer
+ StringBuffer buf = new StringBuffer();
+
+ // kleiner 0 ? Vorzeichen festlegen
+ if (number < 0) {
+ buf.append('-');
+ number = -number;
+ }
+
+ // größer 3000
+ if (number > 3000) {
+ // rekursiver Aufruf (ohne tausender-Bereich)
+ buf.append('|');
+ buf.append(toRomanLowerCase(number / 1000));
+ buf.append('|');
+ // tausender-Bereich
+ number = number - (number / 1000) * 1000;
+ }
+
+ // Schleife
+ int pos = 0;
+ while (true) {
+ // roman-array durchlaufen
+ RomanDigit dig = roman[pos];
+
+ // solange Zahl größer roman-Wert
+ while (number >= dig.value) {
+ // Zeichen hinzufügen
+ buf.append(dig.digit);
+ // Wert des Zeichens abziehen
+ number -= dig.value;
+ }
+
+ // Abbruch
+ if (number <= 0) {
+ break;
+ }
+ // pre=false suchen (ab Stelle pos)
+ int j = pos;
+ while (!roman[++j].pre);
+
+ // neuer Wert größer
+ if (number + roman[j].value >= dig.value) {
+ // hinzufügen
+ buf.append(roman[j].digit).append(dig.digit);
+ // Wert vom Rest abziehen
+ number -= dig.value - roman[j].value;
+ }
+ pos++;
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Helper class for Roman Digits
+ */
+ private static class RomanDigit {
+
+ /** part of a roman number */
+ public char digit;
+
+ /** value of the roman digit */
+ public int value;
+
+ /** can the digit be used as a prefix */
+ public boolean pre;
+
+ /**
+ * Constructs a roman digit
+ * @param digit the roman digit
+ * @param value the value
+ * @param pre can it be used as a prefix
+ */
+ RomanDigit(char digit, int value, boolean pre) {
+ this.digit = digit;
+ this.value = value;
+ this.pre = pre;
+ }
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/Row.java b/src/main/java/com/lowagie/text/Row.java
new file mode 100644
index 0000000..3604d92
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Row.java
@@ -0,0 +1,470 @@
+/*
+ * $Id: Row.java,v 1.63 2005/04/13 09:17:11 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 LIBRARY GENERAL PUBLIC LICENSE for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.util.ArrayList;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * A Row
is part of a Table
+ * and contains some Cells
.
+ * Row
s are constructed by a Table
-object.
+ * You don't have to construct any Row
yourself.
+ * In fact you can't construct a Row
outside the package.
+ * Cell
can span several rows and/or columns
+ * a row can contain reserved space without any content.
+ *
+ * @see Element
+ * @see Cell
+ * @see Table
+ */
+
+public class Row implements Element, MarkupAttributes {
+
+ // membervariables
+
+/** id of a null element in a Row*/
+ public static final int NULL = 0;
+
+/** id of the Cell element in a Row*/
+ public static final int CELL = 1;
+
+/** id of the Table element in a Row*/
+ public static final int TABLE = 2;
+
+/** This is the number of columns in the Row
. */
+ protected int columns;
+
+/** This is a valid position the Row
. */
+ protected int currentColumn;
+
+/** This is the array that keeps track of reserved cells. */
+ protected boolean[] reserved;
+
+/** This is the array of Objects (Cell
or Table
). */
+ protected Object[] cells;
+
+/** This is the vertical alignment. */
+ protected int horizontalAlignment;
+
+/** This is the vertical alignment. */
+ protected int verticalAlignment;
+
+/** Contains extra markupAttributes */
+ protected Properties markupAttributes;
+
+ // constructors
+
+/**
+ * Constructs a Row
with a certain number of columns.
+ *
+ * @param columns a number of columns
+ */
+
+ protected Row(int columns) {
+ this.columns = columns;
+ reserved = new boolean[columns];
+ cells = new Object[columns];
+ currentColumn = 0;
+ }
+
+ // implementation of the Element-methods
+
+/**
+ * Processes the element by adding it (or the different parts) to a
+ * ElementListener
.
+ *
+ * @param listener an ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ return listener.add(this);
+ }
+ catch(DocumentException de) {
+ return false;
+ }
+ }
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.ROW;
+ }
+
+/**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ return new ArrayList();
+ }
+
+/**
+ * Returns a Row
that is a copy of this Row
+ * in which a certain column has been deleted.
+ *
+ * @param column the number of the column to delete
+ */
+
+ void deleteColumn(int column) {
+ if ((column >= columns) || (column < 0)) {
+ throw new IndexOutOfBoundsException("getCell at illegal index : " + column);
+ }
+ columns--;
+ boolean newReserved[] = new boolean[columns];
+ Object newCells[] = new Cell[columns];
+
+ for (int i = 0; i < column; i++) {
+ newReserved[i] = reserved[i];
+ newCells[i] = cells[i];
+ if (newCells[i] != null && (i + ((Cell) newCells[i]).colspan() > column)) {
+ ((Cell) newCells[i]).setColspan(((Cell) cells[i]).colspan() - 1);
+ }
+ }
+ for (int i = column; i < columns; i++) {
+ newReserved[i] = reserved[i + 1];
+ newCells[i] = cells[i + 1];
+ }
+ if (cells[column] != null && ((Cell) cells[column]).colspan() > 1) {
+ newCells[column] = cells[column];
+ ((Cell) newCells[column]).setColspan(((Cell) newCells[column]).colspan() - 1);
+ }
+ reserved = newReserved;
+ cells = newCells;
+ }
+
+ // methods
+
+/**
+ * Adds a Cell
to the Row
.
+ *
+ * @param element the element to add (currently only Cells and Tables supported)
+ * @return the column position the Cell
was added,
+ * or -1
if the element
couldn't be added.
+ */
+
+ int addElement(Object element) {
+ return addElement(element, currentColumn);
+ }
+
+/**
+ * Adds an element to the Row
at the position given.
+ *
+ * @param element the element to add. (currently only Cells and Tables supported
+ * @param column the position where to add the cell.
+ * @return the column position the Cell
was added,
+ * or -1
if the Cell
couldn't be added.
+ */
+
+ int addElement(Object element, int column) {
+ if (element == null) throw new NullPointerException("addCell - null argument");
+ if ((column < 0) || (column > columns)) throw new IndexOutOfBoundsException("addCell - illegal column argument");
+ if ( !((getObjectID(element) == CELL) || (getObjectID(element) == TABLE)) ) throw new IllegalArgumentException("addCell - only Cells or Tables allowed");
+
+ int lColspan = ( (Cell.class.isInstance(element)) ? ((Cell) element).colspan() : 1);
+
+ if ( reserve(column, lColspan) == false ) {
+ return -1;
+ }
+
+ cells[column] = element;
+ currentColumn += lColspan - 1;
+
+ return column;
+ }
+
+/**
+ * Puts Cell
to the Row
at the position given, doesn't reserve colspan.
+ *
+ * @param aElement the cell to add.
+ * @param column the position where to add the cell.
+ */
+
+ void setElement(Object aElement, int column) {
+ if (reserved[column] == true) throw new IllegalArgumentException("setElement - position already taken");
+
+ cells[column] = aElement;
+ if (aElement != null) {
+ reserved[column] = true;
+ }
+ }
+
+/**
+ * Reserves a Cell
in the Row
.
+ *
+ * @param column the column that has to be reserved.
+ * @return true
if the column was reserved, false
if not.
+ */
+
+ boolean reserve(int column) {
+ return reserve(column, 1);
+ }
+
+
+/**
+ * Reserves a Cell
in the Row
.
+ *
+ * @param column the column that has to be reserved.
+ * @param size the number of columns
+ * @return true
if the column was reserved, false
if not.
+ */
+
+ boolean reserve(int column, int size) {
+ if ((column < 0) || ((column + size) > columns)) throw new IndexOutOfBoundsException("reserve - incorrect column/size");
+
+ for(int i=column; i < column + size; i++)
+ {
+ if (reserved[i] == true) {
+ // undo reserve
+ for(int j=i; j >= column; j--) {
+ reserved[i] = false;
+ }
+ return false;
+ }
+ reserved[i] = true;
+ }
+ return true;
+ }
+
+/**
+ * Sets the horizontal alignment.
+ *
+ * @param value the new value
+ */
+
+ public void setHorizontalAlignment(int value) {
+ horizontalAlignment = value;
+ }
+
+/**
+ * Sets the vertical alignment.
+ *
+ * @param value the new value
+ */
+
+ public void setVerticalAlignment(int value) {
+ verticalAlignment = value;
+ }
+
+ // methods to retrieve information
+
+/**
+ * Returns true/false when this position in the Row
has been reserved, either filled or through a colspan of an Element.
+ *
+ * @param column the column.
+ * @return true
if the column was reserved, false
if not.
+ */
+
+ boolean isReserved(int column) {
+ return reserved[column];
+ }
+
+/**
+ * Returns the type-id of the element in a Row.
+ *
+ * @param column the column of which you'd like to know the type
+ * @return the type-id of the element in the row
+ */
+
+ int getElementID(int column) {
+ if (cells[column] == null) return NULL;
+ else if (Cell.class.isInstance(cells[column])) return CELL;
+ else if (Table.class.isInstance(cells[column])) return TABLE;
+
+ return -1;
+ }
+
+
+/**
+ * Returns the type-id of an Object.
+ *
+ * @param element the object of which you'd like to know the type-id, -1 if invalid
+ * @return the type-id of an object
+ */
+
+ int getObjectID(Object element) {
+ if (element == null) return NULL;
+ else if (Cell.class.isInstance(element)) return CELL;
+ else if (Table.class.isInstance(element)) return TABLE;
+
+ return -1;
+ }
+
+
+/**
+ * Gets a Cell
or Table
from a certain column.
+ *
+ * @param column the column the Cell/Table
is in.
+ * @return the Cell
,Table
or Object if the column was
+ * reserved or null if empty.
+ */
+
+ public Object getCell(int column) {
+ if ((column < 0) || (column > columns)) {
+ throw new IndexOutOfBoundsException("getCell at illegal index :" + column + " max is " + columns);
+ }
+ return cells[column];
+ }
+
+/**
+ * Checks if the row is empty.
+ *
+ * @return true
if none of the columns is reserved.
+ */
+
+ public boolean isEmpty() {
+ for (int i = 0; i < columns; i++) {
+ if (cells[i] != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+/**
+ * Gets the index of the current, valid position
+ *
+ * @return a value
+ */
+
+ int validPosition() {
+ return currentColumn;
+ }
+
+/**
+ * Gets the number of columns.
+ *
+ * @return a value
+ */
+
+ public int columns() {
+ return columns;
+ }
+
+/**
+ * Gets the horizontal alignment.
+ *
+ * @return a value
+ */
+
+ public int horizontalAlignment() {
+ return horizontalAlignment;
+ }
+
+/**
+ * Gets the vertical alignment.
+ *
+ * @return a value
+ */
+
+ public int verticalAlignment() {
+ return verticalAlignment;
+ }
+
+/**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.ROW.equals(tag);
+ }
+
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttribute(java.lang.String, java.lang.String)
+ */
+ public void setMarkupAttribute(String name, String value) {
+ if (markupAttributes == null) markupAttributes = new Properties();
+ markupAttributes.put(name, value);
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#setMarkupAttributes(java.util.Properties)
+ */
+ public void setMarkupAttributes(Properties markupAttributes) {
+ this.markupAttributes = markupAttributes;
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttribute(java.lang.String)
+ */
+ public String getMarkupAttribute(String name) {
+ return (markupAttributes == null) ? null : String.valueOf(markupAttributes.get(name));
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributeNames()
+ */
+ public Set getMarkupAttributeNames() {
+ return Chunk.getKeySet(markupAttributes);
+ }
+
+/**
+ * @see com.lowagie.text.MarkupAttributes#getMarkupAttributes()
+ */
+ public Properties getMarkupAttributes() {
+ return markupAttributes;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/Section.java b/src/main/java/com/lowagie/text/Section.java
new file mode 100644
index 0000000..ea5c4ec
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Section.java
@@ -0,0 +1,660 @@
+/*
+ * $Id: Section.java,v 1.81 2005/09/28 07:55:29 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Properties;
+
+/**
+ * A Section
is a part of a Document
containing
+ * other Section
s, Paragraph
s, List
+ * and/or Table
s.
+ * Section
yourself.
+ * You will have to ask an instance of Section
to the
+ * Chapter
or Section
to which you want to
+ * add the new Section
.
+ *
+ */
+
+public class Section extends ArrayList implements TextElementArray {
+
+ // membervariables
+
+/** This is the title of this section. */
+ protected Paragraph title;
+
+/** This is the number of sectionnumbers that has to be shown before the section title. */
+ protected int numberDepth;
+
+/** The indentation of this section on the left side. */
+ protected float indentationLeft;
+
+/** The indentation of this section on the right side. */
+ protected float indentationRight;
+
+/** The additional indentation of the content of this section. */
+ protected float sectionIndent;
+
+/** This is the number of subsections. */
+ protected int subsections = 0;
+
+/** This is the complete list of sectionnumbers of this section and the parents of this section. */
+ protected ArrayList numbers = null;
+
+ /** false if the bookmark children are not visible */
+ protected boolean bookmarkOpen = true;
+
+ /** The bookmark title if different from the content title */
+ protected String bookmarkTitle;
+ // constructors
+
+/**
+ * Constructs a new
+ * Paragraph title2 = new Paragraph("This is Chapter 2", FontFactory.getFont(FontFactory.HELVETICA, 18, Font.BOLDITALIC, new Color(0, 0, 255)));
+ * Chapter chapter2 = new Chapter(title2, 2);
+ * Paragraph someText = new Paragraph("This is some text");
+ * chapter2.add(someText);
+ * Paragraph title21 = new Paragraph("This is Section 1 in Chapter 2", FontFactory.getFont(FontFactory.HELVETICA, 16, Font.BOLD, new Color(255, 0, 0)));
+ * Section section1 = chapter2.addSection(title21);
+ * Paragraph someSectionText = new Paragraph("This is some silly paragraph in a chapter and/or section. It contains some text to test the functionality of Chapters and Section.");
+ * section1.add(someSectionText);
+ * Paragraph title211 = new Paragraph("This is SubSection 1 in Section 1 in Chapter 2", FontFactory.getFont(FontFactory.HELVETICA, 14, Font.BOLD, new Color(255, 0, 0)));
+ * Section section11 = section1.addSection(40, title211, 2);
+ * section11.add(someSectionText);
+ *
Section
.
+ */
+
+ protected Section() {
+ title = new Paragraph();
+ numberDepth = 1;
+ }
+
+/**
+ * Constructs a new Section
.
+ *
+ * @param title a Paragraph
+ * @param numberDepth the numberDepth
+ */
+
+ Section(Paragraph title, int numberDepth) {
+ this.numberDepth = numberDepth;
+ this.title = title;
+ }
+
+ // private methods
+
+/**
+ * Sets the number of this section.
+ *
+ * @param number the number of this section
+ * @param numbers an ArrayList
, containing the numbers of the Parent
+ */
+
+ private void setNumbers(int number, ArrayList numbers) {
+ this.numbers = new ArrayList();
+ this.numbers.add(new Integer(number));
+ this.numbers.addAll(numbers);
+ }
+
+ // implementation of the Element-methods
+
+/**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener the ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ for (Iterator i = iterator(); i.hasNext(); ) {
+ listener.add((Element) i.next());
+ }
+ return true;
+ }
+ catch(DocumentException de) {
+ return false;
+ }
+ }
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.SECTION;
+ }
+
+/**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ ArrayList tmp = new ArrayList();
+ for (Iterator i = iterator(); i.hasNext(); ) {
+ tmp.addAll(((Element) i.next()).getChunks());
+ }
+ return tmp;
+ }
+
+ // overriding some of the ArrayList-methods
+
+/**
+ * Adds a Paragraph
, List
or Table
+ * to this Section
.
+ *
+ * @param index index at which the specified element is to be inserted
+ * @param o an object of type Paragraph
, List
or Table
=
+ * @throws ClassCastException if the object is not a Paragraph
, List
or Table
+ */
+
+ public void add(int index, Object o) {
+ try {
+ Element element = (Element) o;
+ if (element.type() == Element.PARAGRAPH ||
+ element.type() == Element.LIST ||
+ element.type() == Element.CHUNK ||
+ element.type() == Element.PHRASE ||
+ element.type() == Element.ANCHOR ||
+ element.type() == Element.ANNOTATION ||
+ element.type() == Element.TABLE ||
+ element.type() == Element.PTABLE ||
+ element.type() == Element.IMGTEMPLATE ||
+ element.type() == Element.JPEG ||
+ element.type() == Element.IMGRAW) {
+ super.add(index, element);
+ }
+ else {
+ throw new ClassCastException(String.valueOf(element.type()));
+ }
+ }
+ catch(ClassCastException cce) {
+ throw new ClassCastException("Insertion of illegal Element: " + cce.getMessage());
+ }
+ }
+
+/**
+ * Adds a Paragraph
, List
, Table
or another Section
+ * to this Section
.
+ *
+ * @param o an object of type Paragraph
, List
, Table
or another Section
+ * @return a boolean
+ * @throws ClassCastException if the object is not a Paragraph
, List
, Table
or Section
+ */
+
+ public boolean add(Object o) {
+ try {
+ Element element = (Element) o;
+ if (element.type() == Element.PARAGRAPH ||
+ element.type() == Element.LIST ||
+ element.type() == Element.CHUNK ||
+ element.type() == Element.PHRASE ||
+ element.type() == Element.ANCHOR ||
+ element.type() == Element.ANNOTATION ||
+ element.type() == Element.TABLE ||
+ element.type() == Element.IMGTEMPLATE ||
+ element.type() == Element.PTABLE ||
+ element.type() == Element.JPEG ||
+ element.type() == Element.IMGRAW) {
+ return super.add(o);
+ }
+ else if (element.type() == Element.SECTION) {
+ Section section = (Section) o;
+ section.setNumbers(++subsections, numbers);
+ return super.add(section);
+ }
+ else {
+ throw new ClassCastException(String.valueOf(element.type()));
+ }
+ }
+ catch(ClassCastException cce) {
+ throw new ClassCastException("Insertion of illegal Element: " + cce.getMessage());
+ }
+ }
+
+/**
+ * Adds a collection of Element
s
+ * to this Section
.
+ *
+ * @param collection a collection of Paragraph
s, List
s and/or Table
s
+ * @return true
if the action succeeded, false
if not.
+ * @throws ClassCastException if one of the objects isn't a Paragraph
, List
, Table
+ */
+
+ public boolean addAll(Collection collection) {
+ for (Iterator iterator = collection.iterator(); iterator.hasNext(); ) {
+ this.add(iterator.next());
+ }
+ return true;
+ }
+
+ // methods that return a Section
+
+/**
+ * Creates a Section
, adds it to this Section
and returns it.
+ *
+ * @param indentation the indentation of the new section
+ * @param title the title of the new section
+ * @param numberDepth the numberDepth of the section
+ * @return a new Section object
+ */
+
+ public Section addSection(float indentation, Paragraph title, int numberDepth) {
+ Section section = new Section(title, numberDepth);
+ section.setIndentation(indentation);
+ add(section);
+ return section;
+ }
+
+/**
+ * Creates a Section
, adds it to this Section
and returns it.
+ *
+ * @param indentation the indentation of the new section
+ * @param title the title of the new section
+ * @return a new Section object
+ */
+
+ public Section addSection(float indentation, Paragraph title) {
+ Section section = new Section(title, 1);
+ section.setIndentation(indentation);
+ add(section);
+ return section;
+ }
+
+/**
+ * Creates a Section
, add it to this Section
and returns it.
+ *
+ * @param title the title of the new section
+ * @param numberDepth the numberDepth of the section
+ * @return a new Section object
+ */
+
+ public Section addSection(Paragraph title, int numberDepth) {
+ Section section = new Section(title, numberDepth);
+ add(section);
+ return section;
+ }
+
+/**
+ * Creates a Section
, adds it to this Section
and returns it.
+ *
+ * @param title the title of the new section
+ * @return a new Section object
+ */
+
+ public Section addSection(Paragraph title) {
+ Section section = new Section(title, numberDepth + 1);
+ add(section);
+ return section;
+ }
+
+/**
+ * Adds a Section
to this Section
and returns it.
+ *
+ * @param indentation the indentation of the new section
+ * @param title the title of the new section
+ * @param numberDepth the numberDepth of the section
+ * @return a new Section object
+ */
+
+ public Section addSection(float indentation, String title, int numberDepth) {
+ Section section = new Section(new Paragraph(title), numberDepth);
+ section.setIndentation(indentation);
+ add(section);
+ return section;
+ }
+
+/**
+ * Adds a Section
to this Section
and returns it.
+ *
+ * @param title the title of the new section
+ * @param numberDepth the numberDepth of the section
+ * @return a new Section object
+ */
+
+ public Section addSection(String title, int numberDepth) {
+ Section section = new Section(new Paragraph(title), numberDepth);
+ add(section);
+ return section;
+ }
+
+/**
+ * Adds a Section
to this Section
and returns it.
+ *
+ * @param indentation the indentation of the new section
+ * @param title the title of the new section
+ * @return a new Section object
+ */
+
+ public Section addSection(float indentation, String title) {
+ Section section = new Section(new Paragraph(title), 1);
+ section.setIndentation(indentation);
+ add(section);
+ return section;
+ }
+
+/**
+ * Adds a Section
to this Section
and returns it.
+ *
+ * @param title the title of the new section
+ * @return a new Section object
+ */
+
+ public Section addSection(String title) {
+ Section section = new Section(new Paragraph(title), numberDepth + 1);
+ add(section);
+ return section;
+ }
+
+/**
+ * Creates a given Section
following a set of attributes and adds it to this one.
+ *
+ * @param attributes the attributes
+ * @return a Section
+ */
+
+ public Section addSection(Properties attributes) {
+ Section section = new Section(new Paragraph(""), 1);
+ String value;
+ if ((value = (String)attributes.remove(ElementTags.NUMBER)) != null) {
+ subsections = Integer.parseInt(value) - 1;
+ }
+ section.set(attributes);
+ add(section);
+ return section;
+ }
+
+
+ // public methods
+
+/**
+ * Alters the attributes of this Section
.
+ *
+ * @param attributes the attributes
+ */
+
+ public void set(Properties attributes) {
+ String value;
+ if ((value = (String)attributes.remove(ElementTags.NUMBERDEPTH)) != null) {
+ setNumberDepth(Integer.parseInt(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENT)) != null) {
+ setIndentation(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENTATIONLEFT)) != null) {
+ setIndentationLeft(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.INDENTATIONRIGHT)) != null) {
+ setIndentationRight(Float.valueOf(value + "f").floatValue());
+ }
+ }
+
+/**
+ * Sets the title of this section.
+ *
+ * @param title the new title
+ */
+
+ public void setTitle(Paragraph title) {
+ this.title = title;
+ }
+
+/**
+ * Sets the depth of the sectionnumbers that will be shown preceding the title.
+ * Section
on the left side.
+ *
+ * @param indentation the indentation
+ */
+
+ public void setIndentationLeft(float indentation) {
+ indentationLeft = indentation;
+ }
+
+/**
+ * Sets the indentation of this Section
on the right side.
+ *
+ * @param indentation the indentation
+ */
+
+ public void setIndentationRight(float indentation) {
+ indentationRight = indentation;
+ }
+
+/**
+ * Sets the indentation of the content of this Section
.
+ *
+ * @param indentation the indentation
+ */
+
+ public void setIndentation(float indentation) {
+ sectionIndent = indentation;
+ }
+
+ // methods to retrieve information
+
+/**
+ * Checks if this object is a Chapter
.
+ *
+ * @return true
if it is a Chapter
,
+ * false
if it is a Section
.
+ */
+
+ public boolean isChapter() {
+ return type() == Element.CHAPTER;
+ }
+
+/**
+ * Checks if this object is a Section
.
+ *
+ * @return true
if it is a Section
,
+ * false
if it is a Chapter
.
+ */
+
+ public boolean isSection() {
+ return type() == Element.SECTION;
+ }
+
+/**
+ * Returns the numberdepth of this Section
.
+ *
+ * @return the numberdepth
+ */
+
+ public int numberDepth() {
+ return numberDepth;
+ }
+
+/**
+ * Returns the indentation of this Section
on the left side.
+ *
+ * @return the indentation
+ */
+
+ public float indentationLeft() {
+ return indentationLeft;
+ }
+
+/**
+ * Returns the indentation of this Section
on the right side.
+ *
+ * @return the indentation
+ */
+
+ public float indentationRight() {
+ return indentationRight;
+ }
+
+/**
+ * Returns the indentation of the content of this Section
.
+ *
+ * @return the indentation
+ */
+
+ public float indentation() {
+ return sectionIndent;
+ }
+
+/**
+ * Returns the depth of this section.
+ *
+ * @return the depth
+ */
+
+ public int depth() {
+ return numbers.size();
+ }
+
+/**
+ * Returns the title, preceeded by a certain number of sectionnumbers.
+ *
+ * @return a Paragraph
+ */
+
+ public Paragraph title() {
+ if (title == null) {
+ return null;
+ }
+ int depth = Math.min(numbers.size(), numberDepth);
+ if (depth < 1) {
+ return title;
+ }
+ StringBuffer buf = new StringBuffer(" ");
+ for (int i = 0; i < depth; i++) {
+ buf.insert(0, ".");
+ buf.insert(0, ((Integer) numbers.get(i)).intValue());
+ }
+ Paragraph result = new Paragraph(title);
+ result.setMarkupAttributes(title.getMarkupAttributes());
+ result.add(0, new Chunk(buf.toString(), title.font()));
+ return result;
+ }
+
+/**
+ * Checks if a given tag corresponds with a title tag for this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTitle(String tag) {
+ return ElementTags.TITLE.equals(tag);
+ }
+
+/**
+ * Checks if a given tag corresponds with this object.
+ *
+ * @param tag the given tag
+ * @return true if the tag corresponds
+ */
+
+ public static boolean isTag(String tag) {
+ return ElementTags.SECTION.equals(tag);
+ }
+
+ /** Getter for property bookmarkOpen.
+ * @return Value of property bookmarkOpen.
+ */
+ public boolean isBookmarkOpen() {
+ return bookmarkOpen;
+ }
+
+ /** Setter for property bookmarkOpen.
+ * @param bookmarkOpen false if the bookmark children are not
+ * visible.
+ */
+ public void setBookmarkOpen(boolean bookmarkOpen) {
+ this.bookmarkOpen = bookmarkOpen;
+ }
+
+ /**
+ * Gets the bookmark title.
+ * @return the bookmark title
+ */
+ public Paragraph getBookmarkTitle() {
+ if (bookmarkTitle == null)
+ return title();
+ else
+ return new Paragraph(bookmarkTitle);
+ }
+
+ /**
+ * Sets the bookmark title. The bookmark title is the same as the section title but
+ * can be changed with this method.
+ * @param bookmarkTitle the bookmark title
+ */
+ public void setBookmarkTitle(String bookmarkTitle) {
+ this.bookmarkTitle = bookmarkTitle;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/SimpleCell.java b/src/main/java/com/lowagie/text/SimpleCell.java
new file mode 100644
index 0000000..ab3a057
--- /dev/null
+++ b/src/main/java/com/lowagie/text/SimpleCell.java
@@ -0,0 +1,539 @@
+/*
+ * $Id: SimpleCell.java,v 1.7 2005/09/18 13:17:57 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999-2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 LIBRARY GENERAL PUBLIC LICENSE for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfPCell;
+import com.lowagie.text.pdf.PdfPCellEvent;
+import com.lowagie.text.pdf.PdfPTable;
+
+/**
+ * Rectangle that can be used for Cells.
+ * This Rectangle is padded and knows how to draw itself in a PdfPTable or PdfPcellEvent.
+ */
+public class SimpleCell extends Rectangle implements PdfPCellEvent, Element, TextElementArray {
+
+ /** the CellAttributes object represents a row. */
+ public static final boolean ROW = true;
+ /** the CellAttributes object represents a cell. */
+ public static final boolean CELL = false;
+ /** the content of the Cell. */
+ private ArrayList content = new ArrayList();
+ /** the width of the Cell. */
+ private float width = 0f;
+ /** the widthpercentage of the Cell. */
+ private float widthpercentage = 0f;
+ /** an extra spacing variable */
+ private float spacing_left = Float.NaN;
+ /** an extra spacing variable */
+ private float spacing_right = Float.NaN;
+ /** an extra spacing variable */
+ private float spacing_top = Float.NaN;
+ /** an extra spacing variable */
+ private float spacing_bottom = Float.NaN;
+ /** an extra padding variable */
+ private float padding_left = Float.NaN;
+ /** an extra padding variable */
+ private float padding_right = Float.NaN;
+ /** an extra padding variable */
+ private float padding_top = Float.NaN;
+ /** an extra padding variable */
+ private float padding_bottom = Float.NaN;
+ /** the colspan of a Cell */
+ private int colspan = 1;
+ /** horizontal alignment inside the Cell. */
+ private int horizontalAlignment = Element.ALIGN_UNDEFINED;
+ /** vertical alignment inside the Cell. */
+ private int verticalAlignment = Element.ALIGN_UNDEFINED;
+ /** indicates if these are the attributes of a single Cell (false) or a group of Cells (true). */
+ private boolean cellgroup = false;
+ /** Indicates that the largest ascender height should be used to determine the
+ * height of the first line. Note that this only has an effect when rendered
+ * to PDF. Setting this to true can help with vertical alignment problems. */
+ protected boolean useAscender = false;
+ /** Indicates that the largest descender height should be added to the height of
+ * the last line (so characters like y don't dip into the border). Note that
+ * this only has an effect when rendered to PDF. */
+ protected boolean useDescender = false;
+ /**
+ * Adjusts the cell contents to compensate for border widths. Note that
+ * this only has an effect when rendered to PDF.
+ */
+ protected boolean useBorderPadding;
+
+ /**
+ * A CellAttributes object is always constructed without any dimensions.
+ * Dimensions are defined after creation.
+ * @param row only true if the CellAttributes object represents a row.
+ */
+ public SimpleCell(boolean row) {
+ super(0f, 0f, 0f, 0f);
+ cellgroup = row;
+ setBorder(BOX);
+ }
+
+ /**
+ * Adds content to this object.
+ * @param element
+ * @throws BadElementException
+ */
+ public void addElement(Element element) throws BadElementException {
+ if (cellgroup) {
+ if (element instanceof SimpleCell) {
+ if(((SimpleCell)element).isCellgroup()) {
+ throw new BadElementException("You can't add one row to another row.");
+ }
+ content.add(element);
+ return;
+ }
+ else {
+ throw new BadElementException("You can only add cells to rows, no objects of type " + element.getClass().getName());
+ }
+ }
+ if (element.type() == Element.PARAGRAPH
+ || element.type() == Element.PHRASE
+ || element.type() == Element.ANCHOR
+ || element.type() == Element.CHUNK
+ || element.type() == Element.LIST
+ || element.type() == Element.JPEG
+ || element.type() == Element.IMGRAW
+ || element.type() == Element.IMGTEMPLATE) {
+ content.add(element);
+ }
+ else {
+ throw new BadElementException("You can't add an element of type " + element.getClass().getName() + " to a SimpleCell.");
+ }
+ }
+
+ /**
+ * Creates a Cell with these attributes.
+ * @param rowAttributes
+ * @return a cell based on these attributes.
+ * @throws BadElementException
+ */
+ public Cell createCell(SimpleCell rowAttributes) throws BadElementException {
+ Cell cell = new Cell();
+ cell.cloneNonPositionParameters(rowAttributes);
+ cell.softCloneNonPositionParameters(this);
+ cell.setColspan(colspan);
+ cell.setHorizontalAlignment(horizontalAlignment);
+ cell.setVerticalAlignment(verticalAlignment);
+ cell.setUseAscender(useAscender);
+ cell.setUseBorderPadding(useBorderPadding);
+ cell.setUseDescender(useDescender);
+ Element element;
+ for (Iterator i = content.iterator(); i.hasNext(); ) {
+ element = (Element)i.next();
+ cell.addElement(element);
+ }
+ return cell;
+ }
+
+ /**
+ * Creates a PdfPCell with these attributes.
+ * @param rowAttributes
+ * @return a PdfPCell based on these attributes.
+ */
+ public PdfPCell createPdfPCell(SimpleCell rowAttributes) {
+ PdfPCell cell = new PdfPCell();
+ cell.setBorder(NO_BORDER);
+ SimpleCell tmp = new SimpleCell(CELL);
+ tmp.setSpacing_left(spacing_left);
+ tmp.setSpacing_right(spacing_right);
+ tmp.setSpacing_top(spacing_top);
+ tmp.setSpacing_bottom(spacing_bottom);
+ tmp.cloneNonPositionParameters(rowAttributes);
+ tmp.softCloneNonPositionParameters(this);
+ cell.setCellEvent(tmp);
+ cell.setHorizontalAlignment(rowAttributes.horizontalAlignment);
+ cell.setVerticalAlignment(rowAttributes.verticalAlignment);
+ cell.setUseAscender(rowAttributes.useAscender);
+ cell.setUseBorderPadding(rowAttributes.useBorderPadding);
+ cell.setUseDescender(rowAttributes.useDescender);
+ cell.setColspan(colspan);
+ if (horizontalAlignment != Element.ALIGN_UNDEFINED)
+ cell.setHorizontalAlignment(horizontalAlignment);
+ if (verticalAlignment != Element.ALIGN_UNDEFINED)
+ cell.setVerticalAlignment(verticalAlignment);
+ if (useAscender)
+ cell.setUseAscender(useAscender);
+ if (useBorderPadding)
+ cell.setUseBorderPadding(useBorderPadding);
+ if (useDescender)
+ cell.setUseDescender(useDescender);
+ float p;
+ float sp_left = spacing_left;
+ if (Float.isNaN(sp_left)) sp_left = 0f;
+ float sp_right = spacing_right;
+ if (Float.isNaN(sp_right)) sp_right = 0f;
+ float sp_top = spacing_top;
+ if (Float.isNaN(sp_top)) sp_top = 0f;
+ float sp_bottom = spacing_bottom;
+ if (Float.isNaN(sp_bottom)) sp_bottom = 0f;
+ p = padding_left;
+ if (Float.isNaN(p)) p = 0f;
+ cell.setPaddingLeft(p + sp_left);
+ p = padding_right;
+ if (Float.isNaN(p)) p = 0f;
+ cell.setPaddingRight(p + sp_right);
+ p = padding_top;
+ if (Float.isNaN(p)) p = 0f;
+ cell.setPaddingTop(p + sp_top);
+ p = padding_bottom;
+ if (Float.isNaN(p)) p = 0f;
+ cell.setPaddingBottom(p + sp_bottom);
+ Element element;
+ for (Iterator i = content.iterator(); i.hasNext(); ) {
+ element = (Element)i.next();
+ cell.addElement(element);
+ }
+ return cell;
+ }
+
+ /**
+ * @param rectangle
+ * @param spacing
+ * @return a rectangle
+ */
+ public static SimpleCell getDimensionlessInstance(Rectangle rectangle, float spacing) {
+ SimpleCell event = new SimpleCell(CELL);
+ event.cloneNonPositionParameters(rectangle);
+ event.setSpacing(spacing * 2f);
+ return event;
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfPCellEvent#cellLayout(com.lowagie.text.pdf.PdfPCell, com.lowagie.text.Rectangle, com.lowagie.text.pdf.PdfContentByte[])
+ */
+ public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
+ float sp_left = spacing_left;
+ if (Float.isNaN(sp_left)) sp_left = 0f;
+ float sp_right = spacing_right;
+ if (Float.isNaN(sp_right)) sp_right = 0f;
+ float sp_top = spacing_top;
+ if (Float.isNaN(sp_top)) sp_top = 0f;
+ float sp_bottom = spacing_bottom;
+ if (Float.isNaN(sp_bottom)) sp_bottom = 0f;
+ Rectangle rect = new Rectangle(position.left(sp_left), position.bottom(sp_bottom), position.right(sp_right), position.top(sp_top));
+ rect.cloneNonPositionParameters(this);
+ canvases[PdfPTable.BACKGROUNDCANVAS].rectangle(rect);
+ rect.setBackgroundColor(null);
+ canvases[PdfPTable.LINECANVAS].rectangle(rect);
+ }
+
+ /** Sets the padding parameters if they are undefined.
+ * @param padding*/
+ public void setPadding(float padding) {
+ if (Float.isNaN(padding_right)) {
+ setPadding_right(padding);
+ }
+ if (Float.isNaN(padding_left)) {
+ setPadding_left(padding);
+ }
+ if (Float.isNaN(padding_top)) {
+ setPadding_top(padding);
+ }
+ if (Float.isNaN(padding_bottom)) {
+ setPadding_bottom(padding);
+ }
+ }
+
+ /**
+ * @return Returns the colspan.
+ */
+ public int getColspan() {
+ return colspan;
+ }
+ /**
+ * @param colspan The colspan to set.
+ */
+ public void setColspan(int colspan) {
+ if (colspan > 0) this.colspan = colspan;
+ }
+ /**
+ * @return Returns the padding_bottom.
+ */
+ public float getPadding_bottom() {
+ return padding_bottom;
+ }
+ /**
+ * @param padding_bottom The padding_bottom to set.
+ */
+ public void setPadding_bottom(float padding_bottom) {
+ this.padding_bottom = padding_bottom;
+ }
+ /**
+ * @return Returns the padding_left.
+ */
+ public float getPadding_left() {
+ return padding_left;
+ }
+ /**
+ * @param padding_left The padding_left to set.
+ */
+ public void setPadding_left(float padding_left) {
+ this.padding_left = padding_left;
+ }
+ /**
+ * @return Returns the padding_right.
+ */
+ public float getPadding_right() {
+ return padding_right;
+ }
+ /**
+ * @param padding_right The padding_right to set.
+ */
+ public void setPadding_right(float padding_right) {
+ this.padding_right = padding_right;
+ }
+ /**
+ * @return Returns the padding_top.
+ */
+ public float getPadding_top() {
+ return padding_top;
+ }
+ /**
+ * @param padding_top The padding_top to set.
+ */
+ public void setPadding_top(float padding_top) {
+ this.padding_top = padding_top;
+ }
+ /**
+ * @return Returns the spacing.
+ */
+ public float getSpacing_left() {
+ return spacing_left;
+ }
+ /**
+ * @return Returns the spacing.
+ */
+ public float getSpacing_right() {
+ return spacing_right;
+ }
+ /**
+ * @return Returns the spacing.
+ */
+ public float getSpacing_top() {
+ return spacing_top;
+ }
+ /**
+ * @return Returns the spacing.
+ */
+ public float getSpacing_bottom() {
+ return spacing_bottom;
+ }
+
+ /**
+ * @param spacing The spacing to set.
+ */
+ public void setSpacing(float spacing) {
+ this.spacing_left = spacing;
+ this.spacing_right = spacing;
+ this.spacing_top = spacing;
+ this.spacing_bottom = spacing;
+ }
+
+ /**
+ * @param spacing The spacing to set.
+ */
+ public void setSpacing_left(float spacing) {
+ this.spacing_left = spacing;
+ }
+
+ /**
+ * @param spacing The spacing to set.
+ */
+ public void setSpacing_right(float spacing) {
+ this.spacing_right = spacing;
+ }
+
+ /**
+ * @param spacing The spacing to set.
+ */
+ public void setSpacing_top(float spacing) {
+ this.spacing_top = spacing;
+ }
+
+ /**
+ * @param spacing The spacing to set.
+ */
+ public void setSpacing_bottom(float spacing) {
+ this.spacing_bottom = spacing;
+ }
+
+ /**
+ * @return Returns the cellgroup.
+ */
+ public boolean isCellgroup() {
+ return cellgroup;
+ }
+ /**
+ * @param cellgroup The cellgroup to set.
+ */
+ public void setCellgroup(boolean cellgroup) {
+ this.cellgroup = cellgroup;
+ }
+ /**
+ * @return Returns the horizontal alignment.
+ */
+ public int getHorizontalAlignment() {
+ return horizontalAlignment;
+ }
+ /**
+ * @param horizontalAlignment The horizontalAlignment to set.
+ */
+ public void setHorizontalAlignment(int horizontalAlignment) {
+ this.horizontalAlignment = horizontalAlignment;
+ }
+ /**
+ * @return Returns the vertical alignment.
+ */
+ public int getVerticalAlignment() {
+ return verticalAlignment;
+ }
+ /**
+ * @param verticalAlignment The verticalAligment to set.
+ */
+ public void setVerticalAlignment(int verticalAlignment) {
+ this.verticalAlignment = verticalAlignment;
+ }
+ /**
+ * @return Returns the width.
+ */
+ public float getWidth() {
+ return width;
+ }
+ /**
+ * @param width The width to set.
+ */
+ public void setWidth(float width) {
+ this.width = width;
+ }
+ /**
+ * @return Returns the widthpercentage.
+ */
+ public float getWidthpercentage() {
+ return widthpercentage;
+ }
+ /**
+ * @param widthpercentage The widthpercentage to set.
+ */
+ public void setWidthpercentage(float widthpercentage) {
+ this.widthpercentage = widthpercentage;
+ }
+ /**
+ * @return Returns the useAscender.
+ */
+ public boolean isUseAscender() {
+ return useAscender;
+ }
+ /**
+ * @param useAscender The useAscender to set.
+ */
+ public void setUseAscender(boolean useAscender) {
+ this.useAscender = useAscender;
+ }
+ /**
+ * @return Returns the useBorderPadding.
+ */
+ public boolean isUseBorderPadding() {
+ return useBorderPadding;
+ }
+ /**
+ * @param useBorderPadding The useBorderPadding to set.
+ */
+ public void setUseBorderPadding(boolean useBorderPadding) {
+ this.useBorderPadding = useBorderPadding;
+ }
+ /**
+ * @return Returns the useDescender.
+ */
+ public boolean isUseDescender() {
+ return useDescender;
+ }
+ /**
+ * @param useDescender The useDescender to set.
+ */
+ public void setUseDescender(boolean useDescender) {
+ this.useDescender = useDescender;
+ }
+
+ /**
+ * @return Returns the content.
+ */
+ ArrayList getContent() {
+ return content;
+ }
+
+ /**
+ * @see com.lowagie.text.TextElementArray#add(java.lang.Object)
+ */
+ public boolean add(Object o) {
+ try {
+ addElement((Element)o);
+ return true;
+ }
+ catch(ClassCastException e) {
+ return false;
+ }
+ catch(BadElementException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+ /**
+ * @see com.lowagie.text.Element#type()
+ */
+ public int type() {
+ return Element.CELL;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/SimpleTable.java b/src/main/java/com/lowagie/text/SimpleTable.java
new file mode 100644
index 0000000..fac3e7d
--- /dev/null
+++ b/src/main/java/com/lowagie/text/SimpleTable.java
@@ -0,0 +1,357 @@
+/*
+ * $Id: SimpleTable.java,v 1.4 2005/12/09 12:49:34 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999-2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 LIBRARY GENERAL PUBLIC LICENSE for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfPTable;
+import com.lowagie.text.pdf.PdfPTableEvent;
+
+/**
+ * Rectangle that can be used for Cells.
+ * This Rectangle is padded and knows how to draw itself in a PdfPTable or PdfPcellEvent.
+ */
+public class SimpleTable extends Rectangle implements PdfPTableEvent, Element, TextElementArray {
+
+ /** the content of a Table. */
+ private ArrayList content = new ArrayList();
+ /** the width of the Table. */
+ private float width = 0f;
+ /** the widthpercentage of the Table. */
+ private float widthpercentage = 0f;
+ /** the spacing of the Cells. */
+ private float cellspacing;
+ /** the padding of the Cells. */
+ private float cellpadding;
+ /** the alignment of the table. */
+ private int alignment;
+
+ /**
+ * A RectangleCell is always constructed without any dimensions.
+ * Dimensions are defined after creation.
+ */
+ public SimpleTable() {
+ super(0f, 0f, 0f, 0f);
+ setBorder(BOX);
+ setBorderWidth(2f);
+ }
+
+ /**
+ * Adds content to this object.
+ * @param element
+ * @throws BadElementException
+ */
+ public void addElement(SimpleCell element) throws BadElementException {
+ if(!element.isCellgroup()) {
+ throw new BadElementException("You can't add cells to a table directly, add them to a row first.");
+ }
+ content.add(element);
+ }
+
+ /**
+ * Creates a Table object based on this TableAttributes object.
+ * @return a com.lowagie.text.Table object
+ * @throws BadElementException
+ */
+ public Table createTable() throws BadElementException {
+ if (content.size() == 0) throw new BadElementException("Trying to create a table without rows.");
+ SimpleCell row = (SimpleCell)content.get(0);
+ SimpleCell cell;
+ int columns = 0;
+ for (Iterator i = row.getContent().iterator(); i.hasNext(); ) {
+ cell = (SimpleCell)i.next();
+ columns += cell.getColspan();
+ }
+ float[] widths = new float[columns];
+ float[] widthpercentages = new float[columns];
+ Table table = new Table(columns);
+ table.setAlignment(alignment);
+ table.setSpacing(cellspacing);
+ table.setPadding(cellpadding);
+ table.cloneNonPositionParameters(this);
+ int pos;
+ for (Iterator rows = content.iterator(); rows.hasNext(); ) {
+ row = (SimpleCell)rows.next();
+ pos = 0;
+ for (Iterator cells = row.getContent().iterator(); cells.hasNext(); ) {
+ cell = (SimpleCell)cells.next();
+ table.addCell(cell.createCell(row));
+ if (cell.getColspan() == 1) {
+ if (cell.getWidth() > 0) widths[pos] = cell.getWidth();
+ if (cell.getWidthpercentage() > 0) widthpercentages[pos] = cell.getWidthpercentage();
+ }
+ pos += cell.getColspan();
+ }
+ }
+ float sumWidths = 0f;
+ for(int i = 0; i < columns; i++) {
+ if (widths[i] == 0) {
+ sumWidths = 0;
+ break;
+ }
+ sumWidths += widths[i];
+ }
+ if (sumWidths > 0) {
+ table.setAbsWidth(String.valueOf(sumWidths));
+ table.setWidths(widths);
+ }
+ else {
+ for(int i = 0; i < columns; i++) {
+ if (widthpercentages[i] == 0) {
+ sumWidths = 0;
+ break;
+ }
+ sumWidths += widthpercentages[i];
+ }
+ if (sumWidths > 0) {
+ table.setWidths(widthpercentages);
+ }
+ }
+ if (width > 0) {
+ table.setAbsWidth(String.valueOf(width));
+ }
+ if (widthpercentage > 0) {
+ table.setWidth(widthpercentage);
+ }
+ return table;
+ }
+
+ /**
+ * Creates a PdfPTable object based on this TableAttributes object.
+ * @return a com.lowagie.text.pdf.PdfPTable object
+ * @throws DocumentException
+ */
+ public PdfPTable createPdfPTable() throws DocumentException {
+ if (content.size() == 0) throw new BadElementException("Trying to create a table without rows.");
+ SimpleCell row = (SimpleCell)content.get(0);
+ SimpleCell cell;
+ int columns = 0;
+ for (Iterator i = row.getContent().iterator(); i.hasNext(); ) {
+ cell = (SimpleCell)i.next();
+ columns += cell.getColspan();
+ }
+ float[] widths = new float[columns];
+ float[] widthpercentages = new float[columns];
+ PdfPTable table = new PdfPTable(columns);
+ table.setTableEvent(this);
+ table.setHorizontalAlignment(alignment);
+ int pos;
+ for (Iterator rows = content.iterator(); rows.hasNext(); ) {
+ row = (SimpleCell)rows.next();
+ pos = 0;
+ for (Iterator cells = row.getContent().iterator(); cells.hasNext(); ) {
+ cell = (SimpleCell)cells.next();
+ if (Float.isNaN(cell.getSpacing_left())) {
+ cell.setSpacing_left(cellspacing / 2f);
+ }
+ if (Float.isNaN(cell.getSpacing_right())) {
+ cell.setSpacing_right(cellspacing / 2f);
+ }
+ if (Float.isNaN(cell.getSpacing_top())) {
+ cell.setSpacing_top(cellspacing / 2f);
+ }
+ if (Float.isNaN(cell.getSpacing_bottom())) {
+ cell.setSpacing_bottom(cellspacing / 2f);
+ }
+ cell.setPadding(cellpadding);
+ table.addCell(cell.createPdfPCell(row));
+ if (cell.getColspan() == 1) {
+ if (cell.getWidth() > 0) widths[pos] = cell.getWidth();
+ if (cell.getWidthpercentage() > 0) widthpercentages[pos] = cell.getWidthpercentage();
+ }
+ pos += cell.getColspan();
+ }
+ }
+ float sumWidths = 0f;
+ for(int i = 0; i < columns; i++) {
+ if (widths[i] == 0) {
+ sumWidths = 0;
+ break;
+ }
+ sumWidths += widths[i];
+ }
+ if (sumWidths > 0) {
+ table.setTotalWidth(sumWidths);
+ table.setWidths(widths);
+ }
+ else {
+ for(int i = 0; i < columns; i++) {
+ if (widthpercentages[i] == 0) {
+ sumWidths = 0;
+ break;
+ }
+ sumWidths += widthpercentages[i];
+ }
+ if (sumWidths > 0) {
+ table.setWidths(widthpercentages);
+ }
+ }
+ if (width > 0) {
+ table.setTotalWidth(width);
+ }
+ if (widthpercentage > 0) {
+ table.setWidthPercentage(widthpercentage);
+ }
+ return table;
+ }
+
+ /**
+ * @param rectangle
+ * @param spacing
+ * @return a rectangle
+ */
+ public static SimpleTable getDimensionlessInstance(Rectangle rectangle, float spacing) {
+ SimpleTable event = new SimpleTable();
+ event.cloneNonPositionParameters(rectangle);
+ event.setCellspacing(spacing);
+ return event;
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfPTableEvent#tableLayout(com.lowagie.text.pdf.PdfPTable, float[][], float[], int, int, com.lowagie.text.pdf.PdfContentByte[])
+ */
+ public void tableLayout(PdfPTable table, float[][] widths, float[] heights, int headerRows, int rowStart, PdfContentByte[] canvases) {
+ float[] width = widths[0];
+ Rectangle rect = new Rectangle(width[0], heights[heights.length - 1], width[width.length - 1], heights[0]);
+ rect.cloneNonPositionParameters(this);
+ int bd = rect.border();
+ rect.setBorder(Rectangle.NO_BORDER);
+ canvases[PdfPTable.BACKGROUNDCANVAS].rectangle(rect);
+ rect.setBorder(bd);
+ rect.setBackgroundColor(null);
+ canvases[PdfPTable.LINECANVAS].rectangle(rect);
+ }
+
+ /**
+ * @return Returns the cellpadding.
+ */
+ public float getCellpadding() {
+ return cellpadding;
+ }
+ /**
+ * @param cellpadding The cellpadding to set.
+ */
+ public void setCellpadding(float cellpadding) {
+ this.cellpadding = cellpadding;
+ }
+ /**
+ * @return Returns the cellspacing.
+ */
+ public float getCellspacing() {
+ return cellspacing;
+ }
+ /**
+ * @param cellspacing The cellspacing to set.
+ */
+ public void setCellspacing(float cellspacing) {
+ this.cellspacing = cellspacing;
+ }
+
+ /**
+ * @return Returns the alignment.
+ */
+ public int getAlignment() {
+ return alignment;
+ }
+ /**
+ * @param alignment The alignment to set.
+ */
+ public void setAlignment(int alignment) {
+ this.alignment = alignment;
+ }
+ /**
+ * @return Returns the width.
+ */
+ public float getWidth() {
+ return width;
+ }
+ /**
+ * @param width The width to set.
+ */
+ public void setWidth(float width) {
+ this.width = width;
+ }
+ /**
+ * @return Returns the widthpercentage.
+ */
+ public float getWidthpercentage() {
+ return widthpercentage;
+ }
+ /**
+ * @param widthpercentage The widthpercentage to set.
+ */
+ public void setWidthpercentage(float widthpercentage) {
+ this.widthpercentage = widthpercentage;
+ }
+ /**
+ * @see com.lowagie.text.Element#type()
+ */
+ public int type() {
+ return Element.TABLE;
+ }
+
+ /**
+ * @see com.lowagie.text.TextElementArray#add(java.lang.Object)
+ */
+ public boolean add(Object o) {
+ try {
+ addElement((SimpleCell)o);
+ return true;
+ }
+ catch(ClassCastException e) {
+ return false;
+ }
+ catch(BadElementException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/SpecialSymbol.java b/src/main/java/com/lowagie/text/SpecialSymbol.java
new file mode 100644
index 0000000..ec12583
--- /dev/null
+++ b/src/main/java/com/lowagie/text/SpecialSymbol.java
@@ -0,0 +1,214 @@
+/*
+ * $Id: SpecialSymbol.java,v 1.1 2004/12/23 09:14:47 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+/**
+ * This class contains the symbols that correspond with special symbols.
+ * Phrase
with Phrase.getInstance using a String
,
+ * this String
can contain special Symbols. These are characters with an int value
+ * between 913 and 937 (except 930) and between 945 and 969. With this class the value of the
+ * corresponding character of the Font Symbol, can be retrieved.
+ *
+ * @see Phrase
+ *
+ * @author Bruno Lowagie
+ * @author Evelyne De Cordier
+ */
+
+public class SpecialSymbol {
+
+/**
+ * Returns the first occurrence of a special symbol in a String
.
+ *
+ * @param string a String
+ * @return an index of -1 if no special symbol was found
+ */
+
+ public static int index(String string) {
+ int length = string.length();
+ for (int i = 0; i < length; i++) {
+ if (getCorrespondingSymbol(string.charAt(i)) != ' ') {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+/**
+ * Gets a chunk with a symbol character.
+ * @param c a character that has to be changed into a symbol
+ * @param font Font if there is no SYMBOL character corresponding with c
+ * @return a SYMBOL version of a character
+ */
+
+ public static Chunk get(char c, Font font) {
+ char greek = SpecialSymbol.getCorrespondingSymbol(c);
+ if (greek == ' ') {
+ return new Chunk(String.valueOf(c), font);
+ }
+ Font symbol = new Font(Font.SYMBOL, font.size(), font.style(), font.color());
+ String s = String.valueOf(greek);
+ return new Chunk(s, symbol);
+ }
+
+/**
+ * Looks for the corresponding symbol in the font Symbol.
+ *
+ * @param c the original ASCII-char
+ * @return the corresponding symbol in font Symbol
+ */
+
+ public static char getCorrespondingSymbol(char c) {
+ switch(c) {
+ case 913:
+ return 'A'; // ALFA
+ case 914:
+ return 'B'; // BETA
+ case 915:
+ return 'G'; // GAMMA
+ case 916:
+ return 'D'; // DELTA
+ case 917:
+ return 'E'; // EPSILON
+ case 918:
+ return 'Z'; // ZETA
+ case 919:
+ return 'H'; // ETA
+ case 920:
+ return 'Q'; // THETA
+ case 921:
+ return 'I'; // IOTA
+ case 922:
+ return 'K'; // KAPPA
+ case 923:
+ return 'L'; // LAMBDA
+ case 924:
+ return 'M'; // MU
+ case 925:
+ return 'N'; // NU
+ case 926:
+ return 'X'; // XI
+ case 927:
+ return 'O'; // OMICRON
+ case 928:
+ return 'P'; // PI
+ case 929:
+ return 'R'; // RHO
+ case 931:
+ return 'S'; // SIGMA
+ case 932:
+ return 'T'; // TAU
+ case 933:
+ return 'U'; // UPSILON
+ case 934:
+ return 'J'; // PHI
+ case 935:
+ return 'C'; // CHI
+ case 936:
+ return 'Y'; // PSI
+ case 937:
+ return 'W'; // OMEGA
+ case 945:
+ return 'a'; // alfa
+ case 946:
+ return 'b'; // beta
+ case 947:
+ return 'g'; // gamma
+ case 948:
+ return 'd'; // delta
+ case 949:
+ return 'e'; // epsilon
+ case 950:
+ return 'z'; // zeta
+ case 951:
+ return 'h'; // eta
+ case 952:
+ return 'q'; // theta
+ case 953:
+ return 'i'; // iota
+ case 954:
+ return 'k'; // kappa
+ case 955:
+ return 'l'; // lambda
+ case 956:
+ return 'm'; // mu
+ case 957:
+ return 'n'; // nu
+ case 958:
+ return 'x'; // xi
+ case 959:
+ return 'o'; // omicron
+ case 960:
+ return 'p'; // pi
+ case 961:
+ return 'r'; // rho
+ case 962:
+ return 's'; // sigma
+ case 963:
+ return 's'; // sigma
+ case 964:
+ return 't'; // tau
+ case 965:
+ return 'u'; // upsilon
+ case 966:
+ return 'j'; // phi
+ case 967:
+ return 'c'; // chi
+ case 968:
+ return 'y'; // psi
+ case 969:
+ return 'w'; // omega
+ default:
+ return ' ';
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/SplitCharacter.java b/src/main/java/com/lowagie/text/SplitCharacter.java
new file mode 100644
index 0000000..ea1e802
--- /dev/null
+++ b/src/main/java/com/lowagie/text/SplitCharacter.java
@@ -0,0 +1,98 @@
+/*
+ * $Id: SplitCharacter.java,v 1.44 2005/05/04 14:31:17 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import com.lowagie.text.pdf.PdfChunk;
+
+/** Interface for customizing the split character.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+
+public interface SplitCharacter {
+
+ /**
+ * Returns true
if the character can split a line. The splitting implementation
+ * is free to look ahead or look behind characters to make a decision.
+ *
+ * public boolean isSplitCharacter(int start, int current, int end, char[] cc, PdfChunk[] ck) {
+ * char c;
+ * if (ck == null)
+ * c = cc[current];
+ * else
+ * c = ck[Math.min(current, ck.length - 1)].getUnicodeEquivalent(cc[current]);
+ * if (c <= ' ' || c == '-') {
+ * return true;
+ * }
+ * if (c < 0x2e80)
+ * return false;
+ * return ((c >= 0x2e80 && c < 0xd7a0)
+ * || (c >= 0xf900 && c < 0xfb00)
+ * || (c >= 0xfe30 && c < 0xfe50)
+ * || (c >= 0xff61 && c < 0xffa0));
+ * }
+ *
+ * @param start the lower limit of cc
inclusive
+ * @param current the pointer to the character in cc
+ * @param end the upper limit of cc
exclusive
+ * @param cc an array of characters at least end
sized
+ * @param ck an array of PdfChunk
. The main use is to be able to call
+ * {@link PdfChunk#getUnicodeEquivalent(char)}. It may be null
+ * or shorter than end
. If null
no convertion takes place.
+ * If shorter than end
the last element is used
+ * @return true
if the character(s) can split a line
+ */
+
+ public boolean isSplitCharacter(int start, int current, int end, char cc[], PdfChunk ck[]);
+}
diff --git a/src/main/java/com/lowagie/text/StringCompare.java b/src/main/java/com/lowagie/text/StringCompare.java
new file mode 100644
index 0000000..9c7bde0
--- /dev/null
+++ b/src/main/java/com/lowagie/text/StringCompare.java
@@ -0,0 +1,77 @@
+/*
+ * $Id: StringCompare.java,v 1.54 2005/05/04 14:31:10 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * Copyright (c) 2000 Volker Richert
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.util.Comparator;
+
+/**
+ * This class was used in the 1.1-version of iText (by Volker Richert).
+ * Paulo Soares suggested I should add it to the original library, so
+ * that in the future it would be easier to port it to the JDK1.1.x.
+ */
+
+public class StringCompare implements Comparator {
+
+/**
+ * Compares 2 objects.
+ *
+ * @param o1 a first object
+ * @param o2 a second object
+ * @return a value
+ * @throws ClassCastException if the objects aren't Strings
+ */
+
+ public int compare(Object o1, Object o2) {
+ return ((String)o1).compareTo((String)o2);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/Table.java b/src/main/java/com/lowagie/text/Table.java
new file mode 100644
index 0000000..da7e4d8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Table.java
@@ -0,0 +1,1916 @@
+/*
+ * $Id: Table.java,v 1.138 2005/12/09 12:33:26 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 LIBRARY GENERAL PUBLIC LICENSE for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ *
+ * Some methods in this class were contributed by Geert Poels, Kris Jespers and
+ * Steve Ogryzek. Check the CVS repository.
+ */
+
+package com.lowagie.text;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import com.lowagie.text.markup.MarkupParser;
+import com.lowagie.text.pdf.PdfPCell;
+import com.lowagie.text.pdf.PdfPTable;
+/**
+ * A Table
is a Rectangle
that contains Cell
s,
+ * ordered in some kind of matrix.
+ * endHeaders()
.
+ * GridBagLayout
.
+ *
+ * The result of this code is a table:
+ *
+ * // Remark: You MUST know the number of columns when constructing a Table.
+ * // The number of rows is not important.
+ * Table table = new Table(3);
+ * table.setBorderWidth(1);
+ * table.setBorderColor(new Color(0, 0, 255));
+ * table.setPadding(5);
+ * table.setSpacing(5);
+ * Cell cell = new Cell("header");
+ * cell.setHeader(true);
+ * cell.setColspan(3);
+ * table.addCell(cell);
+ * table.endHeaders();
+ * cell = new Cell("example cell with colspan 1 and rowspan 2");
+ * cell.setRowspan(2);
+ * cell.setBorderColor(new Color(255, 0, 0));
+ * table.addCell(cell);
+ * table.addCell("1.1");
+ * table.addCell("2.1");
+ * table.addCell("1.2");
+ * table.addCell("2.2");
+ * table.addCell("cell test1");
+ * cell = new Cell("big cell");
+ * cell.setRowspan(2);
+ * cell.setColspan(2);
+ * table.addCell(cell);
+ * table.addCell("cell test2");
+ *
+ *
+ *
+ * @see Rectangle
+ * @see Element
+ * @see Row
+ * @see Cell
+ */
+
+public class Table extends Rectangle implements Element, MarkupAttributes {
+
+ // membervariables
+
+ // these variables contain the data of the table
+
+ /** This is the number of columns in the
+ *
+ *
+ * header
+ *
+ *
+ *
+ *
+ * example cell with colspan 1 and rowspan 2
+ *
+ *
+ * 1.1
+ *
+ *
+ * 2.1
+ *
+ *
+ *
+ *
+ * 1.2
+ *
+ *
+ * 2.2
+ *
+ *
+ *
+ *
+ * cell test1
+ *
+ *
+ * big cell
+ *
+ *
+ *
+ *
+ * cell test2
+ *
+ * Table
. */
+ private int columns;
+
+ // this is the current Position in the table
+ private Point curPosition = new Point(0, 0);
+
+ /** This is the list of Row
s. */
+ private ArrayList rows = new ArrayList();
+
+ // these variables contain the layout of the table
+
+ /** This Empty Cell contains the DEFAULT layout of each Cell added with the method addCell(String content). */
+ private Cell defaultLayout = new Cell(true);
+
+ /** This is the number of the last row of the table headers. */
+ private int lastHeaderRow = -1;
+
+ /** This is the horizontal alignment. */
+ private int alignment = Element.ALIGN_CENTER;
+
+ /** This is cellpadding. */
+ private float cellpadding;
+
+ /** This is cellspacing. */
+ private float cellspacing;
+
+ /** This is the width of the table (in percent of the available space). */
+ private float widthPercentage = 80;
+
+ // member variable added by Evelyne De Cordier
+ /** This is the width of the table (in pixels). */
+ private String absWidth = "";
+
+ /** This is an array containing the widths (in percentages) of every column. */
+ private float[] widths;
+
+ /** Boolean to track errors (some checks will be performed) */
+ boolean mDebug = false;
+
+ /** Boolean to track if a table was inserted (to avoid unnecessary computations afterwards) */
+ boolean mTableInserted = false;
+
+ /**
+ * Boolean to automatically fill empty cells before a table is rendered
+ * (takes CPU so may be set to false in case of certainty)
+ */
+ boolean mAutoFillEmptyCells = false;
+
+ /** If true this table may not be split over two pages. */
+ boolean tableFitsPage = false;
+
+ /** If true cells may not be split over two pages. */
+ boolean cellsFitPage = false;
+
+ /** This is the offset of the table. */
+ float offset = Float.NaN;
+
+ /** contains the attributes that are added to each odd (or even) row */
+ protected Hashtable alternatingRowAttributes = null;
+
+ /** if you want to generate tables the old way, set this value to false. */
+ protected boolean convert2pdfptable = false;
+
+ // constructors
+
+ /**
+ * Constructs a Table
with a certain number of columns.
+ *
+ * @param columns The number of columns in the table
+ * @throws BadElementException if the creator was called with less than 1 column
+ */
+
+ public Table(int columns) throws BadElementException {
+ this(columns, 1);
+ }
+
+ /**
+ * Constructs a Table
with a certain number of columns
+ * and a certain number of Row
s.
+ *
+ * @param columns The number of columns in the table
+ * @param rows The number of rows
+ * @throws BadElementException if the creator was called with less than 1 column
+ */
+
+ public Table(int columns, int rows) throws BadElementException {
+ // a Rectangle is create with BY DEFAULT a border with a width of 1
+ super(0, 0, 0, 0);
+ setBorder(BOX);
+ setBorderWidth(1);
+ defaultLayout.setBorder(BOX);
+
+ // a table should have at least 1 column
+ if (columns <= 0) {
+ throw new BadElementException("A table should have at least 1 column.");
+ }
+ this.columns = columns;
+
+ // a certain number of rows are created
+ for (int i = 0; i < rows; i++) {
+ this.rows.add(new Row(columns));
+ }
+ curPosition = new Point(0, 0);
+
+ // the DEFAULT widths are calculated
+ widths = new float[columns];
+ float width = 100f / columns;
+ for (int i = 0; i < columns; i++) {
+ widths[i] = width;
+ }
+ }
+
+ /**
+ * Returns a Table
that has been constructed taking in account
+ * the value of some attributes.
+ *
+ * @param attributes Some attributes
+ */
+
+ public Table(Properties attributes) {
+ // a Rectangle is create with BY DEFAULT a border with a width of 1
+ super(0, 0, 0, 0);
+ setBorder(BOX);
+ setBorderWidth(1);
+ defaultLayout.setBorder(BOX);
+
+ String value = (String)attributes.remove(ElementTags.COLUMNS);
+ if (value == null) {
+ columns = 1;
+ }
+ else {
+ columns = Integer.parseInt(value);
+ if (columns <= 0) {
+ columns = 1;
+ }
+ }
+
+ rows.add(new Row(columns));
+ curPosition.setLocation(0, curPosition.y);
+
+ if ((value = (String)attributes.remove(ElementTags.LASTHEADERROW)) != null) {
+ setLastHeaderRow(Integer.parseInt(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.ALIGN)) != null) {
+ setAlignment(value);
+ }
+ if ((value = (String)attributes.remove(ElementTags.CELLSPACING)) != null) {
+ setSpacing(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.CELLPADDING)) != null) {
+ setPadding(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.OFFSET)) != null) {
+ setOffset(Float.valueOf(value + "f").floatValue());
+ }
+ if ((value = (String)attributes.remove(ElementTags.WIDTH)) != null) {
+ if (value.endsWith("%"))
+ setWidth(Float.valueOf(value.substring(0, value.length() - 1) + "f").floatValue());
+ else
+ setAbsWidth(value);
+ }
+ widths = new float[columns];
+ for (int i = 0; i < columns; i++) {
+ widths[i] = 0;
+ }
+ if ((value = (String)attributes.remove(ElementTags.WIDTHS)) != null) {
+ StringTokenizer widthTokens = new StringTokenizer(value, ";");
+ int i = 0;
+ while (widthTokens.hasMoreTokens()) {
+ value = widthTokens.nextToken();
+ widths[i] = Float.valueOf(value + "f").floatValue();
+ i++;
+ }
+ columns = i;
+ }
+ if ((value = (String)attributes.remove(ElementTags.TABLEFITSPAGE)) != null) {
+ tableFitsPage = new Boolean(value).booleanValue();
+ }
+ if ((value = (String)attributes.remove(ElementTags.CELLSFITPAGE)) != null) {
+ cellsFitPage = new Boolean(value).booleanValue();
+ }
+ if ((value = (String)attributes.remove(ElementTags.BORDERWIDTH)) != null) {
+ setBorderWidth(Float.valueOf(value + "f").floatValue());
+ }
+ int border = 0;
+ if ((value = (String)attributes.remove(ElementTags.LEFT)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.LEFT;
+ }
+ if ((value = (String)attributes.remove(ElementTags.RIGHT)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.RIGHT;
+ }
+ if ((value = (String)attributes.remove(ElementTags.TOP)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.TOP;
+ }
+ if ((value = (String)attributes.remove(ElementTags.BOTTOM)) != null) {
+ if (new Boolean(value).booleanValue()) border |= Rectangle.BOTTOM;
+ }
+ setBorder(border);
+ String r = (String)attributes.remove(ElementTags.RED);
+ String g = (String)attributes.remove(ElementTags.GREEN);
+ String b = (String)attributes.remove(ElementTags.BLUE);
+ if (r != null || g != null || b != null) {
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+ if (r != null) red = Integer.parseInt(r);
+ if (g != null) green = Integer.parseInt(g);
+ if (b != null) blue = Integer.parseInt(b);
+ setBorderColor(new Color(red, green, blue));
+ }
+ else if ((value = attributes.getProperty(ElementTags.BORDERCOLOR)) != null) {
+ setBorderColor(MarkupParser.decodeColor(value));
+ }
+ r = (String)attributes.remove(ElementTags.BGRED);
+ g = (String)attributes.remove(ElementTags.BGGREEN);
+ b = (String)attributes.remove(ElementTags.BGBLUE);
+ if (r != null || g != null || b != null) {
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+ if (r != null) red = Integer.parseInt(r);
+ if (g != null) green = Integer.parseInt(g);
+ if (b != null) blue = Integer.parseInt(b);
+ setBackgroundColor(new Color(red, green, blue));
+ }
+ else if ((value = (String)attributes.remove(ElementTags.BACKGROUNDCOLOR)) != null) {
+ setBackgroundColor(MarkupParser.decodeColor(value));
+ }
+ if ((value = (String)attributes.remove(ElementTags.GRAYFILL)) != null) {
+ setGrayFill(Float.valueOf(value + "f").floatValue());
+ }
+ if (attributes.size() > 0) setMarkupAttributes(attributes);
+ }
+
+ // implementation of the Element-methods
+
+ /**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener an ElementListener
+ * @return true
if the element was processed successfully
+ */
+
+ public boolean process(ElementListener listener) {
+ try {
+ return listener.add(this);
+ }
+ catch(DocumentException de) {
+ return false;
+ }
+ }
+
+ /**
+ * Performs extra checks when executing table code (currently only when cells are added).
+ * @param aDebug
+ */
+ public void setDebug(boolean aDebug) {
+ mDebug = aDebug;
+ }
+
+ /**
+ * Sets the default layout of the Table to
+ * the provided Cell
+ * @param value a cell with all the defaults
+ */
+ public void setDefaultLayout(Cell value) {
+ defaultLayout = value;
+ }
+
+ /**
+ * Enables/disables automatic insertion of empty cells before table is rendered. (default = false)
+ * As some people may want to create a table, fill only a couple of the cells and don't bother with
+ * investigating which empty ones need to be added, this default behaviour may be very welcome.
+ * Disabling is recommended to increase speed. (empty cells should be added through extra code then)
+ *
+ * @param aDoAutoFill enable/disable autofill
+ */
+
+ public void setAutoFillEmptyCells(boolean aDoAutoFill) {
+ mAutoFillEmptyCells = aDoAutoFill;
+ }
+
+ /**
+ * Allows you to control when a page break occurs.
+ * Table
has to fit a page.
+ *
+ * @return true if the table may not be split
+ */
+
+ public boolean hasToFitPageTable() {
+ return tableFitsPage;
+ }
+
+ /**
+ * Checks if the cells of this Table
have to fit a page.
+ *
+ * @return true if the cells may not be split
+ */
+
+ public boolean hasToFitPageCells() {
+ return cellsFitPage;
+ }
+
+ /**
+ * Sets the offset of this table.
+ *
+ * Normally a newline is added before you add a Table object.
+ * This newline uses the current leading.
+ * If you want to control the space between the table and the previous
+ * element yourself, you have to set the offset of this table.
+ *
+ * @param offset the space between this table and the previous object.
+ */
+
+ public void setOffset(float offset) {
+ this.offset = offset;
+ }
+
+ /**
+ * Gets the offset of this table.
+ *
+ * @return the space between this table and the previous element.
+ */
+
+ public float getOffset() {
+ return offset;
+ }
+
+ /**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.TABLE;
+ }
+
+ /**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getChunks() {
+ return new ArrayList();
+ }
+
+ // methods to add content to the table
+
+ /**
+ * Adds a Cell
to the Table
at a certain row and column.
+ *
+ * @param aCell The Cell
to add
+ * @param row The row where the Cell
will be added
+ * @param column The column where the Cell
will be added
+ * @throws BadElementException
+ */
+
+ public void addCell(Cell aCell, int row, int column) throws BadElementException {
+ addCell(aCell, new Point(row,column));
+ }
+
+ /**
+ * Adds a Cell
to the Table
at a certain location.
+ *
+ * @param aCell The Cell
to add
+ * @param aLocation The location where the Cell
will be added
+ * @throws BadElementException
+ */
+
+ public void addCell(Cell aCell, Point aLocation) throws BadElementException {
+ if (aCell == null) throw new NullPointerException("addCell - cell has null-value");
+ if (aLocation == null) throw new NullPointerException("addCell - point has null-value");
+ if (aCell.isTable()) insertTable((Table)aCell.getElements().next(), aLocation);
+ if (mDebug == true) {
+ if (aLocation.x < 0) throw new BadElementException("row coordinate of location must be >= 0");
+ if ((aLocation.y <= 0) && (aLocation.y > columns)) throw new BadElementException("column coordinate of location must be >= 0 and < nr of columns");
+ if (!isValidLocation(aCell, aLocation)) throw new BadElementException("Adding a cell at the location (" + aLocation.x + "," + aLocation.y + ") with a colspan of " + aCell.colspan() + " and a rowspan of " + aCell.rowspan() + " is illegal (beyond boundaries/overlapping).");
+ }
+ if (aCell.border() == UNDEFINED) aCell.setBorder(defaultLayout.border());
+ aCell.fill();
+ placeCell(rows, aCell, aLocation);
+ setCurrentLocationToNextValidPosition(aLocation);
+ }
+
+
+ /**
+ * Adds a Cell
to the Table
.
+ *
+ * @param cell a Cell
+ */
+
+ public void addCell(Cell cell) {
+ try {
+ addCell(cell, curPosition);
+ }
+ catch(BadElementException bee) {
+ // don't add the cell
+ }
+ }
+
+ /**
+ * Adds a Cell
to the Table
.
+ * addCell(Cell cell)
.
+ * The Phrase
will be converted to a Cell
.
+ *
+ * @param content a Phrase
+ * @throws BadElementException this should never happen
+ */
+
+ public void addCell(Phrase content) throws BadElementException {
+ addCell(content, curPosition);
+ }
+
+ /**
+ * Adds a Cell
to the Table
.
+ * addCell(Cell cell, Point location)
.
+ * The Phrase
will be converted to a Cell
.
+ *
+ * @param content a Phrase
+ * @param location a Point
+ * @throws BadElementException this should never happen
+ */
+
+ public void addCell(Phrase content, Point location) throws BadElementException {
+ Cell cell = new Cell(content);
+ cell.setBorder(defaultLayout.border());
+ cell.setBorderWidth(defaultLayout.borderWidth());
+ cell.setBorderColor(defaultLayout.borderColor());
+ cell.setBackgroundColor(defaultLayout.backgroundColor());
+ cell.setHorizontalAlignment(defaultLayout.horizontalAlignment());
+ cell.setVerticalAlignment(defaultLayout.verticalAlignment());
+ cell.setColspan(defaultLayout.colspan());
+ cell.setRowspan(defaultLayout.rowspan());
+ addCell(cell, location);
+ }
+
+ /**
+ * Adds a Cell
to the Table
.
+ * addCell(Cell cell)
.
+ * The String
will be converted to a Cell
.
+ *
+ * @param content a String
+ * @throws BadElementException this should never happen
+ */
+
+ public void addCell(String content) throws BadElementException {
+ addCell(new Phrase(content), curPosition);
+ }
+
+ /**
+ * Adds a Cell
to the Table
.
+ * addCell(Cell cell, Point location)
.
+ * The String
will be converted to a Cell
.
+ *
+ * @param content a String
+ * @param location a Point
+ * @throws BadElementException this should never happen
+ */
+
+ public void addCell(String content, Point location) throws BadElementException {
+ addCell(new Phrase(content), location);
+ }
+
+ /**
+ * To put a table within the existing table at the current position
+ * generateTable will of course re-arrange the widths of the columns.
+ *
+ * @param aTable the table you want to insert
+ */
+
+ public void insertTable(Table aTable) {
+ if (aTable == null) throw new NullPointerException("insertTable - table has null-value");
+ insertTable(aTable, curPosition);
+ }
+
+ /**
+ * To put a table within the existing table at the given position
+ * generateTable will of course re-arrange the widths of the columns.
+ *
+ * @param aTable The Table
to add
+ * @param row The row where the Cell
will be added
+ * @param column The column where the Cell
will be added
+ */
+
+ public void insertTable(Table aTable, int row, int column) {
+ if (aTable == null) throw new NullPointerException("insertTable - table has null-value");
+ insertTable(aTable, new Point(row, column));
+ }
+
+ /**
+ * To put a table within the existing table at the given position
+ * generateTable will of course re-arrange the widths of the columns.
+ *
+ * @param aTable the table you want to insert
+ * @param aLocation a Point
+ */
+ public void insertTable(Table aTable, Point aLocation) {
+
+ if (aTable == null) throw new NullPointerException("insertTable - table has null-value");
+ if (aLocation == null) throw new NullPointerException("insertTable - point has null-value");
+ mTableInserted = true;
+ aTable.complete();
+ if (mDebug == true) {
+ if (aLocation.y > columns) System.err.println("insertTable -- wrong columnposition("+ aLocation.y + ") of location; max =" + columns);
+ }
+ int rowCount = aLocation.x + 1 - rows.size();
+ int i = 0;
+ if ( rowCount > 0 ) { //create new rows ?
+ for (; i < rowCount; i++) {
+ rows.add(new Row(columns));
+ }
+ }
+
+ ((Row) rows.get(aLocation.x)).setElement(aTable,aLocation.y);
+
+ setCurrentLocationToNextValidPosition(aLocation);
+ }
+
+/**
+ * Will fill empty cells with valid blank Cell
s
+ */
+
+ public void complete() {
+ if (mTableInserted == true) {
+ mergeInsertedTables(); // integrate tables in the table
+ mTableInserted = false;
+ }
+ if (mAutoFillEmptyCells == true) {
+ fillEmptyMatrixCells();
+ }
+ if (alternatingRowAttributes != null) {
+ Properties even = new Properties();
+ Properties odd = new Properties();
+ String name;
+ String[] value;
+ for (Iterator iterator = alternatingRowAttributes.keySet().iterator(); iterator.hasNext(); ) {
+ name = String.valueOf(iterator.next());
+ value = (String[])alternatingRowAttributes.get(name);
+ even.setProperty(name, value[0]);
+ odd.setProperty(name, value[1]);
+ }
+ Row row;
+ for (int i = lastHeaderRow + 1; i < rows.size(); i++) {
+ row = (Row)rows.get(i);
+ row.setMarkupAttributes(i % 2 == 0 ? even : odd);
+ }
+ }
+ }
+
+ /**
+ * Changes the border in the default layout of the Cell
s
+ * added with method addCell(String content)
.
+ *
+ * @param value the new border value
+ */
+
+ public void setDefaultCellBorder(int value) {
+ defaultLayout.setBorder(value);
+ }
+
+ /**
+ * Changes the width of the borders in the default layout of the Cell
s
+ * added with method addCell(String content)
.
+ *
+ * @param value the new width
+ */
+
+ public void setDefaultCellBorderWidth(float value) {
+ defaultLayout.setBorderWidth(value);
+ }
+
+ /**
+ * Changes the bordercolor in the default layout of the Cell
s
+ * added with method addCell(String content)
.
+ *
+ * @param color the new color
+ */
+
+ public void setDefaultCellBorderColor(Color color) {
+ defaultLayout.setBorderColor(color);
+ }
+
+ /**
+ * Changes the backgroundcolor in the default layout of the Cell
s
+ * added with method addCell(String content)
.
+ *
+ * @param color the new color
+ */
+
+ public void setDefaultCellBackgroundColor(Color color) {
+ defaultLayout.setBackgroundColor(color);
+ }
+
+ /**
+ * Changes the grayfill in the default layout of the Cell
s
+ * added with method addCell(String content)
.
+ *
+ * @param value the new value
+ */
+
+ public void setDefaultCellGrayFill(float value) {
+ if (value >= 0 && value <= 1) {
+ defaultLayout.setGrayFill(value);
+ }
+ }
+
+ /**
+ * Changes the horizontalAlignment in the default layout of the Cell
s
+ * added with method addCell(String content)
.
+ *
+ * @param value the new alignment value
+ */
+
+ public void setDefaultHorizontalAlignment(int value) {
+ defaultLayout.setHorizontalAlignment(value);
+ }
+
+ /**
+ * Changes the verticalAlignment in the default layout of the Cell
s
+ * added with method addCell(String content)
.
+ *
+ * @param value the new alignment value
+ */
+
+ public void setDefaultVerticalAlignment(int value) {
+ defaultLayout.setVerticalAlignment(value);
+ }
+
+ /**
+ * Changes the rowspan in the default layout of the Cell
s
+ * added with method addCell(String content)
.
+ *
+ * @param value the new rowspan value
+ */
+
+ public void setDefaultRowspan(int value) {
+ defaultLayout.setRowspan(value);
+ }
+
+ /**
+ * Changes the colspan in the default layout of the Cell
s
+ * added with method addCell(String content)
.
+ *
+ * @param value the new colspan value
+ */
+
+ public void setDefaultColspan(int value) {
+ defaultLayout.setColspan(value);
+ }
+
+ // methods
+
+ /**
+ * Sets the unset cell properties to be the table defaults.
+ *
+ * @param aCell The cell to set to table defaults as necessary.
+ */
+
+ private void assumeTableDefaults(Cell aCell) {
+
+ if (aCell.border() == Rectangle.UNDEFINED) {
+ aCell.setBorder(defaultLayout.border());
+ }
+ if (aCell.borderWidth() == Rectangle.UNDEFINED) {
+ aCell.setBorderWidth(defaultLayout.borderWidth());
+ }
+ if (aCell.borderColor() == null) {
+ aCell.setBorderColor(defaultLayout.borderColor());
+ }
+ if (aCell.backgroundColor() == null) {
+ aCell.setBackgroundColor(defaultLayout.backgroundColor());
+ }
+ if (aCell.horizontalAlignment() == Element.ALIGN_UNDEFINED) {
+ aCell.setHorizontalAlignment(defaultLayout.horizontalAlignment());
+ }
+ if (aCell.verticalAlignment() == Element.ALIGN_UNDEFINED) {
+ aCell.setVerticalAlignment(defaultLayout.verticalAlignment());
+ }
+ }
+
+ /**
+ * Deletes a column in this table.
+ *
+ * @param column the number of the column that has to be deleted
+ * @throws BadElementException
+ */
+
+ public void deleteColumn(int column) throws BadElementException {
+ float newWidths[] = new float[--columns];
+ for (int i = 0; i < column; i++) {
+ newWidths[i] = widths[i];
+ }
+ for (int i = column; i < columns; i++) {
+ newWidths[i] = widths[i + 1];
+ }
+ setWidths(newWidths);
+ for (int i = 0; i < columns; i++) {
+ newWidths[i] = widths[i];
+ }
+ widths = newWidths;
+ Row row;
+ int size = rows.size();
+ for (int i = 0; i < size; i++) {
+ row = (Row) rows.get(i);
+ row.deleteColumn(column);
+ rows.set(i, row);
+ }
+ if (column == columns) {
+ curPosition.setLocation(curPosition.x+1, 0);
+ }
+ }
+
+ /**
+ * Deletes a row.
+ *
+ * @param row the number of the row to delete
+ * @return boolean true
if the row was deleted; false
if not
+ */
+
+ public boolean deleteRow(int row) {
+ if (row < 0 || row >= rows.size()) {
+ return false;
+ }
+ rows.remove(row);
+ curPosition.setLocation(curPosition.x-1, curPosition.y);
+ return true;
+ }
+
+ /**
+ * Deletes all rows in this table.
+ * (contributed by dperezcar@fcc.es)
+ */
+
+ public void deleteAllRows() {
+ rows.clear();
+ rows.add(new Row(columns));
+ curPosition.setLocation(0, 0);
+ lastHeaderRow = -1;
+ }
+
+ /**
+ * Deletes the last row in this table.
+ *
+ * @return boolean true
if the row was deleted; false
if not
+ */
+
+ public boolean deleteLastRow() {
+ return deleteRow(rows.size() - 1);
+ }
+
+ /**
+ * Marks the last row of the table headers.
+ *
+ * @return the number of the last row of the table headers
+ */
+
+ public int endHeaders() {
+ /* patch sep 8 2001 Francesco De Milato */
+ lastHeaderRow = curPosition.x - 1;
+ return lastHeaderRow;
+ }
+
+ // methods to set the membervariables
+
+ /**
+ * Sets the horizontal alignment.
+ *
+ * @param value the new value
+ */
+
+ public void setLastHeaderRow(int value) {
+ lastHeaderRow = value;
+ }
+
+ /**
+ * Sets the horizontal alignment.
+ *
+ * @param value the new value
+ */
+
+ public void setAlignment(int value) {
+ alignment = value;
+ }
+
+ /**
+ * Sets the alignment of this paragraph.
+ *
+ * @param alignment the new alignment as a String
+ */
+
+ public void setAlignment(String alignment) {
+ if (ElementTags.ALIGN_LEFT.equalsIgnoreCase(alignment)) {
+ this.alignment = Element.ALIGN_LEFT;
+ return;
+ }
+ if (ElementTags.RIGHT.equalsIgnoreCase(alignment)) {
+ this.alignment = Element.ALIGN_RIGHT;
+ return;
+ }
+ this.alignment = Element.ALIGN_CENTER;
+ }
+
+ /**
+ * Sets the cellpadding.
+ *
+ * @param value the new value
+ */
+
+ public void setSpaceInsideCell(float value) {
+ cellpadding = value;
+ }
+
+ /**
+ * Sets the cellspacing.
+ *
+ * @param value the new value
+ */
+
+ public void setSpaceBetweenCells(float value) {
+ cellspacing = value;
+ }
+
+ /**
+ * Sets the cellpadding.
+ *
+ * @param value the new value
+ */
+
+ public void setPadding(float value) {
+ cellpadding = value;
+ }
+
+ /**
+ * Sets the cellspacing.
+ *
+ * @param value the new value
+ */
+
+ public void setSpacing(float value) {
+ cellspacing = value;
+ }
+
+ /**
+ * Sets the cellspacing (the meaning of cellpadding and cellspacing was inverted by mistake).
+ *
+ * @param value the new value
+ * @deprecated use setSpacing instead
+ */
+
+ public void setCellpadding(float value) {
+ cellspacing = value;
+ }
+
+ /**
+ * Sets the cellpadding (the meaning of cellpadding and cellspacing was inverted by mistake).
+ *
+ * @param value the new value
+ * @deprecated use setPadding instead
+ */
+
+ public void setCellspacing(float value) {
+ cellpadding = value;
+ }
+
+ /**
+ * Sets the width of this table (in percentage of the available space).
+ *
+ * @param width the width
+ */
+
+ public void setWidth(float width) {
+ this.widthPercentage = width;
+ }
+
+ /**
+ * Sets the width of this table (in percentage of the available space).
+ *
+ * @param width the width
+ */
+
+ public void setAbsWidth(String width) {
+ this.absWidth = width;
+ }
+
+ /**
+ * Sets the widths of the different columns (percentages).
+ *
+ * The widths will be: a width of 50% for the first column,
+ * 25% for the second and third column.
+ *
+ * @param widths an array with values
+ * @throws BadElementException
+ */
+
+ public void setWidths(float[] widths) throws BadElementException {
+ if (widths.length != columns) {
+ throw new BadElementException("Wrong number of columns.");
+ }
+
+ // The sum of all values is 100%
+ float hundredPercent = 0;
+ for (int i = 0; i < columns; i++) {
+ hundredPercent += widths[i];
+ }
+
+ // The different percentages are calculated
+ float width;
+ this.widths[columns - 1] = 100;
+ for (int i = 0; i < columns - 1; i++) {
+ width = (100.0f * widths[i]) / hundredPercent;
+ this.widths[i] = width;
+ this.widths[columns - 1] -= width;
+ }
+ }
+
+ /**
+ * Sets the widths of the different columns (percentages).
+ *
+ * float[] widths = {2, 1, 1};
+ * table.setWidths(widths)
+ *
Table
.
+ *
+ * @return the number of rows in this Table
+ */
+
+ public int size() {
+ return rows.size();
+ }
+
+ /**
+ * Gets the proportional widths of the columns in this Table
.
+ *
+ * @return the proportional widths of the columns in this Table
+ */
+
+ public float[] getProportionalWidths() {
+ return widths;
+ }
+
+ /**
+ * Gets an Iterator
of all the Row
s.
+ *
+ * @return an Iterator
+ */
+
+ public Iterator iterator() {
+ return rows.iterator();
+ }
+
+ /**
+ * Gets the horizontal alignment.
+ *
+ * @return a value
+ */
+
+ public int alignment() {
+ return alignment;
+ }
+
+ /**
+ * Gets the cellpadding.
+ *
+ * @return a value
+ */
+
+ public float cellpadding() {
+ return cellpadding;
+ }
+
+ /**
+ * Gets the cellspacing.
+ *
+ * @return a value
+ */
+
+ public float cellspacing() {
+ return cellspacing;
+ }
+
+ /**
+ * Gets the table width (a percentage).
+ *
+ * @return the table width
+ */
+
+ public float widthPercentage() {
+ return widthPercentage;
+ }
+
+ /**
+ * Gets the table width (in pixels).
+ *
+ * @return the table width
+ */
+
+ public String absWidth() {
+ return absWidth;
+ }
+
+ /**
+ * Gets the first number of the row that doesn't contain headers.
+ *
+ * @return a rownumber
+ */
+
+ public int firstDataRow() {
+ return lastHeaderRow + 1;
+ }
+
+ /**
+ * Gets the last number of the rows that contain headers.
+ *
+ * @return a rownumber
+ */
+ public int lastHeaderRow() {
+ return this.lastHeaderRow;
+ }
+
+ /**
+ * Gets the dimension of this table
+ *
+ * @return dimension
+ */
+
+ public Dimension getDimension() {
+ return new Dimension(columns, rows.size());
+ }
+
+ /**
+ * returns the element at the position row, column
+ * (Cast to Cell or Table)
+ *
+ * @param row
+ * @param column
+ * @return dimension
+ */
+
+ public Object getElement(int row, int column) {
+ return ((Row) rows.get(row)).getCell(column);
+ }
+
+ /**
+ * Integrates all added tables and recalculates column widths.
+ */
+
+ private void mergeInsertedTables() {
+ int i=0, j=0;
+ float [] lNewWidths = null;
+ int [] lDummyWidths = new int[columns]; // to keep track in how many new cols this one will be split
+ float[][] lDummyColumnWidths = new float[columns][]; // bugfix Tony Copping
+ int [] lDummyHeights = new int[rows.size()]; // to keep track in how many new rows this one will be split
+ ArrayList newRows = null;
+ boolean isTable=false;
+ int lTotalRows = 0, lTotalColumns = 0;
+ int lNewMaxRows = 0, lNewMaxColumns = 0;
+
+ Table lDummyTable = null;
+
+ // first we'll add new columns when needed
+ // check one column at a time, find maximum needed nr of cols
+ // Search internal tables and find one with max columns
+ for (j=0; j < columns; j++) {
+ lNewMaxColumns = 1; // value to hold in how many columns the current one will be split
+ float [] tmpWidths = null;
+ for (i=0; i < rows.size(); i++) {
+ if ( Table.class.isInstance(((Row) rows.get(i)).getCell(j)) ) {
+ isTable=true;
+ lDummyTable = ((Table) ((Row) rows.get(i)).getCell(j));
+ if( tmpWidths == null) {
+ tmpWidths = lDummyTable.widths;
+ lNewMaxColumns=tmpWidths.length;
+ }
+ else {
+ int cols = lDummyTable.getDimension().width;
+ float [] tmpWidthsN = new float[ cols * tmpWidths.length];
+ float tpW=0, btW=0, totW=0;
+ int tpI=0, btI=0, totI=0;
+ tpW+=tmpWidths[0];
+ btW+=lDummyTable.widths[0];
+ while( tpICell
'fits' the table.
+ *
+ *
+ * @param aCell the cell that has to be checked
+ * @param aLocation the location where the cell has to be placed
+ * @return true if the location was valid
+ */
+ private boolean isValidLocation(Cell aCell, Point aLocation) {
+ // rowspan not beyond last column
+ if ( aLocation.x < rows.size() ) // if false : new location is already at new, not-yet-created area so no check
+ {
+ if ((aLocation.y + aCell.colspan()) > columns) {
+ return false;
+ }
+
+ int difx = ((rows.size() - aLocation.x) > aCell.rowspan()) ? aCell.rowspan() : rows.size() - aLocation.x;
+ int dify = ((columns - aLocation.y) > aCell.colspan()) ? aCell.colspan() : columns - aLocation.y;
+ // no other content at cells targetted by rowspan/colspan
+ for (int i=aLocation.x; i < (aLocation.x + difx); i++) {
+ for (int j=aLocation.y; j < (aLocation.y + dify); j++) {
+ if ( ((Row) rows.get(i)).isReserved(j) == true ) {
+ return false;
+ }
+ }
+ }
+ }
+ else {
+ if ((aLocation.y + aCell.colspan()) > columns) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Inserts a Cell in a cell-array and reserves cells defined by row-/colspan.
+ *
+ * @param someRows some rows
+ * @param aCell the cell that has to be inserted
+ * @param aPosition the position where the cell has to be placed
+ */
+
+ private void placeCell(ArrayList someRows, Cell aCell, Point aPosition) {
+ int i;
+ Row row = null;
+ int lColumns = ((Row) someRows.get(0)).columns();
+ int rowCount = aPosition.x + aCell.rowspan() - someRows.size();
+ assumeTableDefaults(aCell);
+ if ( (aPosition.x + aCell.rowspan()) > someRows.size() ) //create new rows ?
+ {
+ for (i = 0; i < rowCount; i++) {
+ row = new Row(lColumns);
+ someRows.add(row);
+ }
+ }
+
+ // reserve cell in rows below
+ for (i = aPosition.x + 1; i < (aPosition.x + aCell.rowspan()); i++) {
+ if ( !((Row) someRows.get(i)).reserve(aPosition.y, aCell.colspan())) {
+
+ // should be impossible to come here :-)
+ throw new RuntimeException("addCell - error in reserve");
+ }
+ }
+ row = (Row) someRows.get(aPosition.x);
+ row.addElement(aCell, aPosition.y);
+
+ }
+
+ /**
+ * Gives you the posibility to add columns.
+ *
+ * @param aColumns the number of columns to add
+ */
+
+ public void addColumns(int aColumns) {
+ ArrayList newRows = new ArrayList(rows.size());
+
+ int newColumns = columns + aColumns;
+ Row row;
+ for (int i = 0; i < rows.size(); i++) {
+ row = new Row(newColumns);
+ for (int j = 0; j < columns; j++) {
+ row.setElement(((Row) rows.get(i)).getCell(j) ,j);
+ }
+ for (int j = columns; j < newColumns && i < curPosition.x; j++) {
+ row.setElement(defaultLayout, j);
+ }
+ newRows.add(row);
+ }
+
+ // applied 1 column-fix; last column needs to have a width of 0
+ float [] newWidths = new float[newColumns];
+ for (int j = 0; j < columns; j++) {
+ newWidths[j] = widths[j];
+ }
+ for (int j = columns; j < newColumns ; j++) {
+ newWidths[j] = 0;
+ }
+ columns = newColumns;
+ widths = newWidths;
+ rows = newRows;
+ }
+
+ /**
+ * Gets an array with the positions of the borders between every column.
+ * UnsupportedOperationException
.
+ * @return NA
+ */
+ public float top() {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException
.
+ * @return NA
+ */
+ public float bottom() {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException
.
+ * @return NA
+ */
+ public float left() {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException
.
+ * @return NA
+ */
+ public float right() {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException
.
+ * @param margin NA
+ * @return NA
+ */
+ public float top(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException
.
+ * @param margin NA
+ * @return NA
+ */
+ public float bottom(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException
.
+ * @param margin NA
+ * @return NA
+ */
+ public float left(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException
.
+ * @param margin NA
+ * @return NA
+ */
+ public float right(int margin) {
+ throw new UnsupportedOperationException("Dimensions of a Table can't be calculated. See the FAQ.");
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException
.
+ * @param value NA
+ */
+ public void setTop(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Table are attributed automagically. See the FAQ.");
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException
.
+ * @param value NA
+ */
+ public void setBottom(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Table are attributed automagically. See the FAQ.");
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException
.
+ * @param value NA
+ */
+ public void setLeft(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Table are attributed automagically. See the FAQ.");
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException
.
+ * @param value NA
+ */
+ public void setRight(int value) {
+ throw new UnsupportedOperationException("Dimensions of a Table are attributed automagically. See the FAQ.");
+ }
+
+ /**
+ * Returns the next row 0-based index where a new cell would be added.
+ * (contributed by dperezcar@fcc.es)
+ * @return x coordinate for the next row
+ */
+ public int getNextRow() {
+ return curPosition.x;
+ }
+
+ /**
+ * Returns the next column 0-based index where a new cell would be added.
+ * (contributed by dperezcar@fcc.es)
+ * @return y coordinate for the next row
+ */
+ public int getNextColumn() {
+ return curPosition.y;
+ }
+
+ private static final double convertWidth( double val) {
+ if( val == 0) {
+ return 0;
+ }
+ try {
+ String tmp = widthFormat.format( val);
+ Number result = widthFormat.parse( tmp);
+
+ return result.doubleValue();
+ }
+ catch( java.text.ParseException pe) {
+ throw new RuntimeException( "Could not convert double to width for val:" + val);
+ }
+ }
+
+ private static DecimalFormat widthFormat = new DecimalFormat( "0.00");
+
+ /**
+ * Create a PdfPTable based on this Table object.
+ * @return a PdfPTable object
+ * @throws BadElementException
+ */
+ public PdfPTable createPdfPTable() throws BadElementException {
+ if (!convert2pdfptable) {
+ throw new BadElementException("No error, just an old style table");
+ }
+ setAutoFillEmptyCells(true);
+ complete();
+ PdfPTable pdfptable = new PdfPTable(widths);
+ pdfptable.setTableEvent(SimpleTable.getDimensionlessInstance(this, cellspacing));
+ pdfptable.setHeaderRows(lastHeaderRow + 1);
+ pdfptable.setSplitLate(cellsFitPage);
+ if (!Float.isNaN(offset)) {
+ pdfptable.setSpacingBefore(offset);
+ }
+ pdfptable.setHorizontalAlignment(alignment);
+ if (absWidth.length() > 0) {
+ try {
+ pdfptable.setTotalWidth(Float.parseFloat(absWidth));
+ }
+ catch(Exception e1) {
+ try {
+ pdfptable.setTotalWidth(Integer.parseInt(absWidth));
+ }
+ catch(Exception e2) {
+ pdfptable.setWidthPercentage(widthPercentage);
+ }
+ }
+ }
+ else {
+ pdfptable.setWidthPercentage(widthPercentage);
+ }
+ Row row;
+ for (Iterator iterator = iterator(); iterator.hasNext(); ) {
+ row = (Row) iterator.next();
+ Element cell;
+ PdfPCell pcell;
+ for (int i = 0; i < row.columns(); i++) {
+ if ((cell = (Element)row.getCell(i)) != null) {
+ if (cell instanceof Table) {
+ pcell = new PdfPCell(((Table)cell).createPdfPTable());
+ }
+ else if (cell instanceof Cell) {
+ pcell = ((Cell)cell).createPdfPCell();
+ pcell.setPadding(cellpadding + cellspacing / 2f);
+ pcell.setCellEvent(SimpleCell.getDimensionlessInstance((Cell)cell, cellspacing));
+ }
+ else {
+ pcell = new PdfPCell();
+ }
+ pdfptable.addCell(pcell);
+ }
+ }
+ }
+ return pdfptable;
+ }
+
+ /**
+ * Method to check if the Table should be converted to a PdfPTable or not.
+ * @return false if the table should be handled the oldfashioned way.
+ */
+ public boolean isConvert2pdfptable() {
+ return convert2pdfptable;
+ }
+ /**
+ * If set to true, iText will try to convert the Table to a PdfPTable.
+ * @param convert2pdfptable true if you want iText to try to convert the Table to a PdfPTable
+ */
+ public void setConvert2pdfptable(boolean convert2pdfptable) {
+ this.convert2pdfptable = convert2pdfptable;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/TextElementArray.java b/src/main/java/com/lowagie/text/TextElementArray.java
new file mode 100644
index 0000000..c03a733
--- /dev/null
+++ b/src/main/java/com/lowagie/text/TextElementArray.java
@@ -0,0 +1,75 @@
+/*
+ * $Id: TextElementArray.java,v 1.59 2005/05/04 14:31:18 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright (c) 1999, 2000, 2001, 2002 Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+/**
+ * Interface for a text element to which other objects can be added.
+ *
+ * @see Phrase
+ * @see Paragraph
+ * @see Section
+ * @see ListItem
+ * @see Chapter
+ * @see Anchor
+ * @see Cell
+ */
+
+public interface TextElementArray extends Element {
+
+ /**
+ * Adds an object to the TextElementArray
.
+ *
+ * @param o an object that has to be added
+ * @return true
if the addition succeeded; false
otherwise
+ */
+
+ public boolean add(Object o);
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/Watermark.java b/src/main/java/com/lowagie/text/Watermark.java
new file mode 100644
index 0000000..771672b
--- /dev/null
+++ b/src/main/java/com/lowagie/text/Watermark.java
@@ -0,0 +1,123 @@
+/*
+ * $Id: Watermark.java,v 1.52 2004/12/14 11:52:47 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text;
+
+import java.net.MalformedURLException;
+
+/**
+ * A Watermark
is a graphic element (GIF or JPEG)
+ * that is shown on a certain position on each page.
+ *
+ * @see Element
+ * @see Jpeg
+ */
+
+public class Watermark extends Image implements Element {
+
+ // membervariables
+
+/** This is the offset in x-direction of the Watermark. */
+ private float offsetX = 0;
+
+/** This is the offset in y-direction of the Watermark. */
+ private float offsetY = 0;
+
+ // Constructors
+
+/**
+ * Constructs a Watermark
-object, using an Image
.
+ *
+ * @param image an Image
-object
+ * @param offsetX the offset in x-direction
+ * @param offsetY the offset in y-direction
+ * @throws MalformedURLException
+ */
+
+ public Watermark(Image image, float offsetX, float offsetY) throws MalformedURLException {
+ super(image);
+ this.offsetX = offsetX;
+ this.offsetY = offsetY;
+ }
+
+ // implementation of the Element interface
+
+/**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return type;
+ }
+
+ // methods to retrieve information
+
+/**
+ * Returns the offset in x direction.
+ *
+ * @return an offset
+ */
+
+ public float offsetX() {
+ return offsetX;
+ }
+
+/**
+ * Returns the offset in y direction.
+ *
+ * @return an offset
+ */
+
+ public float offsetY() {
+ return offsetY;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/ZapfDingbatsList.java b/src/main/java/com/lowagie/text/ZapfDingbatsList.java
new file mode 100644
index 0000000..179b218
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ZapfDingbatsList.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2003 by Michael Niedermair.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text;
+
+/**
+ *
+ * A special-version of LIST
whitch use zapfdingbats-letters.
+ *
+ * @see com.lowagie.text.List
+ * @version 2003-06-22
+ * @author Michael Niedermair
+ */
+
+public class ZapfDingbatsList extends List {
+
+ /**
+ * char-number in zapfdingbats
+ */
+ protected int zn;
+
+ /**
+ * Creates a ZapfDingbatsList
+ *
+ * @param zn a char-number
+ * @param symbolIndent indent
+ */
+ public ZapfDingbatsList(int zn, int symbolIndent) {
+ super(true, symbolIndent);
+ this.zn = zn;
+ float fontsize = symbol.font().size();
+ symbol.setFont(FontFactory.getFont(FontFactory.ZAPFDINGBATS, fontsize, Font.NORMAL));
+ }
+
+ /**
+ * set the char-number
+ * @param zn a char-number
+ */
+ public void setCharNumber(int zn) {
+ this.zn = zn;
+ }
+
+ /**
+ * get the char-number
+ *
+ * @return char-number
+ */
+ public int getCharNumber() {
+ return zn;
+ }
+
+ /**
+ * Adds an Object
to the List
.
+ *
+ * @param o the object to add.
+ * @return true if adding the object succeeded
+ */
+ public boolean add(Object o) {
+ if (o instanceof ListItem) {
+ ListItem item = (ListItem) o;
+ Chunk chunk = new Chunk((char)zn, symbol.font());
+ item.setListSymbol(chunk);
+ item.setIndentationLeft(symbolIndent);
+ item.setIndentationRight(0);
+ list.add(item);
+ } else if (o instanceof List) {
+ List nested = (List) o;
+ nested.setIndentationLeft(nested.indentationLeft() + symbolIndent);
+ first--;
+ return list.add(nested);
+ } else if (o instanceof String) {
+ return this.add(new ListItem((String) o));
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/ZapfDingbatsNumberList.java b/src/main/java/com/lowagie/text/ZapfDingbatsNumberList.java
new file mode 100644
index 0000000..513fbfa
--- /dev/null
+++ b/src/main/java/com/lowagie/text/ZapfDingbatsNumberList.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2003 by Michael Niedermair.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text;
+
+/**
+ *
+ * A special-version of LIST
whitch use zapfdingbats-numbers (1..10).
+ *
+ * @see com.lowagie.text.List
+ * @version 2003-06-22
+ * @author Michael Niedermair
+ */
+
+public class ZapfDingbatsNumberList extends List {
+
+ /**
+ * which type
+ */
+ protected int type;
+
+ /**
+ * Creates a ZapdDingbatsNumberList
+ * @param type the type of list
+ * @param symbolIndent indent
+ */
+ public ZapfDingbatsNumberList(int type, int symbolIndent) {
+ super(true, symbolIndent);
+ this.type = type;
+ float fontsize = symbol.font().size();
+ symbol.setFont(FontFactory.getFont(FontFactory.ZAPFDINGBATS, fontsize, Font.NORMAL));
+ }
+
+ /**
+ * set the type
+ *
+ * @param type
+ */
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ /**
+ * get the type
+ *
+ * @return char-number
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Adds an Object
to the List
.
+ *
+ * @param o the object to add.
+ * @return true if adding the object succeeded
+ */
+ public boolean add(Object o) {
+ if (o instanceof ListItem) {
+ ListItem item = (ListItem) o;
+ Chunk chunk;
+ switch (type ) {
+ case 0:
+ chunk = new Chunk((char)(first + list.size() + 171), symbol.font());
+ break;
+ case 1:
+ chunk = new Chunk((char)(first + list.size() + 181), symbol.font());
+ break;
+ case 2:
+ chunk = new Chunk((char)(first + list.size() + 191), symbol.font());
+ break;
+ default:
+ chunk = new Chunk((char)(first + list.size() + 201), symbol.font());
+ }
+ item.setListSymbol(chunk);
+ item.setIndentationLeft(symbolIndent);
+ item.setIndentationRight(0);
+ list.add(item);
+ } else if (o instanceof List) {
+ List nested = (List) o;
+ nested.setIndentationLeft(nested.indentationLeft() + symbolIndent);
+ first--;
+ return list.add(nested);
+ } else if (o instanceof String) {
+ return this.add(new ListItem((String) o));
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/html/HtmlEncoder.java b/src/main/java/com/lowagie/text/html/HtmlEncoder.java
new file mode 100644
index 0000000..2501815
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/HtmlEncoder.java
@@ -0,0 +1,211 @@
+/*
+ * $Id: HtmlEncoder.java,v 1.56 2005/05/04 14:33:47 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html;
+
+import java.awt.Color;
+
+import com.lowagie.text.Element;
+
+/**
+ * This class converts a String
to the HTML-format of a String.
+ * String
, each character is examined:
+ *
+ *
+ *
+ * (with xxx = the value of the character)
+ *
+ *
+ *
+ * (with xxx = the value of the character)
+ *
+ * String htmlPresentation = HtmlEncoder.encode("Marie-Thérèse Sørensen");
+ *
String
to the HTML-format of this String
.
+ *
+ * @param string The String
to convert
+ * @return a String
+ */
+
+ public static String encode(String string) {
+ int n = string.length();
+ char character;
+ StringBuffer buffer = new StringBuffer();
+ // loop over all the characters of the String.
+ for (int i = 0; i < n; i++) {
+ character = string.charAt(i);
+ // the Htmlcode of these characters are added to a StringBuffer one by one
+ if (character < 256) {
+ buffer.append(htmlCode[character]);
+ }
+ else {
+ // Improvement posted by Joachim Eyrich
+ buffer.append("").append((int)character).append(";");
+ }
+ }
+ return buffer.toString().trim();
+ }
+
+/**
+ * Converts a Color
into a HTML representation of this Color
.
+ *
+ * @param color the Color
that has to be converted.
+ * @return the HTML representation of this Tags
-class maps several XHTML-tags to iText-objects.
+ */
+
+public class HtmlTagMap extends HashMap {
+
+/**
+ * Constructs an HtmlTagMap.
+ */
+
+ public HtmlTagMap() {
+ super();
+ HtmlPeer peer;
+
+ peer = new HtmlPeer(ElementTags.ITEXT, HtmlTags.HTML);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PHRASE, HtmlTags.SPAN);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.CHUNK, HtmlTags.CHUNK);
+ peer.addAlias(ElementTags.FONT, HtmlTags.FONT);
+ peer.addAlias(ElementTags.SIZE, HtmlTags.SIZE);
+ peer.addAlias(ElementTags.COLOR, HtmlTags.COLOR);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.ANCHOR, HtmlTags.ANCHOR);
+ peer.addAlias(ElementTags.NAME, HtmlTags.NAME);
+ peer.addAlias(ElementTags.REFERENCE, HtmlTags.REFERENCE);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PARAGRAPH, HtmlTags.PARAGRAPH);
+ peer.addAlias(ElementTags.ALIGN, HtmlTags.ALIGN);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PARAGRAPH, HtmlTags.DIV);
+ peer.addAlias(ElementTags.ALIGN, HtmlTags.ALIGN);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PARAGRAPH, HtmlTags.H[0]);
+ peer.addValue(ElementTags.SIZE, "20");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PARAGRAPH, HtmlTags.H[1]);
+ peer.addValue(ElementTags.SIZE, "18");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PARAGRAPH, HtmlTags.H[2]);
+ peer.addValue(ElementTags.SIZE, "16");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PARAGRAPH, HtmlTags.H[3]);
+ peer.addValue(ElementTags.SIZE, "14");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PARAGRAPH, HtmlTags.H[4]);
+ peer.addValue(ElementTags.SIZE, "12");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PARAGRAPH, HtmlTags.H[5]);
+ peer.addValue(ElementTags.SIZE, "10");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.LIST, HtmlTags.ORDEREDLIST);
+ peer.addValue(ElementTags.NUMBERED, "true");
+ peer.addValue(ElementTags.SYMBOLINDENT, "20");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.LIST, HtmlTags.UNORDEREDLIST);
+ peer.addValue(ElementTags.NUMBERED, "false");
+ peer.addValue(ElementTags.SYMBOLINDENT, "20");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.LISTITEM, HtmlTags.LISTITEM);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PHRASE, HtmlTags.I);
+ peer.addValue(ElementTags.STYLE, MarkupTags.CSS_VALUE_ITALIC);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PHRASE, HtmlTags.EM);
+ peer.addValue(ElementTags.STYLE, MarkupTags.CSS_VALUE_ITALIC);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PHRASE, HtmlTags.B);
+ peer.addValue(ElementTags.STYLE, MarkupTags.CSS_VALUE_BOLD);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PHRASE, HtmlTags.STRONG);
+ peer.addValue(ElementTags.STYLE, MarkupTags.CSS_VALUE_BOLD);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PHRASE, HtmlTags.S);
+ peer.addValue(ElementTags.STYLE, MarkupTags.CSS_VALUE_LINETHROUGH);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PHRASE, HtmlTags.CODE);
+ peer.addValue(ElementTags.FONT, FontFactory.COURIER);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PHRASE, HtmlTags.VAR);
+ peer.addValue(ElementTags.FONT, FontFactory.COURIER);
+ peer.addValue(ElementTags.STYLE, MarkupTags.CSS_VALUE_ITALIC);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.PHRASE, HtmlTags.U);
+ peer.addValue(ElementTags.STYLE, MarkupTags.CSS_VALUE_UNDERLINE);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.CHUNK, HtmlTags.SUP);
+ peer.addValue(ElementTags.SUBSUPSCRIPT, "6.0");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.CHUNK, HtmlTags.SUB);
+ peer.addValue(ElementTags.SUBSUPSCRIPT, "-6.0");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.HORIZONTALRULE, HtmlTags.HORIZONTALRULE);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.TABLE, HtmlTags.TABLE);
+ peer.addAlias(ElementTags.WIDTH, HtmlTags.WIDTH);
+ peer.addAlias(ElementTags.BACKGROUNDCOLOR, HtmlTags.BACKGROUNDCOLOR);
+ peer.addAlias(ElementTags.BORDERCOLOR, HtmlTags.BORDERCOLOR);
+ peer.addAlias(ElementTags.COLUMNS, HtmlTags.COLUMNS);
+ peer.addAlias(ElementTags.CELLPADDING, HtmlTags.CELLPADDING);
+ peer.addAlias(ElementTags.CELLSPACING, HtmlTags.CELLSPACING);
+ peer.addAlias(ElementTags.BORDERWIDTH, HtmlTags.BORDERWIDTH);
+ peer.addAlias(ElementTags.ALIGN, HtmlTags.ALIGN);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.ROW, HtmlTags.ROW);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.CELL, HtmlTags.CELL);
+ peer.addAlias(ElementTags.WIDTH, HtmlTags.WIDTH);
+ peer.addAlias(ElementTags.BACKGROUNDCOLOR, HtmlTags.BACKGROUNDCOLOR);
+ peer.addAlias(ElementTags.BORDERCOLOR, HtmlTags.BORDERCOLOR);
+ peer.addAlias(ElementTags.COLSPAN, HtmlTags.COLSPAN);
+ peer.addAlias(ElementTags.ROWSPAN, HtmlTags.ROWSPAN);
+ peer.addAlias(ElementTags.NOWRAP, HtmlTags.NOWRAP);
+ peer.addAlias(ElementTags.HORIZONTALALIGN, HtmlTags.HORIZONTALALIGN);
+ peer.addAlias(ElementTags.VERTICALALIGN, HtmlTags.VERTICALALIGN);
+ peer.addValue(ElementTags.HEADER, "false");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.CELL, HtmlTags.HEADERCELL);
+ peer.addAlias(ElementTags.WIDTH, HtmlTags.WIDTH);
+ peer.addAlias(ElementTags.BACKGROUNDCOLOR, HtmlTags.BACKGROUNDCOLOR);
+ peer.addAlias(ElementTags.BORDERCOLOR, HtmlTags.BORDERCOLOR);
+ peer.addAlias(ElementTags.COLSPAN, HtmlTags.COLSPAN);
+ peer.addAlias(ElementTags.ROWSPAN, HtmlTags.ROWSPAN);
+ peer.addAlias(ElementTags.NOWRAP, HtmlTags.NOWRAP);
+ peer.addAlias(ElementTags.HORIZONTALALIGN, HtmlTags.HORIZONTALALIGN);
+ peer.addAlias(ElementTags.VERTICALALIGN, HtmlTags.VERTICALALIGN);
+ peer.addValue(ElementTags.HEADER, "true");
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.IMAGE, HtmlTags.IMAGE);
+ peer.addAlias(ElementTags.URL, HtmlTags.URL);
+ peer.addAlias(ElementTags.ALT, HtmlTags.ALT);
+ peer.addAlias(ElementTags.PLAINWIDTH, HtmlTags.PLAINWIDTH);
+ peer.addAlias(ElementTags.PLAINHEIGHT, HtmlTags.PLAINHEIGHT);
+ put(peer.getAlias(), peer);
+
+ peer = new HtmlPeer(ElementTags.NEWLINE, HtmlTags.NEWLINE);
+ put(peer.getAlias(), peer);
+ }
+
+/**
+ * Checks if this is the root tag.
+ * @param tag a tagvalue
+ * @return true if tag is HTML or html
+ */
+
+ public boolean isHtml(String tag) {
+ return HtmlTags.HTML.equalsIgnoreCase(tag);
+ }
+
+/**
+ * Checks if this is the head tag.
+ * @param tag a tagvalue
+ * @return true if tag is HEAD or head
+ */
+
+ public boolean isHead(String tag) {
+ return HtmlTags.HEAD.equalsIgnoreCase(tag);
+ }
+
+/**
+ * Checks if this is the meta tag.
+ * @param tag a tagvalue
+ * @return true if tag is META or meta
+ */
+
+ public boolean isMeta(String tag) {
+ return HtmlTags.META.equalsIgnoreCase(tag);
+ }
+
+/**
+ * Checks if this is the linl tag.
+ * @param tag a tagvalue
+ * @return true if tag is LINK or link
+ */
+
+ public boolean isLink(String tag) {
+ return HtmlTags.LINK.equalsIgnoreCase(tag);
+ }
+
+/**
+ * Checks if this is the title tag.
+ * @param tag a tagvalue
+ * @return true if tag is TITLE or title
+ */
+
+ public boolean isTitle(String tag) {
+ return HtmlTags.TITLE.equalsIgnoreCase(tag);
+ }
+
+/**
+ * Checks if this is the root tag.
+ * @param tag a tagvalue
+ * @return true if tag is BODY or body
+ */
+
+ public boolean isBody(String tag) {
+ return HtmlTags.BODY.equalsIgnoreCase(tag);
+ }
+
+/**
+ * Checks if this is a special tag.
+ * @param tag a tagvalue
+ * @return true if tag is a HTML, HEAD, META, LINK or BODY tag (case insensitive)
+ */
+ public boolean isSpecialTag(String tag) {
+ return isHtml(tag) || isHead(tag) || isMeta(tag) || isLink(tag) || isBody(tag);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/html/HtmlTags.java b/src/main/java/com/lowagie/text/html/HtmlTags.java
new file mode 100644
index 0000000..43dbcc4
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/HtmlTags.java
@@ -0,0 +1,331 @@
+/*
+ * $Id: HtmlTags.java,v 1.69 2005/05/03 14:43:58 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html;
+
+/**
+ * A class that contains all the possible tagnames and their attributes.
+ */
+
+public class HtmlTags {
+
+/** the root tag. */
+ public static final String HTML = "html";
+
+/** the head tag */
+ public static final String HEAD = "head";
+
+/** This is a possible HTML attribute for the HEAD tag. */
+ public static final String CONTENT = "content";
+
+/** the meta tag */
+ public static final String META = "meta";
+
+/** attribute of the root tag */
+ public static final String SUBJECT = "subject";
+
+/** attribute of the root tag */
+ public static final String KEYWORDS = "keywords";
+
+/** attribute of the root tag */
+ public static final String AUTHOR = "author";
+
+/** the title tag. */
+ public static final String TITLE = "title";
+
+/** the script tag. */
+ public static final String SCRIPT = "script";
+
+/** This is a possible HTML attribute for the SCRIPT tag. */
+ public static final String LANGUAGE = "language";
+
+/** This is a possible value for the LANGUAGE attribute. */
+ public static final String JAVASCRIPT = "JavaScript";
+
+/** the body tag. */
+ public static final String BODY = "body";
+
+/** This is a possible HTML attribute for the BODY tag */
+ public static final String JAVASCRIPT_ONLOAD = "onLoad";
+
+/** This is a possible HTML attribute for the BODY tag */
+ public static final String JAVASCRIPT_ONUNLOAD = "onUnLoad";
+
+/** This is a possible HTML attribute for the BODY tag. */
+ public static final String TOPMARGIN = "topmargin";
+
+/** This is a possible HTML attribute for the BODY tag. */
+ public static final String BOTTOMMARGIN = "bottommargin";
+
+/** This is a possible HTML attribute for the BODY tag. */
+ public static final String LEFTMARGIN = "leftmargin";
+
+/** This is a possible HTML attribute for the BODY tag. */
+ public static final String RIGHTMARGIN = "rightmargin";
+
+ // Phrases, Anchors, Lists and Paragraphs
+
+/** the chunk tag */
+ public static final String CHUNK = "font";
+
+/** the phrase tag */
+ public static final String CODE = "code";
+
+/** the phrase tag */
+ public static final String VAR = "var";
+
+/** the anchor tag */
+ public static final String ANCHOR = "a";
+
+/** the list tag */
+ public static final String ORDEREDLIST = "ol";
+
+/** the list tag */
+ public static final String UNORDEREDLIST = "ul";
+
+/** the listitem tag */
+ public static final String LISTITEM = "li";
+
+/** the paragraph tag */
+ public static final String PARAGRAPH = "p";
+
+/** attribute of anchor tag */
+ public static final String NAME = "name";
+
+/** attribute of anchor tag */
+ public static final String REFERENCE = "href";
+
+/** attribute of anchor tag */
+ public static final String[] H = new String[6];
+ static {
+ H[0] = "h1";
+ H[1] = "h2";
+ H[2] = "h3";
+ H[3] = "h4";
+ H[4] = "h5";
+ H[5] = "h6";
+ }
+
+ // Chunks
+
+/** attribute of the chunk tag */
+ public static final String FONT = "face";
+
+/** attribute of the chunk tag */
+ public static final String SIZE = "point-size";
+
+/** attribute of the chunk/table/cell tag */
+ public static final String COLOR = "color";
+
+/** some phrase tag */
+ public static final String EM = "em";
+
+/** some phrase tag */
+ public static final String I = "i";
+
+/** some phrase tag */
+ public static final String STRONG = "strong";
+
+/** some phrase tag */
+ public static final String B = "b";
+
+/** some phrase tag */
+ public static final String S = "s";
+
+/** some phrase tag */
+ public static final String U = "u";
+
+/** some phrase tag */
+ public static final String SUB = "sub";
+
+/** some phrase tag */
+ public static final String SUP = "sup";
+
+/** the possible value of a tag */
+ public static final String HORIZONTALRULE = "hr";
+
+ // tables/cells
+
+/** the table tag */
+ public static final String TABLE = "table";
+
+/** the cell tag */
+ public static final String ROW = "tr";
+
+/** the cell tag */
+ public static final String CELL = "td";
+
+/** attribute of the cell tag */
+ public static final String HEADERCELL = "th";
+
+/** attribute of the table tag */
+ public static final String COLUMNS = "cols";
+
+/** attribute of the table tag */
+ public static final String CELLPADDING = "cellpadding";
+
+/** attribute of the table tag */
+ public static final String CELLSPACING = "cellspacing";
+
+/** attribute of the cell tag */
+ public static final String COLSPAN = "colspan";
+
+/** attribute of the cell tag */
+ public static final String ROWSPAN = "rowspan";
+
+/** attribute of the cell tag */
+ public static final String NOWRAP = "nowrap";
+
+/** attribute of the table/cell tag */
+ public static final String BORDERWIDTH = "border";
+
+/** attribute of the table/cell tag */
+ public static final String WIDTH = "width";
+
+/** attribute of the table/cell tag */
+ public static final String BACKGROUNDCOLOR = "bgcolor";
+
+/** attribute of the table/cell tag */
+ public static final String BORDERCOLOR = "bordercolor";
+
+/** attribute of paragraph/image/table tag */
+ public static final String ALIGN = "align";
+
+/** attribute of chapter/section/paragraph/table/cell tag */
+ public static final String LEFT = "left";
+
+/** attribute of chapter/section/paragraph/table/cell tag */
+ public static final String RIGHT = "right";
+
+/** attribute of the cell tag */
+ public static final String HORIZONTALALIGN = "align";
+
+/** attribute of the cell tag */
+ public static final String VERTICALALIGN = "valign";
+
+/** attribute of the table/cell tag */
+ public static final String TOP = "top";
+
+/** attribute of the table/cell tag */
+ public static final String BOTTOM = "bottom";
+
+ // Misc
+
+/** the image tag */
+ public static final String IMAGE = "img";
+
+/** attribute of the image tag */
+ public static final String URL = "src";
+
+/** attribute of the image tag */
+ public static final String ALT = "alt";
+
+/** attribute of the image tag */
+ public static final String PLAINWIDTH = "width";
+
+/** attribute of the image tag */
+ public static final String PLAINHEIGHT = "height";
+
+/** the newpage tag */
+ public static final String NEWLINE = "br";
+
+ // alignment attribute values
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_LEFT = "Left";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_CENTER = "Center";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_RIGHT = "Right";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_JUSTIFIED = "Justify";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_TOP = "Top";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_MIDDLE = "Middle";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_BOTTOM = "Bottom";
+
+/** the possible value of an alignment attribute */
+ public static final String ALIGN_BASELINE = "Baseline";
+
+/** the possible value of an alignment attribute */
+ public static final String DEFAULT = "Default";
+
+ /** The DIV tag. */
+ public static final String DIV = "div";
+
+ /** The SPAN tag. */
+ public static final String SPAN = "span";
+ /** The LINK tag. */
+ public static final String LINK = "link";
+
+ /** This is a possible HTML attribute for the LINK tag. */
+ public static final String TEXT_CSS = "text/css";
+
+ /** This is a possible HTML attribute for the LINK tag. */
+ public static final String REL = "rel";
+
+ /** This is used for inline css style information */
+ public static final String STYLE = "style";
+
+ /** This is a possible HTML attribute for the LINK tag. */
+ public static final String TYPE = "type";
+
+ /** This is a possible HTML attribute. */
+ public static final String STYLESHEET = "stylesheet";
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/html/HtmlWriter.java b/src/main/java/com/lowagie/text/html/HtmlWriter.java
new file mode 100644
index 0000000..69ce91c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/HtmlWriter.java
@@ -0,0 +1,1094 @@
+/*
+ * $Id: HtmlWriter.java,v 1.109 2005/11/01 12:27:05 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 LIBRARY GENERAL PUBLIC LICENSE for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Stack;
+import java.util.EmptyStackException;
+
+import com.lowagie.text.*;
+import com.lowagie.text.markup.MarkupTags;
+
+/**
+ * A DocWriter
class for HTML.
+ * HtmlWriter
can be added as a DocListener
+ * to a certain Document
by getting an instance.
+ * Every Element
added to the original Document
+ * will be written to the OutputStream
of this HtmlWriter
.
+ *
+ * // creation of the document with a certain size and certain margins
+ * Document document = new Document(PageSize.A4, 50, 50, 50, 50);
+ * try {
+ * // this will write HTML to the Standard OutputStream
+ * HtmlWriter.getInstance(document, System.out);
+ * // this will write HTML to a file called text.html
+ * HtmlWriter.getInstance(document, new FileOutputStream("text.html"));
+ * // this will write HTML to for instance the OutputStream of a HttpServletResponse-object
+ * HtmlWriter.getInstance(document, response.getOutputStream());
+ * }
+ * catch(DocumentException de) {
+ * System.err.println(de.getMessage());
+ * }
+ * // this will close the document and all the OutputStreams listening to it
+ * document.close();
HtmlWriter
.
+ *
+ * @param doc The Document
that has to be written as HTML
+ * @param os The OutputStream
the writer has to write to.
+ */
+
+ protected HtmlWriter(Document doc, OutputStream os) {
+ super(doc, os);
+
+ document.addDocListener(this);
+ this.pageN = document.getPageNumber();
+ try {
+ os.write(LT);
+ os.write(getISOBytes(HtmlTags.HTML));
+ os.write(GT);
+ os.write(NEWLINE);
+ os.write(TAB);
+ os.write(LT);
+ os.write(getISOBytes(HtmlTags.HEAD));
+ os.write(GT);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+ // get an instance of the HtmlWriter
+
+/**
+ * Gets an instance of the HtmlWriter
.
+ *
+ * @param document The Document
that has to be written
+ * @param os The OutputStream
the writer has to write to.
+ * @return a new HtmlWriter
+ */
+
+ public static HtmlWriter getInstance(Document document, OutputStream os) {
+ return new HtmlWriter(document, os);
+ }
+
+ // implementation of the DocListener methods
+
+/**
+ * Signals that an new page has to be started.
+ *
+ * @return true
if this action succeeded, false
if not.
+ * @throws DocumentException when a document isn't open yet, or has been closed
+ */
+
+ public boolean newPage() throws DocumentException {
+ try {
+ writeStart(HtmlTags.DIV);
+ write(" ");
+ write(HtmlTags.STYLE);
+ write("=\"");
+ writeCssProperty(MarkupTags.CSS_KEY_PAGE_BREAK_BEFORE, MarkupTags.CSS_VALUE_ALWAYS);
+ write("\" /");
+ os.write(GT);
+ }
+ catch(IOException ioe) {
+ throw new DocumentException(ioe);
+ }
+ return true;
+ }
+
+/**
+ * Signals that an Element
was added to the Document
.
+ *
+ * @param element a high level object that has to be translated to HTML
+ * @return true
if the element was added, false
if not.
+ * @throws DocumentException when a document isn't open yet, or has been closed
+ */
+
+ public boolean add(Element element) throws DocumentException {
+ if (pause) {
+ return false;
+ }
+ try {
+
+ switch(element.type()) {
+ case Element.HEADER:
+ try {
+ Header h = (Header) element;
+ if (HtmlTags.STYLESHEET.equals(h.name())) {
+ writeLink(h);
+ }
+ else if (HtmlTags.JAVASCRIPT.equals(h.name())) {
+ writeJavaScript(h);
+ }
+ else {
+ writeHeader(h);
+ }
+ }
+ catch(ClassCastException cce) {
+ }
+ return true;
+ case Element.SUBJECT:
+ case Element.KEYWORDS:
+ case Element.AUTHOR:
+ Meta meta = (Meta) element;
+ writeHeader(meta);
+ return true;
+ case Element.TITLE:
+ addTabs(2);
+ writeStart(HtmlTags.TITLE);
+ os.write(GT);
+ addTabs(3);
+ write(HtmlEncoder.encode(((Meta)element).content()));
+ addTabs(2);
+ writeEnd(HtmlTags.TITLE);
+ return true;
+ case Element.CREATOR:
+ writeComment("Creator: " + HtmlEncoder.encode(((Meta)element).content()));
+ return true;
+ case Element.PRODUCER:
+ writeComment("Producer: " + HtmlEncoder.encode(((Meta)element).content()));
+ return true;
+ case Element.CREATIONDATE:
+ writeComment("Creationdate: " + HtmlEncoder.encode(((Meta)element).content()));
+ return true;
+ default:
+ write(element, 2);
+ return true;
+ }
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+/**
+ * Signals that the Document
has been opened and that
+ * Elements
can be added.
+ * HEAD
-section of the HTML-document is written.
+ */
+
+ public void open() {
+ super.open();
+ try {
+ writeComment(Document.getVersion());
+ writeComment("CreationDate: " + new Date().toString());
+ addTabs(1);
+ writeEnd(HtmlTags.HEAD);
+ addTabs(1);
+ writeStart(HtmlTags.BODY);
+ if (document.leftMargin() > 0) {
+ write(HtmlTags.LEFTMARGIN, String.valueOf(document.leftMargin()));
+ }
+ if (document.rightMargin() > 0) {
+ write(HtmlTags.RIGHTMARGIN, String.valueOf(document.rightMargin()));
+ }
+ if (document.topMargin() > 0) {
+ write(HtmlTags.TOPMARGIN, String.valueOf(document.topMargin()));
+ }
+ if (document.bottomMargin() > 0) {
+ write(HtmlTags.BOTTOMMARGIN, String.valueOf(document.bottomMargin()));
+ }
+ if (pageSize.backgroundColor() != null) {
+ write(HtmlTags.BACKGROUNDCOLOR, HtmlEncoder.encode(pageSize.backgroundColor()));
+ }
+ if (document.getJavaScript_onLoad() != null) {
+ write(HtmlTags.JAVASCRIPT_ONLOAD, HtmlEncoder.encode(document.getJavaScript_onLoad()));
+ }
+ if (document.getJavaScript_onUnLoad() != null) {
+ write(HtmlTags.JAVASCRIPT_ONUNLOAD, HtmlEncoder.encode(document.getJavaScript_onUnLoad()));
+ }
+ if (document.getHtmlStyleClass() != null) {
+ write(MarkupTags.HTML_ATTR_CSS_CLASS, document.getHtmlStyleClass());
+ }
+ os.write(GT);
+ initHeader(); // line added by David Freels
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+/**
+ * Signals that the Document
was closed and that no other
+ * Elements
will be added.
+ */
+
+ public void close() {
+ try {
+ initFooter(); // line added by David Freels
+ addTabs(1);
+ writeEnd(HtmlTags.BODY);
+ os.write(NEWLINE);
+ writeEnd(HtmlTags.HTML);
+ super.close();
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+ // some protected methods
+
+/**
+ * Adds the header to the top of the String
was added to the Document
.
+ *
+ * @param string a String to add to the HTML
+ * @return true
if the string was added, false
if not.
+ * @throws DocumentException when a document isn't open yet, or has been closed
+ */
+
+ public boolean add(String string) throws DocumentException{
+ if (pause) {
+ return false;
+ }
+ try
+ {
+ write(string);
+ return true;
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+/**
+ * Writes the HTML representation of an element.
+ *
+ * @param element the element
+ * @param indent the indentation
+ * @throws IOException
+ */
+
+ protected void write(Element element, int indent) throws IOException {
+ Properties styleAttributes = null;
+ switch(element.type()) {
+ case Element.CHUNK:
+ {
+ Chunk chunk = (Chunk) element;
+ // if the chunk contains an image, return the image representation
+ Image image = chunk.getImage();
+ if (image != null) {
+ write(image, indent);
+ return;
+ }
+
+ if (chunk.isEmpty()) return;
+ HashMap attributes = chunk.getAttributes();
+ if (attributes != null && attributes.get(Chunk.NEWPAGE) != null) {
+ return;
+ }
+ // This doesn't seem to work:
+ //if (attributes != null && attributes.get(Chunk.SUBSUPSCRIPT) != null) {
+ // float p = (((Float)attributes.get(Chunk.SUBSUPSCRIPT)).floatValue() * 100f) / chunk.font().size();
+ // styleAttributes = new Properties();
+ // styleAttributes.setProperty(MarkupTags.CSS_VERTICALALIGN, "" + p + "%");
+ //}
+ boolean tag = isOtherFont(chunk.font()) || hasMarkupAttributes(chunk) || styleAttributes != null;
+ if (tag) {
+ // start span tag
+ addTabs(indent);
+ writeStart(HtmlTags.SPAN);
+ if (isOtherFont(chunk.font())) {
+ write(chunk.font(), styleAttributes);
+ }
+ if (hasMarkupAttributes(chunk)) {
+ writeMarkupAttributes(chunk);
+ }
+ os.write(GT);
+ }
+ if (attributes != null && attributes.get(Chunk.SUBSUPSCRIPT) != null) {
+ // start sup or sub tag
+ if (((Float)attributes.get(Chunk.SUBSUPSCRIPT)).floatValue() > 0) {
+ writeStart(HtmlTags.SUP);
+ }
+ else {
+ writeStart(HtmlTags.SUB);
+ }
+ os.write(GT);
+ }
+ // contents
+ write(HtmlEncoder.encode(chunk.content()));
+ if (attributes != null && attributes.get(Chunk.SUBSUPSCRIPT) != null) {
+ // end sup or sub tag
+ os.write(LT);
+ os.write(FORWARD);
+ if (((Float)attributes.get(Chunk.SUBSUPSCRIPT)).floatValue() > 0) {
+ write(HtmlTags.SUP);
+ }
+ else {
+ write(HtmlTags.SUB);
+ }
+ os.write(GT);
+ }
+ if (tag) {
+ // end tag
+ writeEnd(MarkupTags.HTML_TAG_SPAN);
+ }
+ return;
+ }
+ case Element.PHRASE:
+ {
+ Phrase phrase = (Phrase) element;
+ styleAttributes = new Properties();
+ if (phrase.leadingDefined()) styleAttributes.setProperty(MarkupTags.CSS_KEY_LINEHEIGHT, String.valueOf(phrase.leading()) + "pt");
+
+ // start tag
+ addTabs(indent);
+ writeStart(MarkupTags.HTML_TAG_SPAN);
+ if (hasMarkupAttributes(phrase)) {
+ writeMarkupAttributes(phrase);
+ }
+ write(phrase.font(), styleAttributes);
+ os.write(GT);
+ currentfont.push(phrase.font());
+ // contents
+ for (Iterator i = phrase.iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ // end tag
+ addTabs(indent);
+ writeEnd(MarkupTags.HTML_TAG_SPAN);
+ currentfont.pop();
+ return;
+ }
+ case Element.ANCHOR:
+ {
+ Anchor anchor = (Anchor) element;
+ styleAttributes = new Properties();
+ if (anchor.leadingDefined()) styleAttributes.setProperty(MarkupTags.CSS_KEY_LINEHEIGHT, String.valueOf(anchor.leading()) + "pt");
+
+ // start tag
+ addTabs(indent);
+ writeStart(HtmlTags.ANCHOR);
+ if (anchor.name() != null) {
+ write(HtmlTags.NAME, anchor.name());
+ }
+ if (anchor.reference() != null) {
+ write(HtmlTags.REFERENCE, anchor.reference());
+ }
+ if (hasMarkupAttributes(anchor)) {
+ writeMarkupAttributes(anchor);
+ }
+ write(anchor.font(), styleAttributes);
+ os.write(GT);
+ currentfont.push(anchor.font());
+ // contents
+ for (Iterator i = anchor.iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ // end tag
+ addTabs(indent);
+ writeEnd(HtmlTags.ANCHOR);
+ currentfont.pop();
+ return;
+ }
+ case Element.PARAGRAPH:
+ {
+ Paragraph paragraph = (Paragraph) element;
+ styleAttributes = new Properties();
+ if (paragraph.leadingDefined()) styleAttributes.setProperty(MarkupTags.CSS_KEY_LINEHEIGHT, String.valueOf(paragraph.leading()) + "pt");
+ // start tag
+ addTabs(indent);
+ writeStart(HtmlTags.DIV);
+ if (hasMarkupAttributes(paragraph)) {
+ writeMarkupAttributes(paragraph);
+ }
+ String alignment = HtmlEncoder.getAlignment(paragraph.alignment());
+ if (!"".equals(alignment)) {
+ write(HtmlTags.ALIGN, alignment);
+ }
+ write(paragraph.font(), styleAttributes);
+ os.write(GT);
+ currentfont.push(paragraph.font());
+ // contents
+ for (Iterator i = paragraph.iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ // end tag
+ addTabs(indent);
+ writeEnd(HtmlTags.DIV);
+ currentfont.pop();
+ return;
+ }
+ case Element.SECTION:
+ case Element.CHAPTER:
+ {
+ // part of the start tag + contents
+ writeSection((Section) element, indent);
+ return;
+ }
+ case Element.LIST:
+ {
+ List list = (List) element;
+ // start tag
+ addTabs(indent);
+ if (list.isNumbered()) {
+ writeStart(HtmlTags.ORDEREDLIST);
+ }
+ else {
+ writeStart(HtmlTags.UNORDEREDLIST);
+ }
+ if (hasMarkupAttributes(list)) {
+ writeMarkupAttributes(list);
+ }
+ os.write(GT);
+ // contents
+ for (Iterator i = list.getItems().iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ // end tag
+ addTabs(indent);
+ if (list.isNumbered()) {
+ writeEnd(HtmlTags.ORDEREDLIST);
+ }
+ else {
+ writeEnd(HtmlTags.UNORDEREDLIST);
+ }
+ return;
+ }
+ case Element.LISTITEM:
+ {
+ ListItem listItem = (ListItem) element;
+ styleAttributes = new Properties();
+ if (listItem.leadingDefined()) styleAttributes.setProperty(MarkupTags.CSS_KEY_LINEHEIGHT, String.valueOf(listItem.leading()) + "pt");
+
+ // start tag
+ addTabs(indent);
+ writeStart(HtmlTags.LISTITEM);
+ if (hasMarkupAttributes(listItem)) {
+ writeMarkupAttributes(listItem);
+ }
+ write(listItem.font(), styleAttributes);
+ os.write(GT);
+ currentfont.push(listItem.font());
+ // contents
+ for (Iterator i = listItem.iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ // end tag
+ addTabs(indent);
+ writeEnd(HtmlTags.LISTITEM);
+ currentfont.pop();
+ return;
+ }
+ case Element.CELL:
+ {
+ Cell cell = (Cell) element;
+
+ // start tag
+ addTabs(indent);
+ if (cell.header()) {
+ writeStart(HtmlTags.HEADERCELL);
+ }
+ else {
+ writeStart(HtmlTags.CELL);
+ }
+ if (hasMarkupAttributes(cell)) {
+ writeMarkupAttributes(cell);
+ }
+ if (cell.borderWidth() != Rectangle.UNDEFINED) {
+ write(HtmlTags.BORDERWIDTH, String.valueOf(cell.borderWidth()));
+ }
+ if (cell.borderColor() != null) {
+ write(HtmlTags.BORDERCOLOR, HtmlEncoder.encode(cell.borderColor()));
+ }
+ if (cell.backgroundColor() != null) {
+ write(HtmlTags.BACKGROUNDCOLOR, HtmlEncoder.encode(cell.backgroundColor()));
+ }
+ String alignment = HtmlEncoder.getAlignment(cell.horizontalAlignment());
+ if (!"".equals(alignment)) {
+ write(HtmlTags.HORIZONTALALIGN, alignment);
+ }
+ alignment = HtmlEncoder.getAlignment(cell.verticalAlignment());
+ if (!"".equals(alignment)) {
+ write(HtmlTags.VERTICALALIGN, alignment);
+ }
+ if (cell.cellWidth() != null) {
+ write(HtmlTags.WIDTH, cell.cellWidth());
+ }
+ if (cell.colspan() != 1) {
+ write(HtmlTags.COLSPAN, String.valueOf(cell.colspan()));
+ }
+ if (cell.rowspan() != 1) {
+ write(HtmlTags.ROWSPAN, String.valueOf(cell.rowspan()));
+ }
+ if (cell.noWrap()) {
+ write(HtmlTags.NOWRAP, String.valueOf(true));
+ }
+ os.write(GT);
+ // contents
+ if (cell.isEmpty()) {
+ write(NBSP);
+ } else {
+ for (Iterator i = cell.getElements(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ }
+ // end tag
+ addTabs(indent);
+ if (cell.header()) {
+ writeEnd(HtmlTags.HEADERCELL);
+ }
+ else {
+ writeEnd(HtmlTags.CELL);
+ }
+ return;
+ }
+ case Element.ROW:
+ {
+ Row row = (Row) element;
+
+ // start tag
+ addTabs(indent);
+ writeStart(HtmlTags.ROW);
+ if (hasMarkupAttributes(row)) {
+ writeMarkupAttributes(row);
+ }
+ os.write(GT);
+ // contents
+ Element cell;
+ for (int i = 0; i < row.columns(); i++) {
+ if ((cell = (Element)row.getCell(i)) != null) {
+ write(cell, indent + 1);
+ }
+ }
+ // end tag
+ addTabs(indent);
+ writeEnd(HtmlTags.ROW);
+ return;
+ }
+ case Element.TABLE:
+ {
+ Table table;
+ try {
+ table = (Table) element;
+ }
+ catch(ClassCastException cce) {
+ try {
+ table = ((SimpleTable)element).createTable();
+ } catch (BadElementException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+ table.complete();
+ // start tag
+ addTabs(indent);
+ writeStart(HtmlTags.TABLE);
+ if (hasMarkupAttributes(table)) {
+ writeMarkupAttributes(table);
+ }
+ os.write(SPACE);
+ write(HtmlTags.WIDTH);
+ os.write(EQUALS);
+ os.write(QUOTE);
+ if (! "".equals(table.absWidth())){
+ write(table.absWidth());
+ }
+ else{
+ write(String.valueOf(table.widthPercentage()));
+ write("%");
+ }
+ os.write(QUOTE);
+ String alignment = HtmlEncoder.getAlignment(table.alignment());
+ if (!"".equals(alignment)) {
+ write(HtmlTags.ALIGN, alignment);
+ }
+ write(HtmlTags.CELLPADDING, String.valueOf(table.cellpadding()));
+ write(HtmlTags.CELLSPACING, String.valueOf(table.cellspacing()));
+ if (table.borderWidth() != Rectangle.UNDEFINED) {
+ write(HtmlTags.BORDERWIDTH, String.valueOf(table.borderWidth()));
+ }
+ if (table.borderColor() != null) {
+ write(HtmlTags.BORDERCOLOR, HtmlEncoder.encode(table.borderColor()));
+ }
+ if (table.backgroundColor() != null) {
+ write(HtmlTags.BACKGROUNDCOLOR, HtmlEncoder.encode(table.backgroundColor()));
+ }
+ os.write(GT);
+ // contents
+ Row row;
+ for (Iterator iterator = table.iterator(); iterator.hasNext(); ) {
+ row = (Row) iterator.next();
+ write(row, indent + 1);
+ }
+ // end tag
+ addTabs(indent);
+ writeEnd(HtmlTags.TABLE);
+ return;
+ }
+ case Element.ANNOTATION:
+ {
+ Annotation annotation = (Annotation) element;
+ writeComment(annotation.title() + ": " + annotation.content());
+ if (hasMarkupAttributes(annotation)) {
+ os.write(BEGINCOMMENT);
+ writeMarkupAttributes(annotation);
+ os.write(ENDCOMMENT);
+ }
+ return;
+ }
+ case Element.IMGRAW:
+ case Element.JPEG:
+ case Element.IMGTEMPLATE:
+ {
+ Image image = (Image) element;
+ if (image.url() == null) {
+ return;
+ }
+
+ // start tag
+ addTabs(indent);
+ writeStart(HtmlTags.IMAGE);
+ String path = image.url().toString();
+ if (imagepath != null) {
+ if (path.indexOf("/") > 0) {
+ path = imagepath + path.substring(path.lastIndexOf("/") + 1);
+ }
+ else {
+ path = imagepath + path;
+ }
+ }
+ write(HtmlTags.URL, path);
+ if ((image.alignment() & Image.LEFT) > 0) {
+ write(HtmlTags.ALIGN, HtmlTags.ALIGN_LEFT);
+ }
+ else if ((image.alignment() & Image.RIGHT) > 0) {
+ write(HtmlTags.ALIGN, HtmlTags.ALIGN_RIGHT);
+ }
+ else if ((image.alignment() & Image.MIDDLE) > 0) {
+ write(HtmlTags.ALIGN, HtmlTags.ALIGN_MIDDLE);
+ }
+ if (image.alt() != null) {
+ write(HtmlTags.ALT, image.alt());
+ }
+ write(HtmlTags.PLAINWIDTH, String.valueOf(image.scaledWidth()));
+ write(HtmlTags.PLAINHEIGHT, String.valueOf(image.scaledHeight()));
+ if (hasMarkupAttributes(image)){
+ writeMarkupAttributes(image);
+ }
+ writeEnd();
+ return;
+ }
+
+ default:
+ return;
+ }
+ }
+
+/**
+ * Writes the HTML representation of a section.
+ *
+ * @param section the section to write
+ * @param indent the indentation
+ * @throws IOException
+ */
+
+ protected void writeSection(Section section, int indent) throws IOException {
+ if (section.title() != null) {
+ int depth = section.depth() - 1;
+ if (depth > 5) {
+ depth = 5;
+ }
+ Properties styleAttributes = new Properties();
+ if (section.title().leadingDefined()) styleAttributes.setProperty(MarkupTags.CSS_KEY_LINEHEIGHT, String.valueOf(section.title().leading()) + "pt");
+ // start tag
+ addTabs(indent);
+ writeStart(HtmlTags.H[depth]);
+ write(section.title().font(), styleAttributes);
+ String alignment = HtmlEncoder.getAlignment(section.title().alignment());
+ if (!"".equals(alignment)) {
+ write(HtmlTags.ALIGN, alignment);
+ }
+ if (hasMarkupAttributes(section.title())) {
+ writeMarkupAttributes(section.title());
+ }
+ os.write(GT);
+ currentfont.push(section.title().font());
+ // contents
+ for (Iterator i = section.title().iterator(); i.hasNext(); ) {
+ write((Element)i.next(), indent + 1);
+ }
+ // end tag
+ addTabs(indent);
+ writeEnd(HtmlTags.H[depth]);
+ currentfont.pop();
+ }
+ for (Iterator i = section.iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent);
+ }
+ }
+
+ /**
+ * Writes the representation of a Font
.
+ *
+ * @param font a Font
+ * @param styleAttributes the style of the font
+ * @throws IOException
+ */
+
+ protected void write(Font font, Properties styleAttributes) throws IOException {
+ if (font == null || !isOtherFont(font) /* || styleAttributes == null*/) return;
+ write(" ");
+ write(HtmlTags.STYLE);
+ write("=\"");
+ if (styleAttributes != null) {
+ String key;
+ for (Enumeration e = styleAttributes.propertyNames(); e.hasMoreElements(); ) {
+ key = (String)e.nextElement();
+ writeCssProperty(key, styleAttributes.getProperty(key));
+ }
+ }
+ if (isOtherFont(font)) {
+ writeCssProperty(MarkupTags.CSS_KEY_FONTFAMILY, font.getFamilyname());
+
+ if (font.size() != Font.UNDEFINED) {
+ writeCssProperty(MarkupTags.CSS_KEY_FONTSIZE, String.valueOf(font.size()) + "pt");
+ }
+ if (font.color() != null) {
+ writeCssProperty(MarkupTags.CSS_KEY_COLOR, HtmlEncoder.encode(font.color()));
+ }
+
+ int fontstyle = font.style();
+ if (fontstyle != Font.UNDEFINED && fontstyle != Font.NORMAL) {
+ switch (fontstyle & Font.BOLDITALIC) {
+ case Font.BOLD:
+ writeCssProperty(MarkupTags.CSS_KEY_FONTWEIGHT, MarkupTags.CSS_VALUE_BOLD);
+ break;
+ case Font.ITALIC:
+ writeCssProperty(MarkupTags.CSS_KEY_FONTSTYLE, MarkupTags.CSS_VALUE_ITALIC);
+ break;
+ case Font.BOLDITALIC:
+ writeCssProperty(MarkupTags.CSS_KEY_FONTWEIGHT, MarkupTags.CSS_VALUE_BOLD);
+ writeCssProperty(MarkupTags.CSS_KEY_FONTSTYLE, MarkupTags.CSS_VALUE_ITALIC);
+ break;
+ }
+
+ // CSS only supports one decoration tag so if both are specified
+ // only one of the two will display
+ if ((fontstyle & Font.UNDERLINE) > 0) {
+ writeCssProperty(MarkupTags.CSS_KEY_TEXTDECORATION, MarkupTags.CSS_VALUE_UNDERLINE);
+ }
+ if ((fontstyle & Font.STRIKETHRU) > 0) {
+ writeCssProperty(MarkupTags.CSS_KEY_TEXTDECORATION, MarkupTags.CSS_VALUE_LINETHROUGH);
+ }
+ }
+ }
+ write("\"");
+ }
+
+ /**
+ * Writes out a CSS property.
+ * @param prop a CSS property
+ * @param value the value of the CSS property
+ * @throws IOException
+ */
+ protected void writeCssProperty(String prop, String value) throws IOException {
+ write(new StringBuffer(prop).append(": ").append(value).append("; ").toString());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/html/SAXmyHtmlHandler.java b/src/main/java/com/lowagie/text/html/SAXmyHtmlHandler.java
new file mode 100644
index 0000000..76c2e6a
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/SAXmyHtmlHandler.java
@@ -0,0 +1,293 @@
+/*
+ * $Id: SAXmyHtmlHandler.java,v 1.25 2006/03/23 09:51:50 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Properties;
+
+import org.xml.sax.Attributes;
+
+import com.lowagie.text.Cell;
+import com.lowagie.text.DocListener;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.ElementTags;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.Table;
+import com.lowagie.text.html.HtmlTagMap;
+import com.lowagie.text.html.HtmlTags;
+import com.lowagie.text.pdf.BaseFont;
+import com.lowagie.text.xml.SAXiTextHandler;
+import com.lowagie.text.xml.XmlPeer;
+
+/**
+ * The Tags
-class maps several XHTML-tags to iText-objects.
+ */
+
+public class SAXmyHtmlHandler extends SAXiTextHandler // SAXmyHandler
+{
+
+ /** These are the properties of the body section. */
+ private Properties bodyAttributes = new Properties();
+
+ /** This is the status of the table border. */
+ private boolean tableBorder = false;
+
+ /**
+ * Constructs a new SAXiTextHandler that will translate all the events
+ * triggered by the parser to actions on the Document
-object.
+ *
+ * @param document
+ * this is the document on which events must be triggered
+ * @throws IOException
+ * @throws DocumentException
+ */
+
+ public SAXmyHtmlHandler(DocListener document)
+ throws DocumentException, IOException {
+ super(document, new HtmlTagMap());
+ }
+ /**
+ * Constructs a new SAXiTextHandler that will translate all the events
+ * triggered by the parser to actions on the Document
-object.
+ *
+ * @param document
+ * this is the document on which events must be triggered
+ * @param bf
+ * @throws IOException
+ * @throws DocumentException
+ */
+
+ public SAXmyHtmlHandler(DocListener document, BaseFont bf)
+ throws DocumentException, IOException {
+ super(document, new HtmlTagMap(), bf);
+ }
+
+ /**
+ * Constructs a new SAXiTextHandler that will translate all the events
+ * triggered by the parser to actions on the Document
-object.
+ *
+ * @param document
+ * this is the document on which events must be triggered
+ * @param htmlTags
+ * a tagmap translating HTML tags to iText tags
+ * @throws IOException
+ * @throws DocumentException
+ */
+
+ public SAXmyHtmlHandler(DocListener document, HashMap htmlTags)
+ throws DocumentException, IOException {
+ super(document, htmlTags);
+ }
+
+ /**
+ * This method gets called when a start tag is encountered.
+ *
+ * @param uri
+ * the Uniform Resource Identifier
+ * @param lname
+ * the local name (without prefix), or the empty string if
+ * Namespace processing is not being performed.
+ * @param name
+ * the name of the tag that is encountered
+ * @param attrs
+ * the list of attributes
+ */
+
+ public void startElement(String uri, String lname, String name,
+ Attributes attrs) {
+ // System.err.println("Start: " + name);
+
+ // super.handleStartingTags is replaced with handleStartingTags
+ // suggestion by Vu Ngoc Tan/Hop
+ name = name.toLowerCase();
+ if (((HtmlTagMap) myTags).isHtml(name)) {
+ // we do nothing
+ return;
+ }
+ if (((HtmlTagMap) myTags).isHead(name)) {
+ // we do nothing
+ return;
+ }
+ if (((HtmlTagMap) myTags).isTitle(name)) {
+ // we do nothing
+ return;
+ }
+ if (((HtmlTagMap) myTags).isMeta(name)) {
+ // we look if we can change the body attributes
+ String meta = null;
+ String content = null;
+ if (attrs != null) {
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String attribute = attrs.getQName(i);
+ if (attribute.equalsIgnoreCase(HtmlTags.CONTENT))
+ content = attrs.getValue(i);
+ else if (attribute.equalsIgnoreCase(HtmlTags.NAME))
+ meta = attrs.getValue(i);
+ }
+ }
+ if (meta != null && content != null) {
+ bodyAttributes.put(meta, content);
+ }
+ return;
+ }
+ if (((HtmlTagMap) myTags).isLink(name)) {
+ // we do nothing for the moment, in a later version we could extract
+ // the style sheet
+ return;
+ }
+ if (((HtmlTagMap) myTags).isBody(name)) {
+ // maybe we could extract some info about the document: color,
+ // margins,...
+ // but that's for a later version...
+ XmlPeer peer = new XmlPeer(ElementTags.ITEXT, name);
+ peer.addAlias(ElementTags.TOP, HtmlTags.TOPMARGIN);
+ peer.addAlias(ElementTags.BOTTOM, HtmlTags.BOTTOMMARGIN);
+ peer.addAlias(ElementTags.RIGHT, HtmlTags.RIGHTMARGIN);
+ peer.addAlias(ElementTags.LEFT, HtmlTags.LEFTMARGIN);
+ String content = null;
+ bodyAttributes.putAll(peer.getAttributes(attrs));
+ handleStartingTags(peer.getTag(), bodyAttributes);
+ return;
+ }
+ if (myTags.containsKey(name)) {
+ XmlPeer peer = (XmlPeer) myTags.get(name);
+ if (Table.isTag(peer.getTag()) || Cell.isTag(peer.getTag())) {
+ Properties p = peer.getAttributes(attrs);
+ String value;
+ if (Table.isTag(peer.getTag())
+ && (value = p.getProperty(ElementTags.BORDERWIDTH)) != null) {
+ if (Float.valueOf(value + "f").floatValue() > 0) {
+ tableBorder = true;
+ }
+ }
+ if (tableBorder) {
+ p.put(ElementTags.LEFT, String.valueOf(true));
+ p.put(ElementTags.RIGHT, String.valueOf(true));
+ p.put(ElementTags.TOP, String.valueOf(true));
+ p.put(ElementTags.BOTTOM, String.valueOf(true));
+ }
+ handleStartingTags(peer.getTag(), p);
+ return;
+ }
+ handleStartingTags(peer.getTag(), peer.getAttributes(attrs));
+ return;
+ }
+ Properties attributes = new Properties();
+ if (attrs != null) {
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String attribute = attrs.getQName(i).toLowerCase();
+ attributes.setProperty(attribute, attrs.getValue(i).toLowerCase());
+ }
+ }
+ handleStartingTags(name, attributes);
+ }
+
+ /**
+ * This method gets called when an end tag is encountered.
+ *
+ * @param uri
+ * the Uniform Resource Identifier
+ * @param lname
+ * the local name (without prefix), or the empty string if
+ * Namespace processing is not being performed.
+ * @param name
+ * the name of the tag that ends
+ */
+
+ public void endElement(String uri, String lname, String name) {
+ // System.err.println("End: " + name);
+ name = name.toLowerCase();
+ if (Paragraph.isTag(name)) {
+ try {
+ document.add((Element) stack.pop());
+ return;
+ } catch (DocumentException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+ if (((HtmlTagMap) myTags).isHead(name)) {
+ // we do nothing
+ return;
+ }
+ if (((HtmlTagMap) myTags).isTitle(name)) {
+ if (currentChunk != null) {
+ bodyAttributes.put(ElementTags.TITLE, currentChunk.content());
+ }
+ return;
+ }
+ if (((HtmlTagMap) myTags).isMeta(name)) {
+ // we do nothing
+ return;
+ }
+ if (((HtmlTagMap) myTags).isLink(name)) {
+ // we do nothing
+ return;
+ }
+ if (((HtmlTagMap) myTags).isBody(name)) {
+ // we do nothing
+ return;
+ }
+ if (myTags.containsKey(name)) {
+ XmlPeer peer = (XmlPeer) myTags.get(name);
+ if (Table.isTag(peer.getTag())) {
+ tableBorder = false;
+ }
+ super.handleEndingTags(peer.getTag());
+ return;
+ }
+ // super.handleEndingTags is replaced with handleEndingTags
+ // suggestion by Ken Auer
+ handleEndingTags(name);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/html/simpleparser/ALink.java b/src/main/java/com/lowagie/text/html/simpleparser/ALink.java
new file mode 100644
index 0000000..a190e38
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/simpleparser/ALink.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2005 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html.simpleparser;
+
+import com.lowagie.text.Paragraph;
+/**
+ *
+ * @author psoares
+ */
+public interface ALink {
+ boolean process(Paragraph current, ChainedProperties cprops);
+}
diff --git a/src/main/java/com/lowagie/text/html/simpleparser/ChainedProperties.java b/src/main/java/com/lowagie/text/html/simpleparser/ChainedProperties.java
new file mode 100644
index 0000000..c3e078a
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/simpleparser/ChainedProperties.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2004 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html.simpleparser;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class ChainedProperties {
+
+ public final static int fontSizes[] = {8, 10, 12, 14, 18, 24, 36};
+ public ArrayList chain = new ArrayList();
+
+ /** Creates a new instance of ChainedProperties */
+ public ChainedProperties() {
+ }
+
+ public String getProperty(String key) {
+ for (int k = chain.size() - 1; k >= 0; --k) {
+ Object obj[] = (Object[])chain.get(k);
+ HashMap prop = (HashMap)obj[1];
+ String ret = (String)prop.get(key);
+ if (ret != null)
+ return ret;
+ }
+ return null;
+ }
+
+ public boolean hasProperty(String key) {
+ for (int k = chain.size() - 1; k >= 0; --k) {
+ Object obj[] = (Object[])chain.get(k);
+ HashMap prop = (HashMap)obj[1];
+ if (prop.containsKey(key))
+ return true;
+ }
+ return false;
+ }
+
+ public void addToChain(String key, HashMap prop) {
+ // adjust the font size
+ String value = (String)prop.get("size");
+ if (value != null) {
+ if (value.endsWith("px")) {
+ prop.put("size", value.substring(0, value.length() - 2));
+ }
+ else {
+ int s = 0;
+ if (value.startsWith("+") || value.startsWith("-")) {
+ String old = getProperty("basefontsize");
+ if (old == null)
+ old = "12";
+ float f = Float.valueOf(old).floatValue();
+ int c = (int)f;
+ for (int k = fontSizes.length - 1; k >= 0; --k) {
+ if (c >= fontSizes[k]) {
+ s = k;
+ break;
+ }
+ }
+ int inc = Integer.parseInt(value.startsWith("+") ? value.substring(1) : value);
+ s += inc;
+ }
+ else
+ s = Integer.parseInt(value) - 1;
+ if (s < 0)
+ s = 0;
+ else if (s >= fontSizes.length)
+ s = fontSizes.length - 1;
+ prop.put("size", Integer.toString(fontSizes[s]));
+ }
+ }
+ chain.add(new Object[]{key, prop});
+ }
+
+ public void removeChain(String key) {
+ for (int k = chain.size() - 1; k >= 0; --k) {
+ if (key.equals(((Object[])chain.get(k))[0])) {
+ chain.remove(k);
+ return;
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/html/simpleparser/FactoryProperties.java b/src/main/java/com/lowagie/text/html/simpleparser/FactoryProperties.java
new file mode 100644
index 0000000..a017a1a
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/simpleparser/FactoryProperties.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2004 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html.simpleparser;
+
+import com.lowagie.text.*;
+import com.lowagie.text.pdf.*;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import java.awt.Color;
+
+/**
+ *
+ * @author psoares
+ */
+public class FactoryProperties {
+
+ private FontFactoryImp fontImp;
+
+ /** Creates a new instance of FactoryProperties */
+ public FactoryProperties() {
+ }
+
+ public Chunk createChunk(String text, ChainedProperties props) {
+ Chunk ck = new Chunk(text, getFont(props));
+ if (props.hasProperty("sub"))
+ ck.setTextRise(-6);
+ else if (props.hasProperty("sup"))
+ ck.setTextRise(6);
+ return ck;
+ }
+
+ private static void setParagraphLeading(Paragraph p, String leading) {
+ if (leading == null) {
+ p.setLeading(0, 1.5f);
+ return;
+ }
+ try {
+ StringTokenizer tk = new StringTokenizer(leading, " ,");
+ String v = tk.nextToken();
+ float v1 = Float.valueOf(v).floatValue();
+ if (!tk.hasMoreTokens()) {
+ p.setLeading(v1, 0);
+ return;
+ }
+ v = tk.nextToken();
+ float v2 = Float.valueOf(v).floatValue();
+ p.setLeading(v1, v2);
+ }
+ catch (Exception e) {
+ p.setLeading(0, 1.5f);
+ }
+
+ }
+
+ public static Paragraph createParagraph(HashMap props) {
+ Paragraph p = new Paragraph();
+ String value = (String)props.get("align");
+ if (value != null) {
+ if (value.equalsIgnoreCase("center"))
+ p.setAlignment(Element.ALIGN_CENTER);
+ else if (value.equalsIgnoreCase("right"))
+ p.setAlignment(Element.ALIGN_RIGHT);
+ else if (value.equalsIgnoreCase("justify"))
+ p.setAlignment(Element.ALIGN_JUSTIFIED);
+ }
+ setParagraphLeading(p, (String)props.get("leading"));
+ return p;
+ }
+
+ public static void createParagraph(Paragraph p, ChainedProperties props) {
+ String value = props.getProperty("align");
+ if (value != null) {
+ if (value.equalsIgnoreCase("center"))
+ p.setAlignment(Element.ALIGN_CENTER);
+ else if (value.equalsIgnoreCase("right"))
+ p.setAlignment(Element.ALIGN_RIGHT);
+ else if (value.equalsIgnoreCase("justify"))
+ p.setAlignment(Element.ALIGN_JUSTIFIED);
+ }
+ setParagraphLeading(p, props.getProperty("leading"));
+ value = props.getProperty("before");
+ if (value != null) {
+ try {
+ p.setSpacingBefore(Float.valueOf(value).floatValue());
+ }
+ catch (Exception e) {}
+ }
+ value = props.getProperty("after");
+ if (value != null) {
+ try {
+ p.setSpacingAfter(Float.valueOf(value).floatValue());
+ }
+ catch (Exception e) {}
+ }
+ value = props.getProperty("extraparaspace");
+ if (value != null) {
+ try {
+ p.setExtraParagraphSpace(Float.valueOf(value).floatValue());
+ }
+ catch (Exception e) {}
+ }
+ }
+
+ public static Paragraph createParagraph(ChainedProperties props) {
+ Paragraph p = new Paragraph();
+ createParagraph(p, props);
+ return p;
+ }
+
+ public static ListItem createListItem(ChainedProperties props) {
+ ListItem p = new ListItem();
+ createParagraph(p, props);
+ return p;
+ }
+
+ public Font getFont(ChainedProperties props) {
+ String face = props.getProperty("face");
+ if (face != null) {
+ StringTokenizer tok = new StringTokenizer(face, ",");
+ while (tok.hasMoreTokens()) {
+ face = tok.nextToken().trim();
+ if (FontFactory.isRegistered(face))
+ break;
+ }
+ }
+ int style = 0;
+ if (props.hasProperty("i"))
+ style |= Font.ITALIC;
+ if (props.hasProperty("b"))
+ style |= Font.BOLD;
+ if (props.hasProperty("u"))
+ style |= Font.UNDERLINE;
+ String value = props.getProperty("size");
+ float size = 12;
+ if (value != null)
+ size = Float.valueOf(value).floatValue();
+ Color color = decodeColor(props.getProperty("color"));
+ String encoding = props.getProperty("encoding");
+ if (encoding == null)
+ encoding = BaseFont.WINANSI;
+ FontFactoryImp ff = fontImp;
+ if (ff == null)
+ ff = FontFactory.getFontImp();
+ return ff.getFont(face, encoding, true, size, style, color);
+ }
+
+ public static Color decodeColor(String s) {
+ if (s == null)
+ return null;
+ s = s.toLowerCase().trim();
+ Color c = (Color)colorTable.get(s);
+ if (c != null)
+ return c;
+ try {
+ if (s.startsWith("#"))
+ return new Color(Integer.parseInt(s.substring(1), 16));
+ }
+ catch (Exception e) {
+ }
+ return null;
+ }
+
+ public FontFactoryImp getFontImp() {
+ return fontImp;
+ }
+
+ public void setFontImp(FontFactoryImp fontImp) {
+ this.fontImp = fontImp;
+ }
+
+ public static HashMap colorTable = new HashMap();
+ public static HashMap followTags = new HashMap();
+ static {
+ followTags.put("i", "i");
+ followTags.put("b", "b");
+ followTags.put("u", "u");
+ followTags.put("sub", "sub");
+ followTags.put("sup", "sup");
+ followTags.put("em", "i");
+ followTags.put("strong", "b");
+
+ colorTable.put("black", new Color(0x000000));
+ colorTable.put("green", new Color(0x008000));
+ colorTable.put("silver", new Color(0xC0C0C0));
+ colorTable.put("lime", new Color(0x00FF00));
+ colorTable.put("gray", new Color(0x808080));
+ colorTable.put("olive", new Color(0x808000));
+ colorTable.put("white", new Color(0xFFFFFF));
+ colorTable.put("yellow", new Color(0xFFFF00));
+ colorTable.put("maroon", new Color(0x800000));
+ colorTable.put("navy", new Color(0x000080));
+ colorTable.put("red", new Color(0xFF0000));
+ colorTable.put("blue", new Color(0x0000FF));
+ colorTable.put("purple", new Color(0x800080));
+ colorTable.put("teal", new Color(0x008080));
+ colorTable.put("fuchsia", new Color(0xFF00FF));
+ colorTable.put("aqua", new Color(0x00FFFF));
+ }
+}
diff --git a/src/main/java/com/lowagie/text/html/simpleparser/HTMLWorker.java b/src/main/java/com/lowagie/text/html/simpleparser/HTMLWorker.java
new file mode 100644
index 0000000..b28fe04
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/simpleparser/HTMLWorker.java
@@ -0,0 +1,623 @@
+/*
+ * Copyright 2004 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html.simpleparser;
+
+import com.lowagie.text.*;
+import com.lowagie.text.pdf.*;
+import java.util.Stack;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.File;
+
+public class HTMLWorker implements SimpleXMLDocHandler, DocListener {
+
+ protected ArrayList objectList;
+ protected DocListener document;
+ private Paragraph currentParagraph;
+ private ChainedProperties cprops = new ChainedProperties();
+ private Stack stack = new Stack();
+ private boolean pendingTR = false;
+ private boolean pendingTD = false;
+ private boolean pendingLI = false;
+ private StyleSheet style = new StyleSheet();
+ private boolean isPRE = false;
+ private Stack tableState = new Stack();
+ private boolean skipText = false;
+ private HashMap interfaceProps;
+ private FactoryProperties factoryProperties = new FactoryProperties();
+
+ /** Creates a new instance of HTMLWorker */
+ public HTMLWorker(DocListener document) {
+ this.document = document;
+ }
+
+ public void setStyleSheet(StyleSheet style) {
+ this.style = style;
+ }
+
+ public StyleSheet getStyleSheet() {
+ return style;
+ }
+
+ public void setInterfaceProps(HashMap interfaceProps) {
+ this.interfaceProps = interfaceProps;
+ FontFactoryImp ff = null;
+ if (interfaceProps != null)
+ ff = (FontFactoryImp)interfaceProps.get("font_factory");
+ factoryProperties.setFontImp(ff);
+ }
+
+ public HashMap getInterfaceProps() {
+ return interfaceProps;
+ }
+
+ public void parse(Reader reader) throws IOException {
+ SimpleXMLParser.parse(this, null, reader, true);
+ }
+
+ public static ArrayList parseToList(Reader reader, StyleSheet style) throws IOException {
+ return parseToList(reader, style, null);
+ }
+
+ public static ArrayList parseToList(Reader reader, StyleSheet style, HashMap interfaceProps) throws IOException {
+ HTMLWorker worker = new HTMLWorker(null);
+ if (style != null)
+ worker.style = style;
+ worker.document = worker;
+ worker.setInterfaceProps(interfaceProps);
+ worker.objectList = new ArrayList();
+ worker.parse(reader);
+ return worker.objectList;
+ }
+
+ public void endDocument() {
+ try {
+ for (int k = 0; k < stack.size(); ++k)
+ document.add((Element)stack.elementAt(k));
+ if (currentParagraph != null)
+ document.add(currentParagraph);
+ currentParagraph = null;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ public void startDocument() {
+ HashMap h = new HashMap();
+ style.applyStyle("body", h);
+ cprops.addToChain("body", h);
+ }
+
+ public void startElement(String tag, HashMap h) {
+ if (!tagsSupported.containsKey(tag))
+ return;
+ try {
+ style.applyStyle(tag, h);
+ String follow = (String)FactoryProperties.followTags.get(tag);
+ if (follow != null) {
+ HashMap prop = new HashMap();
+ prop.put(follow, null);
+ cprops.addToChain(follow, prop);
+ return;
+ }
+ if (tag.equals("a")) {
+ cprops.addToChain(tag, h);
+ if (currentParagraph == null)
+ currentParagraph = new Paragraph();
+ stack.push(currentParagraph);
+ currentParagraph = new Paragraph();
+ return;
+ }
+ if (tag.equals("br")) {
+ if (currentParagraph == null)
+ currentParagraph = new Paragraph();
+ currentParagraph.add(factoryProperties.createChunk("\n", cprops));
+ return;
+ }
+ if (tag.equals("font") || tag.equals("span")) {
+ cprops.addToChain(tag, h);
+ return;
+ }
+ if (tag.equals("img")) {
+ String src = (String)h.get("src");
+ if (src == null)
+ return;
+ cprops.addToChain(tag, h);
+ Image img = null;
+ if (interfaceProps != null) {
+ HashMap images = (HashMap)interfaceProps.get("img_static");
+ if (images != null) {
+ Image tim = (Image)images.get(src);
+ if (tim != null)
+ img = Image.getInstance(tim);
+ } else {
+ if (!src.startsWith("http")) { // relative src references only
+ String baseurl = (String)interfaceProps.get("img_baseurl");
+ if (baseurl != null) {
+ src = baseurl+src;
+ img = Image.getInstance(src);
+ }
+ }
+ }
+ }
+ if (img == null) {
+ if (!src.startsWith("http")) {
+ String path = cprops.getProperty("image_path");
+ if (path == null)
+ path = "";
+ src = new File(path, src).getPath();
+ }
+ img = Image.getInstance(src);
+ }
+ String align = (String)h.get("align");
+ String width = (String)h.get("width");
+ String height = (String)h.get("height");
+ String before = cprops.getProperty("before");
+ String after = cprops.getProperty("after");
+ if (before != null)
+ img.setSpacingBefore(Float.valueOf(before).floatValue());
+ if (after != null)
+ img.setSpacingAfter(Float.valueOf(after).floatValue());
+ float wp = lengthParse(width, (int)img.width());
+ float lp = lengthParse(height, (int)img.height());
+ if (wp > 0 && lp > 0)
+ img.scalePercent(wp > lp ? lp : wp);
+ else if (wp > 0)
+ img.scalePercent(wp);
+ else if (lp > 0)
+ img.scalePercent(lp);
+ img.setWidthPercentage(0);
+ if (align != null) {
+ endElement("p");
+ int ralign = Image.MIDDLE;
+ if (align.equalsIgnoreCase("left"))
+ ralign = Image.LEFT;
+ else if (align.equalsIgnoreCase("right"))
+ ralign = Image.RIGHT;
+ img.setAlignment(ralign);
+ Img i = null;
+ boolean skip = false;
+ if (interfaceProps != null) {
+ i = (Img)interfaceProps.get("img_interface");
+ if (i != null)
+ skip = i.process(img, h, cprops, document);
+ }
+ if (!skip)
+ document.add(img);
+ cprops.removeChain(tag);
+ }
+ else {
+ cprops.removeChain(tag);
+ if (currentParagraph == null)
+ currentParagraph = FactoryProperties.createParagraph(cprops);
+ currentParagraph.add(new Chunk(img, 0, 0));
+ }
+ return;
+ }
+ endElement("p");
+ if (tag.equals("h1") || tag.equals("h2") || tag.equals("h3") || tag.equals("h4") || tag.equals("h5") || tag.equals("h6")) {
+ if (!h.containsKey("size"))
+ h.put("size", tag.substring(1));
+ cprops.addToChain(tag, h);
+ return;
+ }
+ if (tag.equals("ul")) {
+ if (pendingLI)
+ endElement("li");
+ skipText = true;
+ cprops.addToChain(tag, h);
+ com.lowagie.text.List list = new com.lowagie.text.List(false, 10);
+ list.setListSymbol("\u2022");
+ stack.push(list);
+ return;
+ }
+ if (tag.equals("ol")) {
+ if (pendingLI)
+ endElement("li");
+ skipText = true;
+ cprops.addToChain(tag, h);
+ com.lowagie.text.List list = new com.lowagie.text.List(true, 10);
+ stack.push(list);
+ return;
+ }
+ if (tag.equals("li")) {
+ if (pendingLI)
+ endElement("li");
+ skipText = false;
+ pendingLI = true;
+ cprops.addToChain(tag, h);
+ stack.push(FactoryProperties.createListItem(cprops));
+ return;
+ }
+ if (tag.equals("div") || tag.equals("body")) {
+ cprops.addToChain(tag, h);
+ return;
+ }
+ if (tag.equals("pre")) {
+ if (!h.containsKey("face")) {
+ h.put("face", "Courier");
+ }
+ cprops.addToChain(tag, h);
+ isPRE = true;
+ return;
+ }
+ if (tag.equals("p")) {
+ cprops.addToChain(tag, h);
+ currentParagraph = FactoryProperties.createParagraph(h);
+ return;
+ }
+ if (tag.equals("tr")) {
+ if (pendingTR)
+ endElement("tr");
+ skipText = true;
+ pendingTR = true;
+ cprops.addToChain("tr", h);
+ return;
+ }
+ if (tag.equals("td") || tag.equals("th")) {
+ if (pendingTD)
+ endElement(tag);
+ skipText = false;
+ pendingTD = true;
+ cprops.addToChain("td", h);
+ stack.push(new IncCell(tag, cprops));
+ return;
+ }
+ if (tag.equals("table")) {
+ cprops.addToChain("table", h);
+ IncTable table = new IncTable(h);
+ stack.push(table);
+ tableState.push(new boolean[]{pendingTR, pendingTD});
+ pendingTR = pendingTD = false;
+ skipText = true;
+ return;
+ }
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ public void endElement(String tag) {
+ if (!tagsSupported.containsKey(tag))
+ return;
+ try {
+ String follow = (String)FactoryProperties.followTags.get(tag);
+ if (follow != null) {
+ cprops.removeChain(follow);
+ return;
+ }
+ if (tag.equals("font") || tag.equals("span")) {
+ cprops.removeChain(tag);
+ return;
+ }
+ if (tag.equals("a")) {
+ if (currentParagraph == null)
+ currentParagraph = new Paragraph();
+ ALink i = null;
+ boolean skip = false;
+ if (interfaceProps != null) {
+ i = (ALink)interfaceProps.get("alink_interface");
+ if (i != null)
+ skip = i.process(currentParagraph, cprops);
+ }
+ if (!skip) {
+ String href = cprops.getProperty("href");
+ if (href != null) {
+ ArrayList chunks = currentParagraph.getChunks();
+ for (int k = 0; k < chunks.size(); ++k) {
+ Chunk ck = (Chunk)chunks.get(k);
+ ck.setAnchor(href);
+ }
+ }
+ }
+ Paragraph tmp = (Paragraph)stack.pop();
+ Phrase tmp2 = new Phrase();
+ tmp2.add(currentParagraph);
+ tmp.add(tmp2);
+ currentParagraph = tmp;
+ cprops.removeChain("a");
+ return;
+ }
+ if (tag.equals("br")) {
+ return;
+ }
+ if (currentParagraph != null) {
+ if (stack.empty())
+ document.add(currentParagraph);
+ else {
+ Object obj = stack.pop();
+ if (obj instanceof TextElementArray) {
+ TextElementArray current = (TextElementArray)obj;
+ current.add(currentParagraph);
+ }
+ stack.push(obj);
+ }
+ }
+ currentParagraph = null;
+ if (tag.equals("ul") || tag.equals("ol")) {
+ if (pendingLI)
+ endElement("li");
+ skipText = false;
+ cprops.removeChain(tag);
+ if (stack.empty())
+ return;
+ Object obj = stack.pop();
+ if (!(obj instanceof com.lowagie.text.List)) {
+ stack.push(obj);
+ return;
+ }
+ if (stack.empty())
+ document.add((Element)obj);
+ else
+ ((TextElementArray)stack.peek()).add(obj);
+ return;
+ }
+ if (tag.equals("li")) {
+ pendingLI = false;
+ skipText = true;
+ cprops.removeChain(tag);
+ if (stack.empty())
+ return;
+ Object obj = stack.pop();
+ if (!(obj instanceof ListItem)) {
+ stack.push(obj);
+ return;
+ }
+ if (stack.empty()) {
+ document.add((Element)obj);
+ return;
+ }
+ Object list = stack.pop();
+ if (!(list instanceof com.lowagie.text.List)) {
+ stack.push(list);
+ return;
+ }
+ ListItem item = (ListItem)obj;
+ ((com.lowagie.text.List)list).add(item);
+ ArrayList cks = item.getChunks();
+ if (cks.size() > 0)
+ item.listSymbol().setFont(((Chunk)cks.get(0)).font());
+ stack.push(list);
+ return;
+ }
+ if (tag.equals("div") || tag.equals("body")) {
+ cprops.removeChain(tag);
+ return;
+ }
+ if (tag.equals("pre")) {
+ cprops.removeChain(tag);
+ isPRE = false;
+ return;
+ }
+ if (tag.equals("p")) {
+ cprops.removeChain(tag);
+ return;
+ }
+ if (tag.equals("h1") || tag.equals("h2") || tag.equals("h3") || tag.equals("h4") || tag.equals("h5") || tag.equals("h6")) {
+ cprops.removeChain(tag);
+ return;
+ }
+ if (tag.equals("table")) {
+ if (pendingTR)
+ endElement("tr");
+ cprops.removeChain("table");
+ IncTable table = (IncTable) stack.pop();
+ PdfPTable tb = table.buildTable();
+ tb.setSplitRows(true);
+ if (stack.empty())
+ document.add(tb);
+ else
+ ((TextElementArray)stack.peek()).add(tb);
+ boolean state[] = (boolean[])tableState.pop();
+ pendingTR = state[0];
+ pendingTD = state[1];
+ skipText = false;
+ return;
+ }
+ if (tag.equals("tr")) {
+ if (pendingTD)
+ endElement("td");
+ pendingTR = false;
+ cprops.removeChain("tr");
+ ArrayList cells = new ArrayList();
+ IncTable table = null;
+ while (true) {
+ Object obj = stack.pop();
+ if (obj instanceof IncCell) {
+ cells.add(((IncCell)obj).getCell());
+ }
+ if (obj instanceof IncTable) {
+ table = (IncTable)obj;
+ break;
+ }
+ }
+ table.addCols(cells);
+ table.endRow();
+ stack.push(table);
+ skipText = true;
+ return;
+ }
+ if (tag.equals("td") || tag.equals("th")) {
+ pendingTD = false;
+ cprops.removeChain("td");
+ skipText = true;
+ return;
+ }
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ public void text(String str) {
+ if (skipText)
+ return;
+ String content = str;
+ if (isPRE) {
+ if (currentParagraph == null)
+ currentParagraph = new Paragraph();
+ currentParagraph.add(factoryProperties.createChunk(content, cprops));
+ return;
+ }
+ if (content.trim().length() == 0 && content.indexOf(' ') < 0) {
+ return;
+ }
+
+ StringBuffer buf = new StringBuffer();
+ int len = content.length();
+ char character;
+ boolean newline = false;
+ for (int i = 0; i < len; i++) {
+ switch(character = content.charAt(i)) {
+ case ' ':
+ if (!newline) {
+ buf.append(character);
+ }
+ break;
+ case '\n':
+ if (i > 0) {
+ newline = true;
+ buf.append(' ');
+ }
+ break;
+ case '\r':
+ break;
+ case '\t':
+ break;
+ default:
+ newline = false;
+ buf.append(character);
+ }
+ }
+ if (currentParagraph == null)
+ currentParagraph = FactoryProperties.createParagraph(cprops);
+ currentParagraph.add(factoryProperties.createChunk(buf.toString(), cprops));
+ }
+
+ public boolean add(Element element) throws DocumentException {
+ objectList.add(element);
+ return true;
+ }
+
+ public boolean add(Watermark watermark) {
+ return true;
+ }
+
+ public void clearTextWrap() throws DocumentException {
+ }
+
+ public void close() {
+ }
+
+ public boolean newPage() throws DocumentException {
+ return true;
+ }
+
+ public void open() {
+ }
+
+ public void removeWatermark() {
+ }
+
+ public void resetFooter() {
+ }
+
+ public void resetHeader() {
+ }
+
+ public void resetPageCount() {
+ }
+
+ public void setFooter(HeaderFooter footer) {
+ }
+
+ public void setHeader(HeaderFooter header) {
+ }
+
+ public boolean setMarginMirroring(boolean marginMirroring) {
+ return true;
+ }
+
+ public boolean setMargins(float marginLeft, float marginRight, float marginTop, float marginBottom) {
+ return true;
+ }
+
+ public void setPageCount(int pageN) {
+ }
+
+ public boolean setPageSize(Rectangle pageSize) {
+ return true;
+ }
+
+ public static final String tagsSupportedString = "ol ul li a pre font span br p div body table td th tr i b u sub sup em strong"
+ + " h1 h2 h3 h4 h5 h6 img";
+
+ public static final HashMap tagsSupported = new HashMap();
+
+ static {
+ StringTokenizer tok = new StringTokenizer(tagsSupportedString);
+ while (tok.hasMoreTokens())
+ tagsSupported.put(tok.nextToken(), null);
+ }
+
+ private static float lengthParse(String txt, int c) {
+ if (txt == null)
+ return -1;
+ if (txt.endsWith("%")) {
+ float vf = Float.valueOf(txt.substring(0, txt.length() - 1)).floatValue();
+ return vf;
+ }
+ int v = Integer.parseInt(txt);
+ return (float)v / c * 100f;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/html/simpleparser/Img.java b/src/main/java/com/lowagie/text/html/simpleparser/Img.java
new file mode 100644
index 0000000..a7c066c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/simpleparser/Img.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2005 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html.simpleparser;
+
+import com.lowagie.text.Image;
+import com.lowagie.text.DocListener;
+import java.util.HashMap;
+
+/**
+ *
+ * @author psoares
+ */
+public interface Img {
+ boolean process(Image img, HashMap h, ChainedProperties cprops, DocListener doc);
+}
diff --git a/src/main/java/com/lowagie/text/html/simpleparser/IncCell.java b/src/main/java/com/lowagie/text/html/simpleparser/IncCell.java
new file mode 100644
index 0000000..3f25e0c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/simpleparser/IncCell.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2004 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html.simpleparser;
+
+import com.lowagie.text.*;
+import com.lowagie.text.pdf.*;
+import java.util.ArrayList;
+/**
+ *
+ * @author psoares
+ */
+public class IncCell implements TextElementArray {
+
+ private ArrayList chunks = new ArrayList();
+ private PdfPCell cell;
+
+ /** Creates a new instance of IncCell */
+ public IncCell(String tag, ChainedProperties props) {
+ cell = new PdfPCell((Phrase)null);
+ String value = props.getProperty("colspan");
+ if (value != null)
+ cell.setColspan(Integer.parseInt(value));
+ value = props.getProperty("align");
+ if (tag.equals("th"))
+ cell.setHorizontalAlignment(Element.ALIGN_CENTER);
+ if (value != null) {
+ if ("center".equalsIgnoreCase(value))
+ cell.setHorizontalAlignment(Element.ALIGN_CENTER);
+ else if ("right".equalsIgnoreCase(value))
+ cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
+ else if ("left".equalsIgnoreCase(value))
+ cell.setHorizontalAlignment(Element.ALIGN_LEFT);
+ else if ("justify".equalsIgnoreCase(value))
+ cell.setHorizontalAlignment(Element.ALIGN_JUSTIFIED);
+ }
+ value = props.getProperty("valign");
+ cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
+ if (value != null) {
+ if ("top".equalsIgnoreCase(value))
+ cell.setVerticalAlignment(Element.ALIGN_TOP);
+ else if ("bottom".equalsIgnoreCase(value))
+ cell.setVerticalAlignment(Element.ALIGN_BOTTOM);
+ }
+ value = props.getProperty("border");
+ float border = 0;
+ if (value != null)
+ border = Float.valueOf(value).floatValue();
+ cell.setBorderWidth(border);
+ value = props.getProperty("cellpadding");
+ if (value != null)
+ cell.setPadding(Float.valueOf(value).floatValue());
+ cell.setUseDescender(true);
+ value = props.getProperty("bgcolor");
+ cell.setBackgroundColor(FactoryProperties.decodeColor(value));
+ }
+
+ public boolean add(Object o) {
+ if (!(o instanceof Element))
+ return false;
+ cell.addElement((Element)o);
+ return true;
+ }
+
+ public ArrayList getChunks() {
+ return chunks;
+ }
+
+ public boolean process(ElementListener listener) {
+ return true;
+ }
+
+ public int type() {
+ return 0;
+ }
+
+ public PdfPCell getCell() {
+ return cell;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/html/simpleparser/IncTable.java b/src/main/java/com/lowagie/text/html/simpleparser/IncTable.java
new file mode 100644
index 0000000..92b569b
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/simpleparser/IncTable.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2004 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html.simpleparser;
+
+import com.lowagie.text.pdf.*;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ *
+ * @author psoares
+ */
+public class IncTable {
+ private HashMap props = new HashMap();
+ private ArrayList rows = new ArrayList();
+ private ArrayList cols;
+ /** Creates a new instance of IncTable */
+ public IncTable(HashMap props) {
+ this.props.putAll(props);
+ }
+
+ public void addCol(PdfPCell cell) {
+ if (cols == null)
+ cols = new ArrayList();
+ cols.add(cell);
+ }
+
+ public void addCols(ArrayList ncols) {
+ if (cols == null)
+ cols = new ArrayList(ncols);
+ else
+ cols.addAll(ncols);
+ }
+
+ public void endRow() {
+ if (cols != null) {
+ Collections.reverse(cols);
+ rows.add(cols);
+ cols = null;
+ }
+ }
+
+ public ArrayList getRows() {
+ return rows;
+ }
+
+ public PdfPTable buildTable() {
+ if (rows.size() == 0)
+ return new PdfPTable(1);
+ int ncol = 0;
+ ArrayList c0 = (ArrayList)rows.get(0);
+ for (int k = 0; k < c0.size(); ++k) {
+ ncol += ((PdfPCell)c0.get(k)).getColspan();
+ }
+ PdfPTable table = new PdfPTable(ncol);
+ String width = (String)props.get("width");
+ if (width == null)
+ table.setWidthPercentage(100);
+ else {
+ if (width.endsWith("%"))
+ table.setWidthPercentage(Float.valueOf(width.substring(0, width.length() - 1)).floatValue());
+ else {
+ table.setTotalWidth(Float.valueOf(width).floatValue());
+ table.setLockedWidth(true);
+ }
+ }
+ for (int row = 0; row < rows.size(); ++row) {
+ ArrayList col = (ArrayList)rows.get(row);
+ for (int k = 0; k < col.size(); ++k) {
+ table.addCell((PdfPCell)col.get(k));
+ }
+ }
+ return table;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/html/simpleparser/StyleSheet.java b/src/main/java/com/lowagie/text/html/simpleparser/StyleSheet.java
new file mode 100644
index 0000000..a659715
--- /dev/null
+++ b/src/main/java/com/lowagie/text/html/simpleparser/StyleSheet.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2004 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.html.simpleparser;
+
+import java.util.HashMap;
+
+public class StyleSheet {
+
+ public HashMap classMap = new HashMap();
+ public HashMap tagMap = new HashMap();
+
+ /** Creates a new instance of StyleSheet */
+ public StyleSheet() {
+ }
+
+ public void applyStyle(String tag, HashMap props) {
+ HashMap map = (HashMap)tagMap.get(tag.toLowerCase());
+ if (map != null) {
+ HashMap temp = new HashMap(map);
+ temp.putAll(props);
+ props.putAll(temp);
+ }
+ String cm = (String)props.get("class");
+ if (cm == null)
+ return;
+ map = (HashMap)classMap.get(cm.toLowerCase());
+ if (map == null)
+ return;
+ props.remove("class");
+ HashMap temp = new HashMap(map);
+ temp.putAll(props);
+ props.putAll(temp);
+ }
+
+ private void applyMap(HashMap map, HashMap props) {
+
+ }
+
+ public void loadStyle(String style, HashMap props) {
+ classMap.put(style.toLowerCase(), props);
+ }
+
+ public void loadStyle(String style, String key, String value) {
+ style = style.toLowerCase();
+ HashMap props = (HashMap)classMap.get(style);
+ if (props == null) {
+ props = new HashMap();
+ classMap.put(style, props);
+ }
+ props.put(key, value);
+ }
+
+ public void loadTagStyle(String tag, HashMap props) {
+ tagMap.put(tag.toLowerCase(), props);
+ }
+
+ public void loadTagStyle(String tag, String key, String value) {
+ tag = tag.toLowerCase();
+ HashMap props = (HashMap)tagMap.get(tag);
+ if (props == null) {
+ props = new HashMap();
+ tagMap.put(tag, props);
+ }
+ props.put(key, value);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/markup/MarkupParser.java b/src/main/java/com/lowagie/text/markup/MarkupParser.java
new file mode 100644
index 0000000..d2446d3
--- /dev/null
+++ b/src/main/java/com/lowagie/text/markup/MarkupParser.java
@@ -0,0 +1,736 @@
+/*
+ * $Id: MarkupParser.java,v 1.56 2005/07/07 10:04:49 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.markup;
+
+import java.awt.Color;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import com.lowagie.text.Element;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Font;
+import com.lowagie.text.FontFactory;
+import com.lowagie.text.ListItem;
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.SimpleCell;
+import com.lowagie.text.SimpleTable;
+
+/**
+ * This class is a HashMap that contains selectors (String) and styles (a
+ * Properties object). Given a tag and its attributes (id/class), this class can
+ * return an iText object with the according style.
+ *
+ * @author blowagie
+ */
+public class MarkupParser extends HashMap {
+ /**
+ * HashMap with styles for each known combination of tag/id/class. The key
+ * is a String-combination, the value a Properties object.
+ */
+ protected HashMap stylecache = new HashMap();
+
+ /**
+ * HashMap with fonts for each known combination of tag/id/class. The key is
+ * the same String-combination used for the stylecache.
+ */
+ protected HashMap fontcache = new HashMap();
+
+ // processing CSS
+
+ /**
+ * Creates new MarkupParser
+ *
+ * @param file
+ * the path to a CSS file.
+ */
+ public MarkupParser(String file) {
+ super();
+ try {
+ FileReader reader = new FileReader(file);
+ BufferedReader br = new BufferedReader(reader);
+ StringBuffer buf = new StringBuffer();
+ String line;
+ while ((line = br.readLine()) != null) {
+ buf.append(line.trim());
+ }
+ String string = buf.toString();
+ string = removeComment(string, "/*", "*/");
+ StringTokenizer tokenizer = new StringTokenizer(string, "}");
+ String tmp;
+ int pos;
+ String selector;
+ String attributes;
+ while (tokenizer.hasMoreTokens()) {
+ tmp = tokenizer.nextToken();
+ pos = tmp.indexOf("{");
+ if (pos > 0) {
+ selector = tmp.substring(0, pos).trim();
+ attributes = tmp.substring(pos + 1).trim();
+ if (attributes.endsWith("}"))
+ attributes = attributes.substring(0, attributes
+ .length() - 1);
+ put(selector, parseAttributes(attributes));
+ }
+ }
+ } catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Removes the comments sections of a String.
+ *
+ * @param string
+ * the original String
+ * @param startComment
+ * the String that marks the start of a Comment section
+ * @param endComment
+ * the String that marks the end of a Comment section.
+ * @return the String stripped of its comment section
+ */
+ public static String removeComment(String string, String startComment,
+ String endComment) {
+ StringBuffer result = new StringBuffer();
+ int pos = 0;
+ int end = endComment.length();
+ int start = string.indexOf(startComment, pos);
+ while (start > -1) {
+ result.append(string.substring(pos, start));
+ pos = string.indexOf(endComment, start) + end;
+ start = string.indexOf(startComment, pos);
+ }
+ result.append(string.substring(pos));
+ return result.toString();
+ }
+
+ /**
+ * This method parses a String with attributes and returns a Properties
+ * object.
+ *
+ * @param string
+ * a String of this form: 'key1="value1"; key2="value2";...
+ * keyN="valueN" '
+ * @return a Properties object
+ */
+ public static Properties parseAttributes(String string) {
+ Properties result = new Properties();
+ if (string == null)
+ return result;
+ StringTokenizer keyValuePairs = new StringTokenizer(string, ";");
+ StringTokenizer keyValuePair;
+ String key;
+ String value;
+ while (keyValuePairs.hasMoreTokens()) {
+ keyValuePair = new StringTokenizer(keyValuePairs.nextToken(), ":");
+ if (keyValuePair.hasMoreTokens())
+ key = keyValuePair.nextToken().trim();
+ else
+ continue;
+ if (keyValuePair.hasMoreTokens())
+ value = keyValuePair.nextToken().trim();
+ else
+ continue;
+ if (value.startsWith("\""))
+ value = value.substring(1);
+ if (value.endsWith("\""))
+ value = value.substring(0, value.length() - 1);
+ result.setProperty(key, value);
+ }
+ return result;
+ }
+
+ // reading attributevalues
+
+ /**
+ * Parses a length.
+ *
+ * @param string
+ * a length in the form of an optional + or -, followed by a
+ * number and a unit.
+ * @return a float
+ */
+
+ public static float parseLength(String string) {
+ int pos = 0;
+ int length = string.length();
+ boolean ok = true;
+ while (ok && pos < length) {
+ switch (string.charAt(pos)) {
+ case '+':
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '.':
+ pos++;
+ break;
+ default:
+ ok = false;
+ }
+ }
+ if (pos == 0)
+ return 0f;
+ if (pos == length)
+ return Float.valueOf(string + "f").floatValue();
+ float f = Float.valueOf(string.substring(0, pos) + "f").floatValue();
+ string = string.substring(pos);
+ // inches
+ if (string.startsWith("in")) {
+ return f * 72f;
+ }
+ // centimeters
+ if (string.startsWith("cm")) {
+ return (f / 2.54f) * 72f;
+ }
+ // millimeters
+ if (string.startsWith("mm")) {
+ return (f / 25.4f) * 72f;
+ }
+ // picas
+ if (string.startsWith("pc")) {
+ return f * 12f;
+ }
+ // default: we assume the length was measured in points
+ return f;
+ }
+
+ /**
+ * Converts a Color
into a HTML representation of this
+ * Color
.
+ *
+ * @param color
+ * the Color
that has to be converted.
+ * @return the HTML representation of this null
if the field does not exist
+ */
+ public String[] getAppearanceStates(String fieldName) {
+ Item fd = (Item)fields.get(fieldName);
+ if (fd == null)
+ return null;
+ HashMap names = new HashMap();
+ PdfDictionary vals = (PdfDictionary)fd.values.get(0);
+ PdfObject opts = PdfReader.getPdfObject(vals.get(PdfName.OPT));
+ if (opts != null) {
+ if (opts.isString())
+ names.put(((PdfString)opts).toUnicodeString(), null);
+ else if (opts.isArray()) {
+ ArrayList list = ((PdfArray)opts).getArrayList();
+ for (int k = 0; k < list.size(); ++k) {
+ PdfObject v = PdfReader.getPdfObject((PdfObject)list.get(k));
+ if (v != null && v.isString())
+ names.put(((PdfString)v).toUnicodeString(), null);
+ }
+ }
+ }
+ ArrayList wd = fd.widgets;
+ for (int k = 0; k < wd.size(); ++k) {
+ PdfDictionary dic = (PdfDictionary)wd.get(k);
+ dic = (PdfDictionary)PdfReader.getPdfObject(dic.get(PdfName.AP));
+ if (dic == null)
+ continue;
+ PdfObject ob = PdfReader.getPdfObject(dic.get(PdfName.N));
+ if (ob == null || !ob.isDictionary())
+ continue;
+ dic = (PdfDictionary)ob;
+ for (Iterator it = dic.getKeys().iterator(); it.hasNext();) {
+ String name = PdfName.decodeName(((PdfName)it.next()).toString());
+ names.put(name, null);
+ }
+ }
+ String out[] = new String[names.size()];
+ return (String[])names.keySet().toArray(out);
+ }
+
+ private String[] getListOption(String fieldName, int idx) {
+ Item fd = (Item)fields.get(fieldName);
+ if (fd == null)
+ return null;
+ PdfObject obj = PdfReader.getPdfObject(((PdfDictionary)fd.merged.get(0)).get(PdfName.OPT));
+ if (obj == null || !obj.isArray())
+ return null;
+ PdfArray ar = (PdfArray)obj;
+ String[] ret = new String[ar.size()];
+ ArrayList a = ar.getArrayList();
+ for (int k = 0; k < a.size(); ++k) {
+ obj = PdfReader.getPdfObject((PdfObject)a.get(k));
+ try {
+ if (obj.isArray()) {
+ obj = (PdfObject)((PdfArray)obj).getArrayList().get(idx);
+ }
+ if (obj.isString())
+ ret[k] = ((PdfString)obj).toUnicodeString();
+ else
+ ret[k] = obj.toString();
+ }
+ catch (Exception e) {
+ ret[k] = "";
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Gets the list of export option values from fields of type list or combo.
+ * If the field doesn't exist or the field type is not list or combo it will return
+ * null
.
+ * @param fieldName the field name
+ * @return the list of export option values from fields of type list or combo
+ */
+ public String[] getListOptionExport(String fieldName) {
+ return getListOption(fieldName, 0);
+ }
+
+ /**
+ * Gets the list of display option values from fields of type list or combo.
+ * If the field doesn't exist or the field type is not list or combo it will return
+ * null
.
+ * @param fieldName the field name
+ * @return the list of export option values from fields of type list or combo
+ */
+ public String[] getListOptionDisplay(String fieldName) {
+ return getListOption(fieldName, 1);
+ }
+
+ /**
+ * Sets the option list for fields of type list or combo. One of exportValues
+ * or displayValues
may be null
but not both. This method will only
+ * set the list but will not set the value or appearance. For that, calling setField()
+ * is required.
+ *
+ * PdfReader pdf = new PdfReader("input.pdf");
+ * PdfStamper stp = new PdfStamper(pdf, new FileOutputStream("output.pdf"));
+ * AcroFields af = stp.getAcroFields();
+ * af.setListOption("ComboBox", new String[]{"a", "b", "c"}, new String[]{"first", "second", "third"});
+ * af.setField("ComboBox", "b");
+ * stp.close();
+ *
+ * @param fieldName the field name
+ * @param exportValues the export values
+ * @param displayValues the display values
+ * @return true
if the operation succeeded, false
otherwise
+ */
+ public boolean setListOption(String fieldName, String[] exportValues, String[] displayValues) {
+ if (exportValues == null && displayValues == null)
+ return false;
+ if (exportValues != null && displayValues != null && exportValues.length != displayValues.length)
+ throw new IllegalArgumentException("The export and the display array must have the same size.");
+ int ftype = getFieldType(fieldName);
+ if (ftype != FIELD_TYPE_COMBO && ftype != FIELD_TYPE_LIST)
+ return false;
+ Item fd = (Item)fields.get(fieldName);
+ String[] sing = null;
+ if (exportValues == null && displayValues != null)
+ sing = displayValues;
+ else if (exportValues != null && displayValues == null)
+ sing = exportValues;
+ PdfArray opt = new PdfArray();
+ if (sing != null) {
+ for (int k = 0; k < sing.length; ++k)
+ opt.add(new PdfString(sing[k], PdfObject.TEXT_UNICODE));
+ }
+ else {
+ for (int k = 0; k < exportValues.length; ++k) {
+ PdfArray a = new PdfArray();
+ a.add(new PdfString(exportValues[k], PdfObject.TEXT_UNICODE));
+ a.add(new PdfString(displayValues[k], PdfObject.TEXT_UNICODE));
+ opt.add(a);
+ }
+ }
+ ((PdfDictionary)fd.values.get(0)).put(PdfName.OPT, opt);
+ for (int j = 0; j < fd.merged.size(); ++j)
+ ((PdfDictionary)fd.merged.get(j)).put(PdfName.OPT, opt);
+ return true;
+ }
+
+ /**
+ * Gets the field type. The type can be one of: FIELD_TYPE_PUSHBUTTON
,
+ * FIELD_TYPE_CHECKBOX
, FIELD_TYPE_RADIOBUTTON
,
+ * FIELD_TYPE_TEXT
, FIELD_TYPE_LIST
,
+ * FIELD_TYPE_COMBO
or FIELD_TYPE_SIGNATURE
.
+ * FIELD_TYPE_NONE
.
+ * @param fieldName the field name
+ * @return the field type
+ */
+ public int getFieldType(String fieldName) {
+ Item fd = (Item)fields.get(fieldName);
+ if (fd == null)
+ return FIELD_TYPE_NONE;
+ PdfObject type = PdfReader.getPdfObject(((PdfDictionary)fd.merged.get(0)).get(PdfName.FT));
+ if (type == null)
+ return FIELD_TYPE_NONE;
+ int ff = 0;
+ PdfObject ffo = PdfReader.getPdfObject(((PdfDictionary)fd.merged.get(0)).get(PdfName.FF));
+ if (ffo != null && ffo.type() == PdfObject.NUMBER)
+ ff = ((PdfNumber)ffo).intValue();
+ if (PdfName.BTN.equals(type)) {
+ if ((ff & PdfFormField.FF_PUSHBUTTON) != 0)
+ return FIELD_TYPE_PUSHBUTTON;
+ if ((ff & PdfFormField.FF_RADIO) != 0)
+ return FIELD_TYPE_RADIOBUTTON;
+ else
+ return FIELD_TYPE_CHECKBOX;
+ }
+ else if (PdfName.TX.equals(type)) {
+ return FIELD_TYPE_TEXT;
+ }
+ else if (PdfName.CH.equals(type)) {
+ if ((ff & PdfFormField.FF_COMBO) != 0)
+ return FIELD_TYPE_COMBO;
+ else
+ return FIELD_TYPE_LIST;
+ }
+ else if (PdfName.SIG.equals(type)) {
+ return FIELD_TYPE_SIGNATURE;
+ }
+ return FIELD_TYPE_NONE;
+ }
+
+ /**
+ * Export the fields as a FDF.
+ * @param writer the FDF writer
+ */
+ public void exportAsFdf(FdfWriter writer) {
+ for (Iterator it = fields.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry)it.next();
+ Item item = (Item)entry.getValue();
+ String name = (String)entry.getKey();
+ PdfObject v = PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.V));
+ if (v == null)
+ continue;
+ String value = getField(name);
+ if (lastWasString)
+ writer.setFieldAsString(name, value);
+ else
+ writer.setFieldAsName(name, value);
+ }
+ }
+
+ /**
+ * Renames a field. Only the last part of the name can be renamed. For example,
+ * if the original field is "ab.cd.ef" only the "ef" part can be renamed.
+ * @param oldName the old field name
+ * @param newName the new field name
+ * @return true
if the renaming was successful, false
+ * otherwise
+ */
+ public boolean renameField(String oldName, String newName) {
+ int idx1 = oldName.lastIndexOf('.') + 1;
+ int idx2 = newName.lastIndexOf('.') + 1;
+ if (idx1 != idx2)
+ return false;
+ if (!oldName.substring(0, idx1).equals(newName.substring(0, idx2)))
+ return false;
+ if (fields.containsKey(newName))
+ return false;
+ Item item = (Item)fields.get(oldName);
+ if (item == null)
+ return false;
+ newName = newName.substring(idx2);
+ PdfString ss = new PdfString(newName, PdfObject.TEXT_UNICODE);
+ for (int k = 0; k < item.merged.size(); ++k) {
+ PdfDictionary dic = (PdfDictionary)item.values.get(k);
+ dic.put(PdfName.T, ss);
+ markUsed(dic);
+ dic = (PdfDictionary)item.merged.get(k);
+ dic.put(PdfName.T, ss);
+ }
+ fields.remove(oldName);
+ fields.put(newName, item);
+ return true;
+ }
+
+ static private Object[] splitDAelements(String da) {
+ try {
+ PRTokeniser tk = new PRTokeniser(PdfEncodings.convertToBytes(da, null));
+ ArrayList stack = new ArrayList();
+ Object ret[] = new Object[3];
+ while (tk.nextToken()) {
+ if (tk.getTokenType() == PRTokeniser.TK_COMMENT)
+ continue;
+ if (tk.getTokenType() == PRTokeniser.TK_OTHER) {
+ String operator = tk.getStringValue();
+ if (operator.equals("Tf")) {
+ if (stack.size() >= 2) {
+ ret[DA_FONT] = stack.get(stack.size() - 2);
+ ret[DA_SIZE] = new Float((String)stack.get(stack.size() - 1));
+ }
+ }
+ else if (operator.equals("g")) {
+ if (stack.size() >= 1) {
+ float gray = new Float((String)stack.get(stack.size() - 1)).floatValue();
+ if (gray != 0)
+ ret[DA_COLOR] = new GrayColor(gray);
+ }
+ }
+ else if (operator.equals("rg")) {
+ if (stack.size() >= 3) {
+ float red = new Float((String)stack.get(stack.size() - 3)).floatValue();
+ float green = new Float((String)stack.get(stack.size() - 2)).floatValue();
+ float blue = new Float((String)stack.get(stack.size() - 1)).floatValue();
+ ret[DA_COLOR] = new Color(red, green, blue);
+ }
+ }
+ else if (operator.equals("k")) {
+ if (stack.size() >= 4) {
+ float cyan = new Float((String)stack.get(stack.size() - 4)).floatValue();
+ float magenta = new Float((String)stack.get(stack.size() - 3)).floatValue();
+ float yellow = new Float((String)stack.get(stack.size() - 2)).floatValue();
+ float black = new Float((String)stack.get(stack.size() - 1)).floatValue();
+ ret[DA_COLOR] = new CMYKColor(cyan, magenta, yellow, black);
+ }
+ }
+ stack.clear();
+ }
+ else
+ stack.add(tk.getStringValue());
+ }
+ return ret;
+ }
+ catch (IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+ PdfAppearance getAppearance(PdfDictionary merged, String text, String fieldName) throws IOException, DocumentException {
+ topFirst = 0;
+ int flags = 0;
+ TextField tx = null;
+ if (fieldCache == null || !fieldCache.containsKey(fieldName)) {
+ tx = new TextField(writer, null, null);
+ tx.setExtraMargin(extraMarginLeft, extraMarginTop);
+ tx.setBorderWidth(0);
+ tx.setSubstitutionFonts(substitutionFonts);
+ // the text size and color
+ PdfString da = (PdfString)PdfReader.getPdfObject(merged.get(PdfName.DA));
+ if (da != null) {
+ Object dab[] = splitDAelements(da.toUnicodeString());
+ if (dab[DA_SIZE] != null)
+ tx.setFontSize(((Float)dab[DA_SIZE]).floatValue());
+ if (dab[DA_COLOR] != null)
+ tx.setTextColor((Color)dab[DA_COLOR]);
+ if (dab[DA_FONT] != null) {
+ PdfDictionary font = (PdfDictionary)PdfReader.getPdfObject(merged.get(PdfName.DR));
+ if (font != null) {
+ font = (PdfDictionary)PdfReader.getPdfObject(font.get(PdfName.FONT));
+ if (font != null) {
+ PdfObject po = font.get(new PdfName((String)dab[DA_FONT]));
+ if (po != null && po.type() == PdfObject.INDIRECT) {
+ PRIndirectReference por = (PRIndirectReference)po;
+ BaseFont bp = new DocumentFont((PRIndirectReference)po);
+ tx.setFont(bp);
+ Integer porkey = new Integer(por.getNumber());
+ BaseFont porf = (BaseFont)extensionFonts.get(porkey);
+ if (porf == null) {
+ if (!extensionFonts.containsKey(porkey)) {
+ PdfDictionary fo = (PdfDictionary)PdfReader.getPdfObject(po);
+ PdfDictionary fd = (PdfDictionary)PdfReader.getPdfObject(fo.get(PdfName.FONTDESCRIPTOR));
+ if (fd != null) {
+ PRStream prs = (PRStream)PdfReader.getPdfObject(fd.get(PdfName.FONTFILE2));
+ if (prs == null)
+ prs = (PRStream)PdfReader.getPdfObject(fd.get(PdfName.FONTFILE3));
+ if (prs == null) {
+ extensionFonts.put(porkey, null);
+ }
+ else {
+ try {
+ porf = BaseFont.createFont("font.ttf", BaseFont.IDENTITY_H, true, false, PdfReader.getStreamBytes(prs), null);
+ }
+ catch (Exception e) {
+ porf = null;
+ }
+ extensionFonts.put(porkey, porf);
+ }
+ }
+ }
+ }
+ tx.setExtensionFont(porf);
+ }
+ else {
+ BaseFont bf = (BaseFont)localFonts.get(dab[DA_FONT]);
+ if (bf == null) {
+ String fn[] = (String[])stdFieldFontNames.get(dab[DA_FONT]);
+ if (fn != null) {
+ try {
+ String enc = "winansi";
+ if (fn.length > 1)
+ enc = fn[1];
+ bf = BaseFont.createFont(fn[0], enc, false);
+ tx.setFont(bf);
+ }
+ catch (Exception e) {
+ // empty
+ }
+ }
+ }
+ else
+ tx.setFont(bf);
+ }
+ }
+ }
+ }
+ }
+ //rotation, border and backgound color
+ PdfDictionary mk = (PdfDictionary)PdfReader.getPdfObject(merged.get(PdfName.MK));
+ if (mk != null) {
+ PdfArray ar = (PdfArray)PdfReader.getPdfObject(mk.get(PdfName.BC));
+ Color border = getMKColor(ar);
+ tx.setBorderColor(border);
+ if (border != null)
+ tx.setBorderWidth(1);
+ ar = (PdfArray)PdfReader.getPdfObject(mk.get(PdfName.BG));
+ tx.setBackgroundColor(getMKColor(ar));
+ PdfNumber rotation = (PdfNumber)PdfReader.getPdfObject(mk.get(PdfName.R));
+ if (rotation != null)
+ tx.setRotation(rotation.intValue());
+ }
+ //multiline
+ PdfNumber nfl = (PdfNumber)PdfReader.getPdfObject(merged.get(PdfName.FF));
+ if (nfl != null)
+ flags = nfl.intValue();
+ tx.setOptions(((flags & PdfFormField.FF_MULTILINE) == 0 ? 0 : TextField.MULTILINE) | ((flags & PdfFormField.FF_COMB) == 0 ? 0 : TextField.COMB));
+ if ((flags & PdfFormField.FF_COMB) != 0) {
+ PdfNumber maxLen = (PdfNumber)PdfReader.getPdfObject(merged.get(PdfName.MAXLEN));
+ int len = 0;
+ if (maxLen != null)
+ len = maxLen.intValue();
+ tx.setMaxCharacterLength(len);
+ }
+ //alignment
+ nfl = (PdfNumber)PdfReader.getPdfObject(merged.get(PdfName.Q));
+ if (nfl != null) {
+ if (nfl.intValue() == PdfFormField.Q_CENTER)
+ tx.setAlignment(Element.ALIGN_CENTER);
+ else if (nfl.intValue() == PdfFormField.Q_RIGHT)
+ tx.setAlignment(Element.ALIGN_RIGHT);
+ }
+ //border styles
+ PdfDictionary bs = (PdfDictionary)PdfReader.getPdfObject(merged.get(PdfName.BS));
+ if (bs != null) {
+ PdfNumber w = (PdfNumber)PdfReader.getPdfObject(bs.get(PdfName.W));
+ if (w != null)
+ tx.setBorderWidth(w.floatValue());
+ PdfName s = (PdfName)PdfReader.getPdfObject(bs.get(PdfName.S));
+ if (PdfName.D.equals(s))
+ tx.setBorderStyle(PdfBorderDictionary.STYLE_DASHED);
+ else if (PdfName.B.equals(s))
+ tx.setBorderStyle(PdfBorderDictionary.STYLE_BEVELED);
+ else if (PdfName.I.equals(s))
+ tx.setBorderStyle(PdfBorderDictionary.STYLE_INSET);
+ else if (PdfName.U.equals(s))
+ tx.setBorderStyle(PdfBorderDictionary.STYLE_UNDERLINE);
+ }
+ else {
+ PdfArray bd = (PdfArray)PdfReader.getPdfObject(merged.get(PdfName.BORDER));
+ if (bd != null) {
+ ArrayList ar = bd.getArrayList();
+ if (ar.size() >= 3)
+ tx.setBorderWidth(((PdfNumber)ar.get(2)).floatValue());
+ if (ar.size() >= 4)
+ tx.setBorderStyle(PdfBorderDictionary.STYLE_DASHED);
+ }
+ }
+ //rect
+ PdfArray rect = (PdfArray)PdfReader.getPdfObject(merged.get(PdfName.RECT));
+ Rectangle box = PdfReader.getNormalizedRectangle(rect);
+ if (tx.getRotation() == 90 || tx.getRotation() == 270)
+ box = box.rotate();
+ tx.setBox(box);
+ if (fieldCache != null)
+ fieldCache.put(fieldName, tx);
+ }
+ else {
+ tx = (TextField)fieldCache.get(fieldName);
+ tx.setWriter(writer);
+ }
+ PdfName fieldType = (PdfName)PdfReader.getPdfObject(merged.get(PdfName.FT));
+ if (PdfName.TX.equals(fieldType)) {
+ tx.setText(text);
+ return tx.getAppearance();
+ }
+ if (!PdfName.CH.equals(fieldType))
+ throw new DocumentException("An appearance was requested without a variable text field.");
+ PdfArray opt = (PdfArray)PdfReader.getPdfObject(merged.get(PdfName.OPT));
+ if ((flags & PdfFormField.FF_COMBO) != 0 && opt == null) {
+ tx.setText(text);
+ return tx.getAppearance();
+ }
+ if (opt != null) {
+ ArrayList op = opt.getArrayList();
+ String choices[] = new String[op.size()];
+ String choicesExp[] = new String[op.size()];
+ for (int k = 0; k < op.size(); ++k) {
+ PdfObject obj = (PdfObject)op.get(k);
+ if (obj.isString()) {
+ choices[k] = choicesExp[k] = ((PdfString)obj).toUnicodeString();
+ }
+ else {
+ ArrayList opar = ((PdfArray)obj).getArrayList();
+ choicesExp[k] = ((PdfString)opar.get(0)).toUnicodeString();
+ choices[k] = ((PdfString)opar.get(1)).toUnicodeString();
+ }
+ }
+ if ((flags & PdfFormField.FF_COMBO) != 0) {
+ for (int k = 0; k < choices.length; ++k) {
+ if (text.equals(choicesExp[k])) {
+ text = choices[k];
+ break;
+ }
+ }
+ tx.setText(text);
+ return tx.getAppearance();
+ }
+ int idx = 0;
+ for (int k = 0; k < choicesExp.length; ++k) {
+ if (text.equals(choicesExp[k])) {
+ idx = k;
+ break;
+ }
+ }
+ tx.setChoices(choices);
+ tx.setChoiceExports(choicesExp);
+ tx.setChoiceSelection(idx);
+ }
+ PdfAppearance app = tx.getListAppearance();
+ topFirst = tx.getTopFirst();
+ return app;
+ }
+
+ Color getMKColor(PdfArray ar) {
+ if (ar == null)
+ return null;
+ ArrayList cc = ar.getArrayList();
+ switch (cc.size()) {
+ case 1:
+ return new GrayColor(((PdfNumber)cc.get(0)).floatValue());
+ case 3:
+ return new Color(((PdfNumber)cc.get(0)).floatValue(), ((PdfNumber)cc.get(1)).floatValue(), ((PdfNumber)cc.get(2)).floatValue());
+ case 4:
+ return new CMYKColor(((PdfNumber)cc.get(0)).floatValue(), ((PdfNumber)cc.get(1)).floatValue(), ((PdfNumber)cc.get(2)).floatValue(), ((PdfNumber)cc.get(3)).floatValue());
+ default:
+ return null;
+ }
+ }
+
+ /** Gets the field value.
+ * @param name the fully qualified field name
+ * @return the field value
+ */
+ public String getField(String name) {
+ Item item = (Item)fields.get(name);
+ if (item == null)
+ return null;
+ lastWasString = false;
+ PdfObject v = PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.V));
+ if (v == null)
+ return "";
+ PdfName type = (PdfName)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.FT));
+ if (PdfName.BTN.equals(type)) {
+ PdfNumber ff = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.FF));
+ int flags = 0;
+ if (ff != null)
+ flags = ff.intValue();
+ if ((flags & PdfFormField.FF_PUSHBUTTON) != 0)
+ return "";
+ String value = "";
+ if (v.isName())
+ value = PdfName.decodeName(v.toString());
+ else if (v.isString())
+ value = ((PdfString)v).toUnicodeString();
+ PdfObject opts = PdfReader.getPdfObject(((PdfDictionary)item.values.get(0)).get(PdfName.OPT));
+ if (opts != null && opts.isArray()) {
+ ArrayList list = ((PdfArray)opts).getArrayList();
+ int idx = 0;
+ try {
+ idx = Integer.parseInt(value);
+ PdfString ps = (PdfString)list.get(idx);
+ value = ps.toUnicodeString();
+ lastWasString = true;
+ }
+ catch (Exception e) {
+ }
+ }
+ return value;
+ }
+ if (v.isString()) {
+ lastWasString = true;
+ return ((PdfString)v).toUnicodeString();
+ }
+ return PdfName.decodeName(v.toString());
+ }
+
+ /**
+ * Sets a field property. Valid property names are:
+ *
+ *
+ * @param field the field name
+ * @param name the property name
+ * @param value the property value
+ * @param inst an array of BaseFont
.
+ * java.awt.Color
.
+ * Float
.
+ * java.awt.Color
.
+ * If null
removes the background.
+ * java.awt.Color
.
+ * If null
removes the border.
+ * int
indexing into AcroField.Item.merged
elements to process.
+ * Set to null
to process all
+ * @return true
if the property exists, false
otherwise
+ */
+ public boolean setFieldProperty(String field, String name, Object value, int inst[]) {
+ if (writer == null)
+ throw new RuntimeException("This AcroFields instance is read-only.");
+ try {
+ Item item = (Item)fields.get(field);
+ if (item == null)
+ return false;
+ InstHit hit = new InstHit(inst);
+ if (name.equalsIgnoreCase("textfont")) {
+ for (int k = 0; k < item.merged.size(); ++k) {
+ if (hit.isHit(k)) {
+ PdfString da = (PdfString)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(k)).get(PdfName.DA));
+ PdfDictionary dr = (PdfDictionary)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(k)).get(PdfName.DR));
+ if (da != null && dr != null) {
+ Object dao[] = splitDAelements(da.toUnicodeString());
+ PdfAppearance cb = new PdfAppearance();
+ if (dao[DA_FONT] != null) {
+ BaseFont bf = (BaseFont)value;
+ PdfName psn = (PdfName)PdfAppearance.stdFieldFontNames.get(bf.getPostscriptFontName());
+ if (psn == null) {
+ psn = new PdfName(bf.getPostscriptFontName());
+ }
+ PdfDictionary fonts = (PdfDictionary)PdfReader.getPdfObject(dr.get(PdfName.FONT));
+ if (fonts == null) {
+ fonts = new PdfDictionary();
+ dr.put(PdfName.FONT, fonts);
+ }
+ PdfIndirectReference fref = (PdfIndirectReference)fonts.get(psn);
+ PdfDictionary top = (PdfDictionary)PdfReader.getPdfObject(reader.getCatalog().get(PdfName.ACROFORM));
+ markUsed(top);
+ dr = (PdfDictionary)PdfReader.getPdfObject(top.get(PdfName.DR));
+ if (dr == null) {
+ dr = new PdfDictionary();
+ top.put(PdfName.DR, dr);
+ }
+ markUsed(dr);
+ PdfDictionary fontsTop = (PdfDictionary)PdfReader.getPdfObject(dr.get(PdfName.FONT));
+ if (fontsTop == null) {
+ fontsTop = new PdfDictionary();
+ dr.put(PdfName.FONT, fontsTop);
+ }
+ markUsed(fontsTop);
+ PdfIndirectReference frefTop = (PdfIndirectReference)fontsTop.get(psn);
+ if (frefTop != null) {
+ if (fref == null)
+ fonts.put(psn, frefTop);
+ }
+ else if (fref == null) {
+ FontDetails fd;
+ if (bf.getFontType() == BaseFont.FONT_TYPE_DOCUMENT) {
+ fd = new FontDetails(null, ((DocumentFont)bf).getIndirectReference(), bf);
+ }
+ else {
+ bf.setSubset(false);
+ fd = writer.addSimple(bf);
+ localFonts.put(psn.toString().substring(1), bf);
+ }
+ fontsTop.put(psn, fd.getIndirectReference());
+ fonts.put(psn, fd.getIndirectReference());
+ }
+ ByteBuffer buf = cb.getInternalBuffer();
+ buf.append(psn.getBytes()).append(' ').append(((Float)dao[DA_SIZE]).floatValue()).append(" Tf ");
+ if (dao[DA_COLOR] != null)
+ cb.setColorFill((Color)dao[DA_COLOR]);
+ PdfString s = new PdfString(cb.toString());
+ ((PdfDictionary)item.merged.get(k)).put(PdfName.DA, s);
+ ((PdfDictionary)item.widgets.get(k)).put(PdfName.DA, s);
+ markUsed((PdfDictionary)item.widgets.get(k));
+ }
+ }
+ }
+ }
+ }
+ else if (name.equalsIgnoreCase("textcolor")) {
+ for (int k = 0; k < item.merged.size(); ++k) {
+ if (hit.isHit(k)) {
+ PdfString da = (PdfString)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(k)).get(PdfName.DA));
+ if (da != null) {
+ Object dao[] = splitDAelements(da.toUnicodeString());
+ PdfAppearance cb = new PdfAppearance();
+ if (dao[DA_FONT] != null) {
+ ByteBuffer buf = cb.getInternalBuffer();
+ buf.append(new PdfName((String)dao[DA_FONT]).getBytes()).append(' ').append(((Float)dao[DA_SIZE]).floatValue()).append(" Tf ");
+ cb.setColorFill((Color)value);
+ PdfString s = new PdfString(cb.toString());
+ ((PdfDictionary)item.merged.get(k)).put(PdfName.DA, s);
+ ((PdfDictionary)item.widgets.get(k)).put(PdfName.DA, s);
+ markUsed((PdfDictionary)item.widgets.get(k));
+ }
+ }
+ }
+ }
+ }
+ else if (name.equalsIgnoreCase("textsize")) {
+ for (int k = 0; k < item.merged.size(); ++k) {
+ if (hit.isHit(k)) {
+ PdfString da = (PdfString)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(k)).get(PdfName.DA));
+ if (da != null) {
+ Object dao[] = splitDAelements(da.toUnicodeString());
+ PdfAppearance cb = new PdfAppearance();
+ if (dao[DA_FONT] != null) {
+ ByteBuffer buf = cb.getInternalBuffer();
+ buf.append(new PdfName((String)dao[DA_FONT]).getBytes()).append(' ').append(((Float)value).floatValue()).append(" Tf ");
+ if (dao[DA_COLOR] != null)
+ cb.setColorFill((Color)dao[DA_COLOR]);
+ PdfString s = new PdfString(cb.toString());
+ ((PdfDictionary)item.merged.get(k)).put(PdfName.DA, s);
+ ((PdfDictionary)item.widgets.get(k)).put(PdfName.DA, s);
+ markUsed((PdfDictionary)item.widgets.get(k));
+ }
+ }
+ }
+ }
+ }
+ else if (name.equalsIgnoreCase("bgcolor") || name.equalsIgnoreCase("bordercolor")) {
+ PdfName dname = (name.equalsIgnoreCase("bgcolor") ? PdfName.BG : PdfName.BC);
+ for (int k = 0; k < item.merged.size(); ++k) {
+ if (hit.isHit(k)) {
+ PdfObject obj = PdfReader.getPdfObject(((PdfDictionary)item.merged.get(k)).get(PdfName.MK));
+ markUsed(obj);
+ PdfDictionary mk = (PdfDictionary)obj;
+ if (mk == null) {
+ if (value == null)
+ return true;
+ mk = new PdfDictionary();
+ ((PdfDictionary)item.merged.get(k)).put(PdfName.MK, mk);
+ ((PdfDictionary)item.widgets.get(k)).put(PdfName.MK, mk);
+ markUsed((PdfDictionary)item.widgets.get(k));
+ }
+ if (value == null)
+ mk.remove(dname);
+ else
+ mk.put(dname, PdfFormField.getMKColor((Color)value));
+ }
+ }
+ }
+ else
+ return false;
+ return true;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Sets a field property. Valid property names are:
+ *
+ *
+ * @param field the field name
+ * @param name the property name
+ * @param value the property value
+ * @param inst an array of
+ *
+ *
+ *
+ *
+ *
+ * int
indexing into AcroField.Item.merged
elements to process.
+ * Set to null
to process all
+ * @return true
if the property exists, false
otherwise
+ */
+ public boolean setFieldProperty(String field, String name, int value, int inst[]) {
+ if (writer == null)
+ throw new RuntimeException("This AcroFields instance is read-only.");
+ Item item = (Item)fields.get(field);
+ if (item == null)
+ return false;
+ InstHit hit = new InstHit(inst);
+ if (name.equalsIgnoreCase("flags")) {
+ PdfNumber num = new PdfNumber(value);
+ for (int k = 0; k < item.merged.size(); ++k) {
+ if (hit.isHit(k)) {
+ ((PdfDictionary)item.merged.get(k)).put(PdfName.F, num);
+ ((PdfDictionary)item.widgets.get(k)).put(PdfName.F, num);
+ markUsed((PdfDictionary)item.widgets.get(k));
+ }
+ }
+ }
+ else if (name.equalsIgnoreCase("setflags")) {
+ for (int k = 0; k < item.merged.size(); ++k) {
+ if (hit.isHit(k)) {
+ PdfNumber num = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.widgets.get(k)).get(PdfName.F));
+ int val = 0;
+ if (num != null)
+ val = num.intValue();
+ num = new PdfNumber(val | value);
+ ((PdfDictionary)item.merged.get(k)).put(PdfName.F, num);
+ ((PdfDictionary)item.widgets.get(k)).put(PdfName.F, num);
+ markUsed((PdfDictionary)item.widgets.get(k));
+ }
+ }
+ }
+ else if (name.equalsIgnoreCase("clrflags")) {
+ for (int k = 0; k < item.merged.size(); ++k) {
+ if (hit.isHit(k)) {
+ PdfNumber num = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.widgets.get(k)).get(PdfName.F));
+ int val = 0;
+ if (num != null)
+ val = num.intValue();
+ num = new PdfNumber(val & (~value));
+ ((PdfDictionary)item.merged.get(k)).put(PdfName.F, num);
+ ((PdfDictionary)item.widgets.get(k)).put(PdfName.F, num);
+ markUsed((PdfDictionary)item.widgets.get(k));
+ }
+ }
+ }
+ else if (name.equalsIgnoreCase("fflags")) {
+ PdfNumber num = new PdfNumber(value);
+ for (int k = 0; k < item.merged.size(); ++k) {
+ if (hit.isHit(k)) {
+ ((PdfDictionary)item.merged.get(k)).put(PdfName.FF, num);
+ ((PdfDictionary)item.values.get(k)).put(PdfName.FF, num);
+ markUsed((PdfDictionary)item.values.get(k));
+ }
+ }
+ }
+ else if (name.equalsIgnoreCase("setfflags")) {
+ for (int k = 0; k < item.merged.size(); ++k) {
+ if (hit.isHit(k)) {
+ PdfNumber num = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.values.get(k)).get(PdfName.FF));
+ int val = 0;
+ if (num != null)
+ val = num.intValue();
+ num = new PdfNumber(val | value);
+ ((PdfDictionary)item.merged.get(k)).put(PdfName.FF, num);
+ ((PdfDictionary)item.values.get(k)).put(PdfName.FF, num);
+ markUsed((PdfDictionary)item.values.get(k));
+ }
+ }
+ }
+ else if (name.equalsIgnoreCase("clrfflags")) {
+ for (int k = 0; k < item.merged.size(); ++k) {
+ if (hit.isHit(k)) {
+ PdfNumber num = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.values.get(k)).get(PdfName.FF));
+ int val = 0;
+ if (num != null)
+ val = num.intValue();
+ num = new PdfNumber(val & (~value));
+ ((PdfDictionary)item.merged.get(k)).put(PdfName.FF, num);
+ ((PdfDictionary)item.values.get(k)).put(PdfName.FF, num);
+ markUsed((PdfDictionary)item.values.get(k));
+ }
+ }
+ }
+ else
+ return false;
+ return true;
+ }
+
+ /** Sets the fields by FDF merging.
+ * @param fdf the FDF form
+ * @throws IOException on error
+ * @throws DocumentException on error
+ */
+ public void setFields(FdfReader fdf) throws IOException, DocumentException {
+ HashMap fd = fdf.getFields();
+ for (Iterator i = fd.keySet().iterator(); i.hasNext();) {
+ String f = (String)i.next();
+ String v = fdf.getFieldValue(f);
+ if (v != null)
+ setField(f, v);
+ }
+ }
+
+ /** Sets the fields by XFDF merging.
+ * @param xfdf the XFDF form
+ * @throws IOException on error
+ * @throws DocumentException on error
+ */
+
+ public void setFields(XfdfReader xfdf) throws IOException, DocumentException {
+ HashMap fd = xfdf.getFields();
+ for (Iterator i = fd.keySet().iterator(); i.hasNext();) {
+ String f = (String)i.next();
+ String v = xfdf.getFieldValue(f);
+ if (v != null)
+ setField(f, v);
+ }
+ }
+
+ /** Sets the field value.
+ * @param name the fully qualified field name
+ * @param value the field value
+ * @throws IOException on error
+ * @throws DocumentException on error
+ * @return true
if the field was found and changed,
+ * false
otherwise
+ */
+ public boolean setField(String name, String value) throws IOException, DocumentException {
+ return setField(name, value, value);
+ }
+
+ /** Sets the field value and the display string. The display string
+ * is used to build the appearance in the cases where the value
+ * is modified by Acrobat with JavaScript and the algorithm is
+ * known.
+ * @param name the fully qualified field name
+ * @param value the field value
+ * @param display the string that is used for the appearance
+ * @return true
if the field was found and changed,
+ * false
otherwise
+ * @throws IOException on error
+ * @throws DocumentException on error
+ */
+ public boolean setField(String name, String value, String display) throws IOException, DocumentException {
+ if (writer == null)
+ throw new DocumentException("This AcroFields instance is read-only.");
+ Item item = (Item)fields.get(name);
+ if (item == null)
+ return false;
+ PdfName type = (PdfName)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.FT));
+ if (PdfName.TX.equals(type)) {
+ PdfNumber maxLen = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.MAXLEN));
+ int len = 0;
+ if (maxLen != null)
+ len = maxLen.intValue();
+ if (len > 0)
+ value = value.substring(0, Math.min(len, value.length()));
+ }
+ if (PdfName.TX.equals(type) || PdfName.CH.equals(type)) {
+ PdfString v = new PdfString(value, PdfObject.TEXT_UNICODE);
+ for (int idx = 0; idx < item.values.size(); ++idx) {
+ PdfDictionary valueDic = (PdfDictionary)item.values.get(idx);
+ valueDic.put(PdfName.V, v);
+ valueDic.remove(PdfName.I);
+ markUsed(valueDic);
+ PdfDictionary merged = (PdfDictionary)item.merged.get(idx);
+ merged.remove(PdfName.I);
+ merged.put(PdfName.V, v);
+ PdfDictionary widget = (PdfDictionary)item.widgets.get(idx);
+ if (generateAppearances) {
+ PdfAppearance app = getAppearance(merged, display, name);
+ if (PdfName.CH.equals(type)) {
+ PdfNumber n = new PdfNumber(topFirst);
+ widget.put(PdfName.TI, n);
+ merged.put(PdfName.TI, n);
+ }
+ PdfDictionary appDic = (PdfDictionary)PdfReader.getPdfObject(widget.get(PdfName.AP));
+ if (appDic == null) {
+ appDic = new PdfDictionary();
+ widget.put(PdfName.AP, appDic);
+ merged.put(PdfName.AP, appDic);
+ }
+ appDic.put(PdfName.N, app.getIndirectReference());
+ writer.releaseTemplate(app);
+ }
+ else {
+ widget.remove(PdfName.AP);
+ merged.remove(PdfName.AP);
+ }
+ markUsed(widget);
+ }
+ return true;
+ }
+ else if (PdfName.BTN.equals(type)) {
+ PdfNumber ff = (PdfNumber)PdfReader.getPdfObject(((PdfDictionary)item.merged.get(0)).get(PdfName.FF));
+ int flags = 0;
+ if (ff != null)
+ flags = ff.intValue();
+ if ((flags & PdfFormField.FF_PUSHBUTTON) != 0)
+ return true;
+ PdfName v = new PdfName(value);
+ if ((flags & PdfFormField.FF_RADIO) == 0) {
+ for (int idx = 0; idx < item.values.size(); ++idx) {
+ ((PdfDictionary)item.values.get(idx)).put(PdfName.V, v);
+ markUsed((PdfDictionary)item.values.get(idx));
+ PdfDictionary merged = (PdfDictionary)item.merged.get(idx);
+ merged.put(PdfName.V, v);
+ merged.put(PdfName.AS, v);
+ PdfDictionary widget = (PdfDictionary)item.widgets.get(idx);
+ if (isInAP(widget, v))
+ widget.put(PdfName.AS, v);
+ else
+ widget.put(PdfName.AS, PdfName.Off);
+ markUsed(widget);
+ }
+ }
+ else {
+ ArrayList lopt = new ArrayList();
+ PdfObject opts = PdfReader.getPdfObject(((PdfDictionary)item.values.get(0)).get(PdfName.OPT));
+ if (opts != null && opts.isArray()) {
+ ArrayList list = ((PdfArray)opts).getArrayList();
+ for (int k = 0; k < list.size(); ++k) {
+ PdfObject vv = PdfReader.getPdfObject((PdfObject)list.get(k));
+ if (vv != null && vv.isString())
+ lopt.add(((PdfString)vv).toUnicodeString());
+ else
+ lopt.add(null);
+ }
+ }
+ int vidx = lopt.indexOf(value);
+ PdfName valt = null;
+ PdfName vt;
+ if (vidx >= 0) {
+ vt = valt = new PdfName(String.valueOf(vidx));
+ }
+ else
+ vt = v;
+ for (int idx = 0; idx < item.values.size(); ++idx) {
+ PdfDictionary merged = (PdfDictionary)item.merged.get(idx);
+ PdfDictionary widget = (PdfDictionary)item.widgets.get(idx);
+ markUsed((PdfDictionary)item.values.get(idx));
+ if (valt != null) {
+ PdfString ps = new PdfString(value, PdfObject.TEXT_UNICODE);
+ ((PdfDictionary)item.values.get(idx)).put(PdfName.V, ps);
+ merged.put(PdfName.V, ps);
+ }
+ else {
+ ((PdfDictionary)item.values.get(idx)).put(PdfName.V, v);
+ merged.put(PdfName.V, v);
+ }
+ markUsed(widget);
+ if (isInAP(widget, vt)) {
+ merged.put(PdfName.AS, vt);
+ widget.put(PdfName.AS, vt);
+ }
+ else {
+ merged.put(PdfName.AS, PdfName.Off);
+ widget.put(PdfName.AS, PdfName.Off);
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ boolean isInAP(PdfDictionary dic, PdfName check) {
+ PdfDictionary appDic = (PdfDictionary)PdfReader.getPdfObject(dic.get(PdfName.AP));
+ if (appDic == null)
+ return false;
+ PdfDictionary NDic = (PdfDictionary)PdfReader.getPdfObject(appDic.get(PdfName.N));
+ return (NDic != null && NDic.get(check) != null);
+ }
+
+ /** Gets all the fields. The fields are keyed by the fully qualified field name and
+ * the value is an instance of AcroFields.Item
.
+ * @return all the fields
+ */
+ public HashMap getFields() {
+ return fields;
+ }
+
+ /**
+ * Gets the field structure.
+ * @param name the name of the field
+ * @return the field structure or null
if the field
+ * does not exist
+ */
+ public Item getFieldItem(String name) {
+ return (Item)fields.get(name);
+ }
+
+ /**
+ * Gets the field box positions in the document. The return is an array of float
+ * multiple of 5. For each of this groups the values are: [page, llx, lly, urx,
+ * ury].
+ * @param name the field name
+ * @return the positions or null
if field does not exist
+ */
+ public float[] getFieldPositions(String name) {
+ Item item = (Item)fields.get(name);
+ if (item == null)
+ return null;
+ float ret[] = new float[item.page.size() * 5];
+ int ptr = 0;
+ for (int k = 0; k < item.page.size(); ++k) {
+ try {
+ PdfDictionary wd = (PdfDictionary)item.widgets.get(k);
+ PdfArray rect = (PdfArray)wd.get(PdfName.RECT);
+ if (rect == null)
+ continue;
+ Rectangle r = PdfReader.getNormalizedRectangle(rect);
+ ret[ptr] = ((Integer)item.page.get(k)).floatValue();
+ ++ptr;
+ ret[ptr++] = r.left();
+ ret[ptr++] = r.bottom();
+ ret[ptr++] = r.right();
+ ret[ptr++] = r.top();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ if (ptr < ret.length) {
+ float ret2[] = new float[ptr];
+ System.arraycopy(ret, 0, ret2, 0, ptr);
+ return ret2;
+ }
+ return ret;
+ }
+
+ private int removeRefFromArray(PdfArray array, PdfObject refo) {
+ ArrayList ar = array.getArrayList();
+ if (refo == null || !refo.isIndirect())
+ return ar.size();
+ PdfIndirectReference ref = (PdfIndirectReference)refo;
+ for (int j = 0; j < ar.size(); ++j) {
+ PdfObject obj = (PdfObject)ar.get(j);
+ if (!obj.isIndirect())
+ continue;
+ if (((PdfIndirectReference)obj).getNumber() == ref.getNumber())
+ ar.remove(j--);
+ }
+ return ar.size();
+ }
+
+ /**
+ * Removes all the fields from page
.
+ * @param page the page to remove the fields from
+ * @return true
if any field was removed, false otherwise
+ */
+ public boolean removeFieldsFromPage(int page) {
+ if (page < 1)
+ return false;
+ String names[] = new String[fields.size()];
+ fields.keySet().toArray(names);
+ boolean found = false;
+ for (int k = 0; k < names.length; ++k) {
+ boolean fr = removeField(names[k], page);
+ found = (found || fr);
+ }
+ return found;
+ }
+
+ /**
+ * Removes a field from the document. If page equals -1 all the fields with this
+ * name
are removed from the document otherwise only the fields in
+ * that particular page are removed.
+ * @param name the field name
+ * @param page the page to remove the field from or -1 to remove it from all the pages
+ * @return true
if the field exists, false otherwise
+ */
+ public boolean removeField(String name, int page) {
+ Item item = (Item)fields.get(name);
+ if (item == null)
+ return false;
+ PdfDictionary acroForm = (PdfDictionary)PdfReader.getPdfObject(reader.getCatalog().get(PdfName.ACROFORM), reader.getCatalog());
+
+ if (acroForm == null)
+ return false;
+ PdfArray arrayf = (PdfArray)PdfReader.getPdfObject(acroForm.get(PdfName.FIELDS), acroForm);
+ if (arrayf == null)
+ return false;
+ for (int k = 0; k < item.widget_refs.size(); ++k) {
+ int pageV = ((Integer)item.page.get(k)).intValue();
+ if (page != -1 && page != pageV)
+ continue;
+ PdfIndirectReference ref = (PdfIndirectReference)item.widget_refs.get(k);
+ PdfDictionary wd = (PdfDictionary)PdfReader.getPdfObject(ref);
+ PdfDictionary pageDic = reader.getPageN(pageV);
+ PdfArray annots = (PdfArray)PdfReader.getPdfObject(pageDic.get(PdfName.ANNOTS), pageDic);
+ if (annots != null) {
+ if (removeRefFromArray(annots, ref) == 0) {
+ pageDic.remove(PdfName.ANNOTS);
+ markUsed(pageDic);
+ }
+ else
+ markUsed(annots);
+ }
+ PdfReader.killIndirect(ref);
+ PdfIndirectReference kid = ref;
+ while ((ref = (PdfIndirectReference)wd.get(PdfName.PARENT)) != null) {
+ wd = (PdfDictionary)PdfReader.getPdfObject(ref);
+ PdfArray kids = (PdfArray)PdfReader.getPdfObject(wd.get(PdfName.KIDS));
+ if (removeRefFromArray(kids, kid) != 0)
+ break;
+ kid = ref;
+ PdfReader.killIndirect(ref);
+ }
+ if (ref == null) {
+ removeRefFromArray(arrayf, kid);
+ markUsed(arrayf);
+ }
+ if (page != -1) {
+ item.merged.remove(k);
+ item.page.remove(k);
+ item.values.remove(k);
+ item.widget_refs.remove(k);
+ item.widgets.remove(k);
+ --k;
+ }
+ }
+ if (page == -1 || item.merged.size() == 0)
+ fields.remove(name);
+ return true;
+ }
+
+ /**
+ * Removes a field from the document.
+ * @param name the field name
+ * @return true
if the field exists, false otherwise
+ */
+ public boolean removeField(String name) {
+ return removeField(name, -1);
+ }
+
+ /** Gets the property generateAppearances.
+ * @return the property generateAppearances
+ */
+ public boolean isGenerateAppearances() {
+ return this.generateAppearances;
+ }
+
+ /** Sets the option to generate appearances. Not generating apperances
+ * will speed-up form filling but the results can be
+ * unexpected in Acrobat. Don't use it unless your environment is well
+ * controlled. The default is true
.
+ * @param generateAppearances the option to generate appearances
+ */
+ public void setGenerateAppearances(boolean generateAppearances) {
+ this.generateAppearances = generateAppearances;
+ PdfDictionary top = (PdfDictionary)PdfReader.getPdfObject(reader.getCatalog().get(PdfName.ACROFORM));
+ if (generateAppearances)
+ top.remove(PdfName.NEEDAPPEARANCES);
+ else
+ top.put(PdfName.NEEDAPPEARANCES, PdfBoolean.PDFTRUE);
+ }
+
+ /** The field representations for retrieval and modification. */
+ public static class Item {
+ /** An array of PdfDictionary
where the value tag /V
+ * is present.
+ */
+ public ArrayList values = new ArrayList();
+ /** An array of PdfDictionary
with the widgets.
+ */
+ public ArrayList widgets = new ArrayList();
+ /** An array of PdfDictionary
with the widget references.
+ */
+ public ArrayList widget_refs = new ArrayList();
+ /** An array of PdfDictionary
with all the field
+ * and widget tags merged.
+ */
+ public ArrayList merged = new ArrayList();
+ /** An array of Integer
with the page numbers where
+ * the widgets are displayed.
+ */
+ public ArrayList page = new ArrayList();
+ /** An array of Integer
with the tab order of the field in the page.
+ */
+ public ArrayList tabOrder = new ArrayList();
+ }
+
+ private static class InstHit {
+ IntHashtable hits;
+ public InstHit(int inst[]) {
+ if (inst == null)
+ return;
+ hits = new IntHashtable();
+ for (int k = 0; k < inst.length; ++k)
+ hits.put(inst[k], 1);
+ }
+
+ public boolean isHit(int n) {
+ if (hits == null)
+ return true;
+ return hits.containsKey(n);
+ }
+ }
+
+ /**
+ * Gets the field names that have signatures and are signed.
+ * @return the field names that have signatures and are signed
+ */
+ public ArrayList getSignatureNames() {
+ if (sigNames != null)
+ return new ArrayList(sigNames.keySet());
+ sigNames = new HashMap();
+ ArrayList sorter = new ArrayList();
+ for (Iterator it = fields.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry)it.next();
+ Item item = (Item)entry.getValue();
+ PdfDictionary merged = (PdfDictionary)item.merged.get(0);
+ if (!PdfName.SIG.equals(merged.get(PdfName.FT)))
+ continue;
+ PdfObject vo = PdfReader.getPdfObject(merged.get(PdfName.V));
+ if (vo == null || vo.type() != PdfObject.DICTIONARY)
+ continue;
+ PdfDictionary v = (PdfDictionary)vo;
+ PdfObject contents = v.get(PdfName.CONTENTS);
+ if (contents == null || contents.type() != PdfObject.STRING)
+ continue;
+ PdfObject ro = v.get(PdfName.BYTERANGE);
+ if (ro == null || ro.type() != PdfObject.ARRAY)
+ continue;
+ ArrayList ra = ((PdfArray)ro).getArrayList();
+ if (ra.size() < 2)
+ continue;
+ int length = ((PdfNumber)ra.get(ra.size() - 1)).intValue() + ((PdfNumber)ra.get(ra.size() - 2)).intValue();
+ sorter.add(new Object[]{entry.getKey(), new int[]{length, 0}});
+ }
+ Collections.sort(sorter, new AcroFields.SorterComparator());
+ if (sorter.size() > 0) {
+ if (((int[])((Object[])sorter.get(sorter.size() - 1))[1])[0] == reader.getFileLength())
+ totalRevisions = sorter.size();
+ else
+ totalRevisions = sorter.size() + 1;
+ for (int k = 0; k < sorter.size(); ++k) {
+ Object objs[] = (Object[])sorter.get(k);
+ String name = (String)objs[0];
+ int p[] = (int[])objs[1];
+ p[1] = k + 1;
+ sigNames.put(name, p);
+ }
+ }
+ return new ArrayList(sigNames.keySet());
+ }
+
+ /**
+ * Gets the field names that have blank signatures.
+ * @return the field names that have blank signatures
+ */
+ public ArrayList getBlankSignatureNames() {
+ getSignatureNames();
+ ArrayList sigs = new ArrayList();
+ for (Iterator it = fields.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry)it.next();
+ Item item = (Item)entry.getValue();
+ PdfDictionary merged = (PdfDictionary)item.merged.get(0);
+ if (!PdfName.SIG.equals(merged.get(PdfName.FT)))
+ continue;
+ if (sigNames.containsKey(entry.getKey()))
+ continue;
+ sigs.add(entry.getKey());
+ }
+ return sigs;
+ }
+
+ /**
+ * Gets the signature dictionary, the one keyed by /V.
+ * @param name the field name
+ * @return the signature dictionary keyed by /V or null
if the field is not
+ * a signature
+ */
+ public PdfDictionary getSignatureDictionary(String name) {
+ getSignatureNames();
+ if (!sigNames.containsKey(name))
+ return null;
+ Item item = (Item)fields.get(name);
+ PdfDictionary merged = (PdfDictionary)item.merged.get(0);
+ return (PdfDictionary)PdfReader.getPdfObject(merged.get(PdfName.V));
+ }
+
+ /**
+ * Checks is the signature covers the entire document or just part of it.
+ * @param name the signature field name
+ * @return true
if the signature covers the entire document,
+ * false
otherwise
+ */
+ public boolean signatureCoversWholeDocument(String name) {
+ getSignatureNames();
+ if (!sigNames.containsKey(name))
+ return false;
+ return ((int[])sigNames.get(name))[0] == reader.getFileLength();
+ }
+
+ /**
+ * Verifies a signature. An example usage is:
+ *
+ * KeyStore kall = PdfPKCS7.loadCacertsKeyStore();
+ * PdfReader reader = new PdfReader("my_signed_doc.pdf");
+ * AcroFields af = reader.getAcroFields();
+ * ArrayList names = af.getSignatureNames();
+ * for (int k = 0; k < names.size(); ++k) {
+ * String name = (String)names.get(k);
+ * System.out.println("Signature name: " + name);
+ * System.out.println("Signature covers whole document: " + af.signatureCoversWholeDocument(name));
+ * PdfPKCS7 pk = af.verifySignature(name);
+ * Calendar cal = pk.getSignDate();
+ * Certificate pkc[] = pk.getCertificates();
+ * System.out.println("Subject: " + PdfPKCS7.getSubjectFields(pk.getSigningCertificate()));
+ * System.out.println("Document modified: " + !pk.verify());
+ * Object fails[] = PdfPKCS7.verifyCertificates(pkc, kall, null, cal);
+ * if (fails == null)
+ * System.out.println("Certificates verified against the KeyStore");
+ * else
+ * System.out.println("Certificate failed: " + fails[1]);
+ * }
+ *
+ * @param name the signature field name
+ * @return a PdfPKCS7
class to continue the verification
+ */
+ public PdfPKCS7 verifySignature(String name) {
+ return verifySignature(name, null);
+ }
+
+ /**
+ * Verifies a signature. An example usage is:
+ *
+ * KeyStore kall = PdfPKCS7.loadCacertsKeyStore();
+ * PdfReader reader = new PdfReader("my_signed_doc.pdf");
+ * AcroFields af = reader.getAcroFields();
+ * ArrayList names = af.getSignatureNames();
+ * for (int k = 0; k < names.size(); ++k) {
+ * String name = (String)names.get(k);
+ * System.out.println("Signature name: " + name);
+ * System.out.println("Signature covers whole document: " + af.signatureCoversWholeDocument(name));
+ * PdfPKCS7 pk = af.verifySignature(name);
+ * Calendar cal = pk.getSignDate();
+ * Certificate pkc[] = pk.getCertificates();
+ * System.out.println("Subject: " + PdfPKCS7.getSubjectFields(pk.getSigningCertificate()));
+ * System.out.println("Document modified: " + !pk.verify());
+ * Object fails[] = PdfPKCS7.verifyCertificates(pkc, kall, null, cal);
+ * if (fails == null)
+ * System.out.println("Certificates verified against the KeyStore");
+ * else
+ * System.out.println("Certificate failed: " + fails[1]);
+ * }
+ *
+ * @param name the signature field name
+ * @param provider the provider or null
for the default provider
+ * @return a PdfPKCS7
class to continue the verification
+ */
+ public PdfPKCS7 verifySignature(String name, String provider) {
+ PdfDictionary v = getSignatureDictionary(name);
+ if (v == null)
+ return null;
+ try {
+ PdfName sub = (PdfName)PdfReader.getPdfObject(v.get(PdfName.SUBFILTER));
+ PdfString contents = (PdfString)PdfReader.getPdfObject(v.get(PdfName.CONTENTS));
+ PdfPKCS7 pk = null;
+ if (sub.equals(PdfName.ADBE_X509_RSA_SHA1)) {
+ PdfString cert = (PdfString)PdfReader.getPdfObject(v.get(PdfName.CERT));
+ pk = new PdfPKCS7(contents.getOriginalBytes(), cert.getBytes(), provider);
+ }
+ else
+ pk = new PdfPKCS7(contents.getOriginalBytes(), provider);
+ updateByteRange(pk, v);
+ PdfString str = (PdfString)PdfReader.getPdfObject(v.get(PdfName.M));
+ if (str != null)
+ pk.setSignDate(PdfDate.decode(str.toString()));
+ str = (PdfString)PdfReader.getPdfObject(v.get(PdfName.NAME));
+ if (str != null)
+ pk.setSignName(str.toUnicodeString());
+ str = (PdfString)PdfReader.getPdfObject(v.get(PdfName.REASON));
+ if (str != null)
+ pk.setReason(str.toUnicodeString());
+ str = (PdfString)PdfReader.getPdfObject(v.get(PdfName.LOCATION));
+ if (str != null)
+ pk.setLocation(str.toUnicodeString());
+ return pk;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ private void updateByteRange(PdfPKCS7 pkcs7, PdfDictionary v) {
+ PdfArray b = (PdfArray)PdfReader.getPdfObject(v.get(PdfName.BYTERANGE));
+ RandomAccessFileOrArray rf = reader.getSafeFile();
+ try {
+ rf.reOpen();
+ byte buf[] = new byte[8192];
+ ArrayList ar = b.getArrayList();
+ for (int k = 0; k < ar.size(); ++k) {
+ int start = ((PdfNumber)ar.get(k)).intValue();
+ int length = ((PdfNumber)ar.get(++k)).intValue();
+ rf.seek(start);
+ while (length > 0) {
+ int rd = rf.read(buf, 0, Math.min(length, buf.length));
+ if (rd <= 0)
+ break;
+ length -= rd;
+ pkcs7.update(buf, 0, rd);
+ }
+ }
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ finally {
+ try{rf.close();}catch(Exception e){}
+ }
+ }
+
+ private void markUsed(PdfObject obj) {
+ if (!append)
+ return;
+ ((PdfStamperImp)writer).markUsed(obj);
+ }
+
+ /**
+ * Gets the total number of revisions this document has.
+ * @return the total number of revisions
+ */
+ public int getTotalRevisions() {
+ getSignatureNames();
+ return this.totalRevisions;
+ }
+
+ /**
+ * Gets this field
revision.
+ * @param field the signature field name
+ * @return the revision or zero if it's not a signature field
+ */
+ public int getRevision(String field) {
+ getSignatureNames();
+ if (!sigNames.containsKey(field))
+ return 0;
+ return ((int[])sigNames.get(field))[1];
+ }
+
+ /**
+ * Extracts a revision from the document.
+ * @param field the signature field name
+ * @return an InputStream
covering the revision. Returns null
if
+ * it's not a signature field
+ * @throws IOException on error
+ */
+ public InputStream extractRevision(String field) throws IOException {
+ getSignatureNames();
+ int length = ((int[])sigNames.get(field))[0];
+ RandomAccessFileOrArray raf = reader.getSafeFile();
+ raf.reOpen();
+ raf.seek(0);
+ return new RevisionStream(raf, length);
+ }
+
+ /**
+ * Gets the appearances cache.
+ * @return the appearances cache
+ */
+ public HashMap getFieldCache() {
+ return this.fieldCache;
+ }
+
+ /**
+ * Sets a cache for field appearances. Parsing the existing PDF to
+ * create a new TextField is time expensive. For those tasks that repeatedly
+ * fill the same PDF with different field values the use of the cache has dramatic
+ * speed advantages. An example usage:
+ *
+ * String pdfFile = ...;// the pdf file used as template
+ * ArrayList xfdfFiles = ...;// the xfdf file names
+ * ArrayList pdfOutFiles = ...;// the output file names, one for each element in xpdfFiles
+ * HashMap cache = new HashMap();// the appearances cache
+ * PdfReader originalReader = new PdfReader(pdfFile);
+ * for (int k = 0; k < xfdfFiles.size(); ++k) {
+ * PdfReader reader = new PdfReader(originalReader);
+ * XfdfReader xfdf = new XfdfReader((String)xfdfFiles.get(k));
+ * PdfStamper stp = new PdfStamper(reader, new FileOutputStream((String)pdfOutFiles.get(k)));
+ * AcroFields af = stp.getAcroFields();
+ * af.setFieldCache(cache);
+ * af.setFields(xfdf);
+ * stp.close();
+ * }
+ *
+ * @param fieldCache an HasMap that will carry the cached appearances
+ */
+ public void setFieldCache(HashMap fieldCache) {
+ this.fieldCache = fieldCache;
+ }
+
+ /**
+ * Sets extra margins in text fields to better mimic the Acrobat layout.
+ * @param extraMarginLeft the extra marging left
+ * @param extraMarginTop the extra margin top
+ */
+ public void setExtraMargin(float extraMarginLeft, float extraMarginTop) {
+ this.extraMarginLeft = extraMarginLeft;
+ this.extraMarginTop = extraMarginTop;
+ }
+
+ /**
+ * Adds a substitution font to the list. The fonts in this list will be used if the original
+ * font doesn't contain the needed glyphs.
+ * @param font the font
+ */
+ public void addSubstitutionFont(BaseFont font) {
+ if (substitutionFonts == null)
+ substitutionFonts = new ArrayList();
+ substitutionFonts.add(font);
+ }
+
+ private static final HashMap stdFieldFontNames = new HashMap();
+
+ /**
+ * Holds value of property totalRevisions.
+ */
+ private int totalRevisions;
+
+ /**
+ * Holds value of property fieldCache.
+ */
+ private HashMap fieldCache;
+
+ static {
+ stdFieldFontNames.put("CoBO", new String[]{"Courier-BoldOblique"});
+ stdFieldFontNames.put("CoBo", new String[]{"Courier-Bold"});
+ stdFieldFontNames.put("CoOb", new String[]{"Courier-Oblique"});
+ stdFieldFontNames.put("Cour", new String[]{"Courier"});
+ stdFieldFontNames.put("HeBO", new String[]{"Helvetica-BoldOblique"});
+ stdFieldFontNames.put("HeBo", new String[]{"Helvetica-Bold"});
+ stdFieldFontNames.put("HeOb", new String[]{"Helvetica-Oblique"});
+ stdFieldFontNames.put("Helv", new String[]{"Helvetica"});
+ stdFieldFontNames.put("Symb", new String[]{"Symbol"});
+ stdFieldFontNames.put("TiBI", new String[]{"Times-BoldItalic"});
+ stdFieldFontNames.put("TiBo", new String[]{"Times-Bold"});
+ stdFieldFontNames.put("TiIt", new String[]{"Times-Italic"});
+ stdFieldFontNames.put("TiRo", new String[]{"Times-Roman"});
+ stdFieldFontNames.put("ZaDb", new String[]{"ZapfDingbats"});
+ stdFieldFontNames.put("HySm", new String[]{"HYSMyeongJo-Medium", "UniKS-UCS2-H"});
+ stdFieldFontNames.put("HyGo", new String[]{"HYGoThic-Medium", "UniKS-UCS2-H"});
+ stdFieldFontNames.put("KaGo", new String[]{"HeiseiKakuGo-W5", "UniKS-UCS2-H"});
+ stdFieldFontNames.put("KaMi", new String[]{"HeiseiMin-W3", "UniJIS-UCS2-H"});
+ stdFieldFontNames.put("MHei", new String[]{"MHei-Medium", "UniCNS-UCS2-H"});
+ stdFieldFontNames.put("MSun", new String[]{"MSung-Light", "UniCNS-UCS2-H"});
+ stdFieldFontNames.put("STSo", new String[]{"STSong-Light", "UniGB-UCS2-H"});
+ }
+
+ private static class RevisionStream extends InputStream {
+ private byte b[] = new byte[1];
+ private RandomAccessFileOrArray raf;
+ private int length;
+ private int rangePosition = 0;
+ private boolean closed;
+
+ private RevisionStream(RandomAccessFileOrArray raf, int length) {
+ this.raf = raf;
+ this.length = length;
+ }
+
+ public int read() throws IOException {
+ int n = read(b);
+ if (n != 1)
+ return -1;
+ return b[0] & 0xff;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+ } else if ((off < 0) || (off > b.length) || (len < 0) ||
+ ((off + len) > b.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+ if (rangePosition >= length) {
+ close();
+ return -1;
+ }
+ int elen = Math.min(len, length - rangePosition);
+ raf.readFully(b, off, elen);
+ rangePosition += elen;
+ return elen;
+ }
+
+ public void close() throws IOException {
+ if (!closed) {
+ raf.close();
+ closed = true;
+ }
+ }
+ }
+
+ private static class SorterComparator implements Comparator {
+ public int compare(Object o1, Object o2) {
+ int n1 = ((int[])((Object[])o1)[1])[0];
+ int n2 = ((int[])((Object[])o2)[1])[0];
+ return n1 - n2;
+ }
+ }
+
+ /**
+ * Gets the list of substitution fonts. The list is composed of BaseFont
and can be null
. The fonts in this list will be used if the original
+ * font doesn't contain the needed glyphs.
+ * @return the list
+ */
+ public ArrayList getSubstitutionFonts() {
+ return substitutionFonts;
+ }
+
+ /**
+ * Sets a list of substitution fonts. The list is composed of BaseFont
and can also be null
. The fonts in this list will be used if the original
+ * font doesn't contain the needed glyphs.
+ * @param substitutionFonts the list
+ */
+ public void setSubstitutionFonts(ArrayList substitutionFonts) {
+ this.substitutionFonts = substitutionFonts;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/ArabicLigaturizer.java b/src/main/java/com/lowagie/text/pdf/ArabicLigaturizer.java
new file mode 100644
index 0000000..959b7b5
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/ArabicLigaturizer.java
@@ -0,0 +1,771 @@
+/*
+ * Copyright 2003 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+/** Shape arabic characters. This code was converted from a C version
+ * at www.pango.org.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class ArabicLigaturizer {
+
+ static boolean isVowel(char s) {
+ return ((s >= 0x064B) && (s <= 0x0655)) || (s == 0x0670);
+ }
+
+ static char charshape(char s, int which)
+ /* which 0=isolated 1=final 2=initial 3=medial */
+ {
+ int l, r, m;
+ if ((s >= 0x0621) && (s <= 0x06D3)) {
+ l = 0;
+ r = chartable.length - 1;
+ while (l <= r) {
+ m = (l + r) / 2;
+ if (s == chartable[m][0]) {
+ return chartable[m][which + 1];
+ }
+ else if (s < chartable[m][0]) {
+ r = m - 1;
+ }
+ else {
+ l = m + 1;
+ }
+ }
+ }
+ else if (s >= 0xfef5 && s <= 0xfefb)
+ return (char)(s + which);
+ return s;
+ }
+
+ static int shapecount(char s) {
+ int l, r, m;
+ if ((s >= 0x0621) && (s <= 0x06D3) && !isVowel(s)) {
+ l = 0;
+ r = chartable.length - 1;
+ while (l <= r) {
+ m = (l + r) / 2;
+ if (s == chartable[m][0]) {
+ return chartable[m].length - 1;
+ }
+ else if (s < chartable[m][0]) {
+ r = m - 1;
+ }
+ else {
+ l = m + 1;
+ }
+ }
+ }
+ else if (s == ZWJ) {
+ return 4;
+ }
+ return 1;
+ }
+
+ static int ligature(char newchar, charstruct oldchar) {
+ /* 0 == no ligature possible; 1 == vowel; 2 == two chars; 3 == Lam+Alef */
+ int retval = 0;
+
+ if (oldchar.basechar == 0)
+ return 0;
+ if (isVowel(newchar)) {
+ retval = 1;
+ if ((oldchar.vowel != 0) && (newchar != SHADDA)) {
+ retval = 2; /* we eliminate the old vowel .. */
+ }
+ switch (newchar) {
+ case SHADDA:
+ if (oldchar.mark1 == 0) {
+ oldchar.mark1 = SHADDA;
+ }
+ else {
+ return 0; /* no ligature possible */
+ }
+ break;
+ case HAMZABELOW:
+ switch (oldchar.basechar) {
+ case ALEF:
+ oldchar.basechar = ALEFHAMZABELOW;
+ retval = 2;
+ break;
+ case LAM_ALEF:
+ oldchar.basechar = LAM_ALEFHAMZABELOW;
+ retval = 2;
+ break;
+ default:
+ oldchar.mark1 = HAMZABELOW;
+ break;
+ }
+ break;
+ case HAMZAABOVE:
+ switch (oldchar.basechar) {
+ case ALEF:
+ oldchar.basechar = ALEFHAMZA;
+ retval = 2;
+ break;
+ case LAM_ALEF:
+ oldchar.basechar = LAM_ALEFHAMZA;
+ retval = 2;
+ break;
+ case WAW:
+ oldchar.basechar = WAWHAMZA;
+ retval = 2;
+ break;
+ case YEH:
+ case ALEFMAKSURA:
+ case FARSIYEH:
+ oldchar.basechar = YEHHAMZA;
+ retval = 2;
+ break;
+ default: /* whatever sense this may make .. */
+ oldchar.mark1 = HAMZAABOVE;
+ break;
+ }
+ break;
+ case MADDA:
+ switch (oldchar.basechar) {
+ case ALEF:
+ oldchar.basechar = ALEFMADDA;
+ retval = 2;
+ break;
+ }
+ break;
+ default:
+ oldchar.vowel = newchar;
+ break;
+ }
+ if (retval == 1) {
+ oldchar.lignum++;
+ }
+ return retval;
+ }
+ if (oldchar.vowel != 0) { /* if we already joined a vowel, we can't join a Hamza */
+ return 0;
+ }
+
+ switch (oldchar.basechar) {
+ case LAM:
+ switch (newchar) {
+ case ALEF:
+ oldchar.basechar = LAM_ALEF;
+ oldchar.numshapes = 2;
+ retval = 3;
+ break;
+ case ALEFHAMZA:
+ oldchar.basechar = LAM_ALEFHAMZA;
+ oldchar.numshapes = 2;
+ retval = 3;
+ break;
+ case ALEFHAMZABELOW:
+ oldchar.basechar = LAM_ALEFHAMZABELOW;
+ oldchar.numshapes = 2;
+ retval = 3;
+ break;
+ case ALEFMADDA:
+ oldchar.basechar = LAM_ALEFMADDA;
+ oldchar.numshapes = 2;
+ retval = 3;
+ break;
+ }
+ break;
+ case 0:
+ oldchar.basechar = newchar;
+ oldchar.numshapes = shapecount(newchar);
+ retval = 1;
+ break;
+ }
+ return retval;
+ }
+
+ static void copycstostring(StringBuffer string, charstruct s, int level) {
+ /* s is a shaped charstruct; i is the index into the string */
+ if (s.basechar == 0)
+ return;
+
+ string.append(s.basechar);
+ s.lignum--;
+ if (s.mark1 != 0) {
+ if ((level & ar_novowel) == 0) {
+ string.append(s.mark1);
+ s.lignum--;
+ }
+ else {
+ s.lignum--;
+ }
+ }
+ if (s.vowel != 0) {
+ if ((level & ar_novowel) == 0) {
+ string.append(s.vowel);
+ s.lignum--;
+ }
+ else { /* vowel elimination */
+ s.lignum--;
+ }
+ }
+// while (s.lignum > 0) { /* NULL-insertion for Langbox-font */
+// string[i] = 0;
+// i++;
+// (s.lignum)--;
+// }
+// return i;
+ }
+
+ // return len
+ static void doublelig(StringBuffer string, int level)
+ /* Ok. We have presentation ligatures in our font. */
+ {
+ int len;
+ int olen = len = string.length();
+ int j = 0, si = 1;
+ char lapresult;
+
+ while (si < olen) {
+ lapresult = 0;
+ if ((level & ar_composedtashkeel) != 0) {
+ switch (string.charAt(j)) {
+ case SHADDA:
+ switch (string.charAt(si)) {
+ case KASRA:
+ lapresult = 0xFC62;
+ break;
+ case FATHA:
+ lapresult = 0xFC60;
+ break;
+ case DAMMA:
+ lapresult = 0xFC61;
+ break;
+ case 0x064C:
+ lapresult = 0xFC5E;
+ break;
+ case 0x064D:
+ lapresult = 0xFC5F;
+ break;
+ }
+ break;
+ case KASRA:
+ if (string.charAt(si) == SHADDA)
+ lapresult = 0xFC62;
+ break;
+ case FATHA:
+ if (string.charAt(si) == SHADDA)
+ lapresult = 0xFC60;
+ break;
+ case DAMMA:
+ if (string.charAt(si) == SHADDA)
+ lapresult = 0xFC61;
+ break;
+ }
+ }
+
+ if ((level & ar_lig) != 0) {
+ switch (string.charAt(j)) {
+ case 0xFEDF: /* LAM initial */
+ switch (string.charAt(si)) {
+ case 0xFE9E:
+ lapresult = 0xFC3F;
+ break; /* JEEM final */
+ case 0xFEA0:
+ lapresult = 0xFCC9;
+ break; /* JEEM medial */
+ case 0xFEA2:
+ lapresult = 0xFC40;
+ break; /* HAH final */
+ case 0xFEA4:
+ lapresult = 0xFCCA;
+ break; /* HAH medial */
+ case 0xFEA6:
+ lapresult = 0xFC41;
+ break; /* KHAH final */
+ case 0xFEA8:
+ lapresult = 0xFCCB;
+ break; /* KHAH medial */
+ case 0xFEE2:
+ lapresult = 0xFC42;
+ break; /* MEEM final */
+ case 0xFEE4:
+ lapresult = 0xFCCC;
+ break; /* MEEM medial */
+ }
+ break;
+ case 0xFE97: /* TEH inital */
+ switch (string.charAt(si)) {
+ case 0xFEA0:
+ lapresult = 0xFCA1;
+ break; /* JEEM medial */
+ case 0xFEA4:
+ lapresult = 0xFCA2;
+ break; /* HAH medial */
+ case 0xFEA8:
+ lapresult = 0xFCA3;
+ break; /* KHAH medial */
+ }
+ break;
+ case 0xFE91: /* BEH inital */
+ switch (string.charAt(si)) {
+ case 0xFEA0:
+ lapresult = 0xFC9C;
+ break; /* JEEM medial */
+ case 0xFEA4:
+ lapresult = 0xFC9D;
+ break; /* HAH medial */
+ case 0xFEA8:
+ lapresult = 0xFC9E;
+ break; /* KHAH medial */
+ }
+ break;
+ case 0xFEE7: /* NOON inital */
+ switch (string.charAt(si)) {
+ case 0xFEA0:
+ lapresult = 0xFCD2;
+ break; /* JEEM initial */
+ case 0xFEA4:
+ lapresult = 0xFCD3;
+ break; /* HAH medial */
+ case 0xFEA8:
+ lapresult = 0xFCD4;
+ break; /* KHAH medial */
+ }
+ break;
+
+ case 0xFEE8: /* NOON medial */
+ switch (string.charAt(si)) {
+ case 0xFEAE:
+ lapresult = 0xFC8A;
+ break; /* REH final */
+ case 0xFEB0:
+ lapresult = 0xFC8B;
+ break; /* ZAIN final */
+ }
+ break;
+ case 0xFEE3: /* MEEM initial */
+ switch (string.charAt(si)) {
+ case 0xFEA0:
+ lapresult = 0xFCCE;
+ break; /* JEEM medial */
+ case 0xFEA4:
+ lapresult = 0xFCCF;
+ break; /* HAH medial */
+ case 0xFEA8:
+ lapresult = 0xFCD0;
+ break; /* KHAH medial */
+ case 0xFEE4:
+ lapresult = 0xFCD1;
+ break; /* MEEM medial */
+ }
+ break;
+
+ case 0xFED3: /* FEH initial */
+ switch (string.charAt(si)) {
+ case 0xFEF2:
+ lapresult = 0xFC32;
+ break; /* YEH final */
+ }
+ break;
+
+ default:
+ break;
+ } /* end switch string[si] */
+ }
+ if (lapresult != 0) {
+ string.setCharAt(j, lapresult);
+ len--;
+ si++; /* jump over one character */
+ /* we'll have to change this, too. */
+ }
+ else {
+ j++;
+ string.setCharAt(j, string.charAt(si));
+ si++;
+ }
+ }
+ string.setLength(len);
+ }
+
+ static boolean connects_to_left(charstruct a) {
+ return a.numshapes > 2;
+ }
+
+ static void shape(char text[], StringBuffer string, int level) {
+ /* string is assumed to be empty and big enough.
+ * text is the original text.
+ * This routine does the basic arabic reshaping.
+ * *len the number of non-null characters.
+ *
+ * Note: We have to unshape each character first!
+ */
+ int join;
+ int which;
+ char nextletter;
+
+ int p = 0; /* initialize for output */
+ charstruct oldchar = new charstruct();
+ charstruct curchar = new charstruct();
+ while (p < text.length) {
+ nextletter = text[p++];
+ //nextletter = unshape (nextletter);
+
+ join = ligature(nextletter, curchar);
+ if (join == 0) { /* shape curchar */
+ int nc = shapecount(nextletter);
+ //(*len)++;
+ if (nc == 1) {
+ which = 0; /* final or isolated */
+ }
+ else {
+ which = 2; /* medial or initial */
+ }
+ if (connects_to_left(oldchar)) {
+ which++;
+ }
+
+ which = which % (curchar.numshapes);
+ curchar.basechar = charshape(curchar.basechar, which);
+
+ /* get rid of oldchar */
+ copycstostring(string, oldchar, level);
+ oldchar = curchar; /* new values in oldchar */
+
+ /* init new curchar */
+ curchar = new charstruct();
+ curchar.basechar = nextletter;
+ curchar.numshapes = nc;
+ curchar.lignum++;
+ // (*len) += unligature (&curchar, level);
+ }
+ else if (join == 1) {
+ }
+ // else
+ // {
+ // (*len) += unligature (&curchar, level);
+ // }
+ // p = g_utf8_next_char (p);
+ }
+
+ /* Handle last char */
+ if (connects_to_left(oldchar))
+ which = 1;
+ else
+ which = 0;
+ which = which % (curchar.numshapes);
+ curchar.basechar = charshape(curchar.basechar, which);
+
+ /* get rid of oldchar */
+ copycstostring(string, oldchar, level);
+ copycstostring(string, curchar, level);
+ }
+
+ static int arabic_shape(char src[], int srcoffset, int srclength, char dest[], int destoffset, int destlength, int level) {
+ char str[] = new char[srclength];
+ for (int k = srclength + srcoffset - 1; k >= srcoffset; --k)
+ str[k - srcoffset] = src[k];
+ StringBuffer string = new StringBuffer(srclength);
+ shape(str, string, level);
+ if ((level & (ar_composedtashkeel | ar_lig)) != 0)
+ doublelig(string, level);
+// string.reverse();
+ System.arraycopy(string.toString().toCharArray(), 0, dest, destoffset, string.length());
+ return string.length();
+ }
+
+ static void processNumbers(char text[], int offset, int length, int options) {
+ int limit = offset + length;
+ if ((options & DIGITS_MASK) != 0) {
+ char digitBase = '\u0030'; // European digits
+ switch (options & DIGIT_TYPE_MASK) {
+ case DIGIT_TYPE_AN:
+ digitBase = '\u0660'; // Arabic-Indic digits
+ break;
+
+ case DIGIT_TYPE_AN_EXTENDED:
+ digitBase = '\u06f0'; // Eastern Arabic-Indic digits (Persian and Urdu)
+ break;
+
+ default:
+ break;
+ }
+
+ switch (options & DIGITS_MASK) {
+ case DIGITS_EN2AN: {
+ int digitDelta = digitBase - '\u0030';
+ for (int i = offset; i < limit; ++i) {
+ char ch = text[i];
+ if (ch <= '\u0039' && ch >= '\u0030') {
+ text[i] += digitDelta;
+ }
+ }
+ }
+ break;
+
+ case DIGITS_AN2EN: {
+ char digitTop = (char)(digitBase + 9);
+ int digitDelta = '\u0030' - digitBase;
+ for (int i = offset; i < limit; ++i) {
+ char ch = text[i];
+ if (ch <= digitTop && ch >= digitBase) {
+ text[i] += digitDelta;
+ }
+ }
+ }
+ break;
+
+ case DIGITS_EN2AN_INIT_LR:
+ shapeToArabicDigitsWithContext(text, 0, length, digitBase, false);
+ break;
+
+ case DIGITS_EN2AN_INIT_AL:
+ shapeToArabicDigitsWithContext(text, 0, length, digitBase, true);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ static void shapeToArabicDigitsWithContext(char[] dest, int start, int length, char digitBase, boolean lastStrongWasAL) {
+ digitBase -= '0'; // move common adjustment out of loop
+
+ int limit = start + length;
+ for(int i = start; i < limit; ++i) {
+ char ch = dest[i];
+ switch (BidiOrder.getDirection(ch)) {
+ case BidiOrder.L:
+ case BidiOrder.R:
+ lastStrongWasAL = false;
+ break;
+ case BidiOrder.AL:
+ lastStrongWasAL = true;
+ break;
+ case BidiOrder.EN:
+ if (lastStrongWasAL && ch <= '\u0039') {
+ dest[i] = (char)(ch + digitBase);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private static final char ALEF = 0x0627;
+ private static final char ALEFHAMZA = 0x0623;
+ private static final char ALEFHAMZABELOW = 0x0625;
+ private static final char ALEFMADDA = 0x0622;
+ private static final char LAM = 0x0644;
+ private static final char HAMZA = 0x0621;
+ private static final char TATWEEL = 0x0640;
+ private static final char ZWJ = 0x200D;
+
+ private static final char HAMZAABOVE = 0x0654;
+ private static final char HAMZABELOW = 0x0655;
+
+ private static final char WAWHAMZA = 0x0624;
+ private static final char YEHHAMZA = 0x0626;
+ private static final char WAW = 0x0648;
+ private static final char ALEFMAKSURA = 0x0649;
+ private static final char YEH = 0x064A;
+ private static final char FARSIYEH = 0x06CC;
+
+ private static final char SHADDA = 0x0651;
+ private static final char KASRA = 0x0650;
+ private static final char FATHA = 0x064E;
+ private static final char DAMMA = 0x064F;
+ private static final char MADDA = 0x0653;
+
+ private static final char LAM_ALEF = 0xFEFB;
+ private static final char LAM_ALEFHAMZA = 0xFEF7;
+ private static final char LAM_ALEFHAMZABELOW = 0xFEF9;
+ private static final char LAM_ALEFMADDA = 0xFEF5;
+
+ private static final char chartable[][] = {
+ {0x0621, 0xFE80}, /* HAMZA */
+ {0x0622, 0xFE81, 0xFE82}, /* ALEF WITH MADDA ABOVE */
+ {0x0623, 0xFE83, 0xFE84}, /* ALEF WITH HAMZA ABOVE */
+ {0x0624, 0xFE85, 0xFE86}, /* WAW WITH HAMZA ABOVE */
+ {0x0625, 0xFE87, 0xFE88}, /* ALEF WITH HAMZA BELOW */
+ {0x0626, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C}, /* YEH WITH HAMZA ABOVE */
+ {0x0627, 0xFE8D, 0xFE8E}, /* ALEF */
+ {0x0628, 0xFE8F, 0xFE90, 0xFE91, 0xFE92}, /* BEH */
+ {0x0629, 0xFE93, 0xFE94}, /* TEH MARBUTA */
+ {0x062A, 0xFE95, 0xFE96, 0xFE97, 0xFE98}, /* TEH */
+ {0x062B, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C}, /* THEH */
+ {0x062C, 0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0}, /* JEEM */
+ {0x062D, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4}, /* HAH */
+ {0x062E, 0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8}, /* KHAH */
+ {0x062F, 0xFEA9, 0xFEAA}, /* DAL */
+ {0x0630, 0xFEAB, 0xFEAC}, /* THAL */
+ {0x0631, 0xFEAD, 0xFEAE}, /* REH */
+ {0x0632, 0xFEAF, 0xFEB0}, /* ZAIN */
+ {0x0633, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4}, /* SEEN */
+ {0x0634, 0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8}, /* SHEEN */
+ {0x0635, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC}, /* SAD */
+ {0x0636, 0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0}, /* DAD */
+ {0x0637, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4}, /* TAH */
+ {0x0638, 0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8}, /* ZAH */
+ {0x0639, 0xFEC9, 0xFECA, 0xFECB, 0xFECC}, /* AIN */
+ {0x063A, 0xFECD, 0xFECE, 0xFECF, 0xFED0}, /* GHAIN */
+ {0x0640, 0x0640, 0x0640, 0x0640, 0x0640}, /* TATWEEL */
+ {0x0641, 0xFED1, 0xFED2, 0xFED3, 0xFED4}, /* FEH */
+ {0x0642, 0xFED5, 0xFED6, 0xFED7, 0xFED8}, /* QAF */
+ {0x0643, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC}, /* KAF */
+ {0x0644, 0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0}, /* LAM */
+ {0x0645, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4}, /* MEEM */
+ {0x0646, 0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8}, /* NOON */
+ {0x0647, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC}, /* HEH */
+ {0x0648, 0xFEED, 0xFEEE}, /* WAW */
+ {0x0649, 0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9}, /* ALEF MAKSURA */
+ {0x064A, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4}, /* YEH */
+ {0x0671, 0xFB50, 0xFB51}, /* ALEF WASLA */
+ {0x0679, 0xFB66, 0xFB67, 0xFB68, 0xFB69}, /* TTEH */
+ {0x067A, 0xFB5E, 0xFB5F, 0xFB60, 0xFB61}, /* TTEHEH */
+ {0x067B, 0xFB52, 0xFB53, 0xFB54, 0xFB55}, /* BEEH */
+ {0x067E, 0xFB56, 0xFB57, 0xFB58, 0xFB59}, /* PEH */
+ {0x067F, 0xFB62, 0xFB63, 0xFB64, 0xFB65}, /* TEHEH */
+ {0x0680, 0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D}, /* BEHEH */
+ {0x0683, 0xFB76, 0xFB77, 0xFB78, 0xFB79}, /* NYEH */
+ {0x0684, 0xFB72, 0xFB73, 0xFB74, 0xFB75}, /* DYEH */
+ {0x0686, 0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D}, /* TCHEH */
+ {0x0687, 0xFB7E, 0xFB7F, 0xFB80, 0xFB81}, /* TCHEHEH */
+ {0x0688, 0xFB88, 0xFB89}, /* DDAL */
+ {0x068C, 0xFB84, 0xFB85}, /* DAHAL */
+ {0x068D, 0xFB82, 0xFB83}, /* DDAHAL */
+ {0x068E, 0xFB86, 0xFB87}, /* DUL */
+ {0x0691, 0xFB8C, 0xFB8D}, /* RREH */
+ {0x0698, 0xFB8A, 0xFB8B}, /* JEH */
+ {0x06A4, 0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D}, /* VEH */
+ {0x06A6, 0xFB6E, 0xFB6F, 0xFB70, 0xFB71}, /* PEHEH */
+ {0x06A9, 0xFB8E, 0xFB8F, 0xFB90, 0xFB91}, /* KEHEH */
+ {0x06AD, 0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6}, /* NG */
+ {0x06AF, 0xFB92, 0xFB93, 0xFB94, 0xFB95}, /* GAF */
+ {0x06B1, 0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D}, /* NGOEH */
+ {0x06B3, 0xFB96, 0xFB97, 0xFB98, 0xFB99}, /* GUEH */
+ {0x06BA, 0xFB9E, 0xFB9F}, /* NOON GHUNNA */
+ {0x06BB, 0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3}, /* RNOON */
+ {0x06BE, 0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD}, /* HEH DOACHASHMEE */
+ {0x06C0, 0xFBA4, 0xFBA5}, /* HEH WITH YEH ABOVE */
+ {0x06C1, 0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9}, /* HEH GOAL */
+ {0x06C5, 0xFBE0, 0xFBE1}, /* KIRGHIZ OE */
+ {0x06C6, 0xFBD9, 0xFBDA}, /* OE */
+ {0x06C7, 0xFBD7, 0xFBD8}, /* U */
+ {0x06C8, 0xFBDB, 0xFBDC}, /* YU */
+ {0x06C9, 0xFBE2, 0xFBE3}, /* KIRGHIZ YU */
+ {0x06CB, 0xFBDE, 0xFBDF}, /* VE */
+ {0x06CC, 0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF}, /* FARSI YEH */
+ {0x06D0, 0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7}, /* E */
+ {0x06D2, 0xFBAE, 0xFBAF}, /* YEH BARREE */
+ {0x06D3, 0xFBB0, 0xFBB1} /* YEH BARREE WITH HAMZA ABOVE */
+ };
+
+ public static final int ar_nothing = 0x0;
+ public static final int ar_novowel = 0x1;
+ public static final int ar_composedtashkeel = 0x4;
+ public static final int ar_lig = 0x8;
+ /**
+ * Digit shaping option: Replace European digits (U+0030...U+0039) by Arabic-Indic digits.
+ */
+ public static final int DIGITS_EN2AN = 0x20;
+
+ /**
+ * Digit shaping option: Replace Arabic-Indic digits by European digits (U+0030...U+0039).
+ */
+ public static final int DIGITS_AN2EN = 0x40;
+
+ /**
+ * Digit shaping option:
+ * Replace European digits (U+0030...U+0039) by Arabic-Indic digits
+ * if the most recent strongly directional character
+ * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC).
+ * The initial state at the start of the text is assumed to be not an Arabic,
+ * letter, so European digits at the start of the text will not change.
+ * Compare to DIGITS_ALEN2AN_INIT_AL.
+ */
+ public static final int DIGITS_EN2AN_INIT_LR = 0x60;
+
+ /**
+ * Digit shaping option:
+ * Replace European digits (U+0030...U+0039) by Arabic-Indic digits
+ * if the most recent strongly directional character
+ * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC).
+ * The initial state at the start of the text is assumed to be an Arabic,
+ * letter, so European digits at the start of the text will change.
+ * Compare to DIGITS_ALEN2AN_INT_LR.
+ */
+ public static final int DIGITS_EN2AN_INIT_AL = 0x80;
+
+ /** Not a valid option value. */
+ private static final int DIGITS_RESERVED = 0xa0;
+
+ /**
+ * Bit mask for digit shaping options.
+ */
+ public static final int DIGITS_MASK = 0xe0;
+
+ /**
+ * Digit type option: Use Arabic-Indic digits (U+0660...U+0669).
+ */
+ public static final int DIGIT_TYPE_AN = 0;
+
+ /**
+ * Digit type option: Use Eastern (Extended) Arabic-Indic digits (U+06f0...U+06f9).
+ */
+ public static final int DIGIT_TYPE_AN_EXTENDED = 0x100;
+
+ /**
+ * Bit mask for digit type options.
+ */
+ public static final int DIGIT_TYPE_MASK = 0x0100; // 0x3f00?
+
+ static class charstruct {
+ char basechar;
+ char mark1; /* has to be initialized to zero */
+ char vowel;
+ int lignum; /* is a ligature with lignum aditional characters */
+ int numshapes = 1;
+ };
+
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/AsianFontMapper.java b/src/main/java/com/lowagie/text/pdf/AsianFontMapper.java
new file mode 100644
index 0000000..484ab0e
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/AsianFontMapper.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2004 by Takenori.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.awt.Font;
+
+import com.lowagie.text.pdf.BaseFont;
+import com.lowagie.text.pdf.DefaultFontMapper;
+
+public class AsianFontMapper extends DefaultFontMapper {
+
+ public static String ChineseSimplifiedFont = "STSong-Light";
+ public static String ChineseSimplifiedEncoding_H = "UniGB-UCS2-H";
+ public static String ChineseSimplifiedEncoding_V = "UniGB-UCS2-V";
+
+ public static String ChineseTraditionalFont_MHei = "MHei-Medium";
+ public static String ChineseTraditionalFont_MSung = "MSung-Light";
+ public static String ChineseTraditionalEncoding_H = "UniCNS-UCS2-H";
+ public static String ChineseTraditionalEncoding_V = "UniCNS-UCS2-V";
+
+ public static String JapaneseFont_Go = "HeiseiKakuGo-W5";
+ public static String JapaneseFont_Min = "HeiseiMin-W3";
+ public static String JapaneseEncoding_H = "UniJIS-UCS2-H";
+ public static String JapaneseEncoding_V = "UniJIS-UCS2-V";
+ public static String JapaneseEncoding_HW_H = "UniJIS-UCS2-HW-H";
+ public static String JapaneseEncoding_HW_V = "UniJIS-UCS2-HW-V";
+
+ public static String KoreanFont_GoThic = "HYGoThic-Medium";
+ public static String KoreanFont_SMyeongJo = "HYSMyeongJo-Medium";
+ public static String KoreanEncoding_H = "UniKS-UCS2-H";
+ public static String KoreanEncoding_V = "UniKS-UCS2-V";
+
+ private String defaultFont;
+ private String encoding;
+
+ public AsianFontMapper(String font, String encoding) {
+ super();
+
+ this.defaultFont = font;
+ this.encoding = encoding;
+ }
+
+ public BaseFont awtToPdf(Font font) {
+ try {
+ BaseFontParameters p = getBaseFontParameters(font.getFontName());
+ if (p != null){
+ return BaseFont.createFont(p.fontName, p.encoding, p.embedded, p.cached, p.ttfAfm, p.pfb);
+ }else{
+ return BaseFont.createFont(defaultFont, encoding, true);
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/BadPdfFormatException.java b/src/main/java/com/lowagie/text/pdf/BadPdfFormatException.java
new file mode 100644
index 0000000..70c4001
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/BadPdfFormatException.java
@@ -0,0 +1,85 @@
+/*
+ * $Id: BadPdfFormatException.java,v 1.55 2006/02/16 16:17:48 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * Signals that a bad PDF format has been used to construct a PdfObject
.
+ *
+ * @see PdfException
+ * @see PdfBoolean
+ * @see PdfNumber
+ * @see PdfString
+ * @see PdfName
+ * @see PdfDictionary
+ */
+
+public class BadPdfFormatException extends PdfException {
+
+ // constructors
+
+/**
+ * Constructs a BadPdfFormatException
whithout a message.
+ */
+
+ BadPdfFormatException() {
+ super();
+ }
+
+/**
+ * Constructs a BadPdfFormatException
with a message.
+ *
+ * @param message a message describing the exception
+ */
+
+ BadPdfFormatException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/Barcode.java b/src/main/java/com/lowagie/text/pdf/Barcode.java
new file mode 100644
index 0000000..5eea0dc
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/Barcode.java
@@ -0,0 +1,477 @@
+/*
+ * $Id: Barcode.java,v 1.20 2006/02/09 18:07:56 psoares33 Exp $
+ *
+ * Copyright 2002-2006 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Image;
+import java.awt.Color;
+/** Base class containing properties and methods commom to all
+ * barcode types.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public abstract class Barcode {
+ /** A type of barcode */
+ public static final int EAN13 = 1;
+ /** A type of barcode */
+ public static final int EAN8 = 2;
+ /** A type of barcode */
+ public static final int UPCA = 3;
+ /** A type of barcode */
+ public static final int UPCE = 4;
+ /** A type of barcode */
+ public static final int SUPP2 = 5;
+ /** A type of barcode */
+ public static final int SUPP5 = 6;
+ /** A type of barcode */
+ public static final int POSTNET = 7;
+ /** A type of barcode */
+ public static final int PLANET = 8;
+ /** A type of barcode */
+ public static final int CODE128 = 9;
+ /** A type of barcode */
+ public static final int CODE128_UCC = 10;
+ /** A type of barcode */
+ public static final int CODE128_RAW = 11;
+ /** A type of barcode */
+ public static final int CODABAR = 12;
+
+ /** The minimum bar width.
+ */
+ protected float x;
+
+ /** The bar multiplier for wide bars or the distance between
+ * bars for Postnet and Planet.
+ */
+ protected float n;
+
+ /** The text font. null
if no text.
+ */
+ protected BaseFont font;
+
+ /** The size of the text or the height of the shorter bar
+ * in Postnet.
+ */
+ protected float size;
+
+ /** If positive, the text distance under the bars. If zero or negative,
+ * the text distance above the bars.
+ */
+ protected float baseline;
+
+ /** The height of the bars.
+ */
+ protected float barHeight;
+
+ /** The text alignment. Can be Element.ALIGN_LEFT
,
+ * Element.ALIGN_CENTER
or Element.ALIGN_RIGHT
.
+ */
+ protected int textAlignment;
+
+ /** The optional checksum generation.
+ */
+ protected boolean generateChecksum;
+
+ /** Shows the generated checksum in the the text.
+ */
+ protected boolean checksumText;
+
+ /** Show the start and stop character '*' in the text for
+ * the barcode 39 or 'ABCD' for codabar.
+ */
+ protected boolean startStopText;
+
+ /** Generates extended barcode 39.
+ */
+ protected boolean extended;
+
+ /** The code to generate.
+ */
+ protected String code = "";
+
+ /** Show the guard bars for barcode EAN.
+ */
+ protected boolean guardBars;
+
+ /** The code type.
+ */
+ protected int codeType;
+
+ /** The ink spreading. */
+ protected float inkSpreading = 0;
+
+ /** Gets the minimum bar width.
+ * @return the minimum bar width
+ */
+ public float getX() {
+ return x;
+ }
+
+ /** Sets the minimum bar width.
+ * @param x the minimum bar width
+ */
+ public void setX(float x) {
+ this.x = x;
+ }
+
+ /** Gets the bar multiplier for wide bars.
+ * @return the bar multiplier for wide bars
+ */
+ public float getN() {
+ return n;
+ }
+
+ /** Sets the bar multiplier for wide bars.
+ * @param n the bar multiplier for wide bars
+ */
+ public void setN(float n) {
+ this.n = n;
+ }
+
+ /** Gets the text font. null
if no text.
+ * @return the text font. null
if no text
+ */
+ public BaseFont getFont() {
+ return font;
+ }
+
+ /** Sets the text font.
+ * @param font the text font. Set to null
to suppress any text
+ */
+ public void setFont(BaseFont font) {
+ this.font = font;
+ }
+
+ /** Gets the size of the text.
+ * @return the size of the text
+ */
+ public float getSize() {
+ return size;
+ }
+
+ /** Sets the size of the text.
+ * @param size the size of the text
+ */
+ public void setSize(float size) {
+ this.size = size;
+ }
+
+ /** Gets the text baseline.
+ * If positive, the text distance under the bars. If zero or negative,
+ * the text distance above the bars.
+ * @return the baseline.
+ */
+ public float getBaseline() {
+ return baseline;
+ }
+
+ /** Sets the text baseline.
+ * If positive, the text distance under the bars. If zero or negative,
+ * the text distance above the bars.
+ * @param baseline the baseline.
+ */
+ public void setBaseline(float baseline) {
+ this.baseline = baseline;
+ }
+
+ /** Gets the height of the bars.
+ * @return the height of the bars
+ */
+ public float getBarHeight() {
+ return barHeight;
+ }
+
+ /** Sets the height of the bars.
+ * @param barHeight the height of the bars
+ */
+ public void setBarHeight(float barHeight) {
+ this.barHeight = barHeight;
+ }
+
+ /** Gets the text alignment. Can be Element.ALIGN_LEFT
,
+ * Element.ALIGN_CENTER
or Element.ALIGN_RIGHT
.
+ * @return the text alignment
+ */
+ public int getTextAlignment() {
+ return textAlignment;
+ }
+
+ /** Sets the text alignment. Can be Element.ALIGN_LEFT
,
+ * Element.ALIGN_CENTER
or Element.ALIGN_RIGHT
.
+ * @param textAlignment the text alignment
+ */
+ public void setTextAlignment(int textAlignment) {
+ this.textAlignment = textAlignment;
+ }
+
+ /** Gets the optional checksum generation.
+ * @return the optional checksum generation
+ */
+ public boolean isGenerateChecksum() {
+ return generateChecksum;
+ }
+
+ /** Setter for property generateChecksum.
+ * @param generateChecksum New value of property generateChecksum.
+ */
+ public void setGenerateChecksum(boolean generateChecksum) {
+ this.generateChecksum = generateChecksum;
+ }
+
+ /** Gets the property to show the generated checksum in the the text.
+ * @return value of property checksumText
+ */
+ public boolean isChecksumText() {
+ return checksumText;
+ }
+
+ /** Sets the property to show the generated checksum in the the text.
+ * @param checksumText new value of property checksumText
+ */
+ public void setChecksumText(boolean checksumText) {
+ this.checksumText = checksumText;
+ }
+
+ /** Sets the property to show the start and stop character '*' in the text for
+ * the barcode 39.
+ * @return value of property startStopText
+ */
+ public boolean isStartStopText() {
+ return startStopText;
+ }
+
+ /** Gets the property to show the start and stop character '*' in the text for
+ * the barcode 39.
+ * @param startStopText new value of property startStopText
+ */
+ public void setStartStopText(boolean startStopText) {
+ this.startStopText = startStopText;
+ }
+
+ /** Gets the property to generate extended barcode 39.
+ * @return value of property extended.
+ */
+ public boolean isExtended() {
+ return extended;
+ }
+
+ /** Sets the property to generate extended barcode 39.
+ * @param extended new value of property extended
+ */
+ public void setExtended(boolean extended) {
+ this.extended = extended;
+ }
+
+ /** Gets the code to generate.
+ * @return the code to generate
+ */
+ public String getCode() {
+ return code;
+ }
+
+ /** Sets the code to generate.
+ * @param code the code to generate
+ */
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ /** Gets the property to show the guard bars for barcode EAN.
+ * @return value of property guardBars
+ */
+ public boolean isGuardBars() {
+ return guardBars;
+ }
+
+ /** Sets the property to show the guard bars for barcode EAN.
+ * @param guardBars new value of property guardBars
+ */
+ public void setGuardBars(boolean guardBars) {
+ this.guardBars = guardBars;
+ }
+
+ /** Gets the code type.
+ * @return the code type
+ */
+ public int getCodeType() {
+ return codeType;
+ }
+
+ /** Sets the code type.
+ * @param codeType the code type
+ */
+ public void setCodeType(int codeType) {
+ this.codeType = codeType;
+ }
+
+ /** Gets the maximum area that the barcode and the text, if
+ * any, will occupy. The lower left corner is always (0, 0).
+ * @return the size the barcode occupies.
+ */
+ public abstract Rectangle getBarcodeSize();
+
+ /** Places the barcode in a PdfContentByte
. The
+ * barcode is always placed at coodinates (0, 0). Use the
+ * translation matrix to move it elsewhere.
+ *
+ * @param cb the
+ *
+ *
+ * barColor
+ * textColor
+ *
+ *
+ *
+ * null
+ * null
+ *
+ *
+ *
+ * barColor
+ * null
+ * barColor
+ *
+ *
+ * null
+ * textColor
+ *
text painted with textColor
+ *
+ *
+ * barColor
+ * textColor
+ * barColor
text painted with textColor
PdfContentByte
where the barcode will be placed
+ * @param barColor the color of the bars. It can be null
+ * @param textColor the color of the text. It can be null
+ * @return the dimensions the barcode occupies
+ */
+ public abstract Rectangle placeBarcode(PdfContentByte cb, Color barColor, Color textColor);
+
+ /** Creates a template with the barcode.
+ * @param cb the PdfContentByte
to create the template. It
+ * serves no other use
+ * @param barColor the color of the bars. It can be null
+ * @param textColor the color of the text. It can be null
+ * @return the template
+ * @see #placeBarcode(PdfContentByte cb, Color barColor, Color textColor)
+ */
+ public PdfTemplate createTemplateWithBarcode(PdfContentByte cb, Color barColor, Color textColor) {
+ PdfTemplate tp = cb.createTemplate(0, 0);
+ Rectangle rect = placeBarcode(tp, barColor, textColor);
+ tp.setBoundingBox(rect);
+ return tp;
+ }
+
+ /** Creates an Image
with the barcode.
+ * @param cb the PdfContentByte
to create the Image
. It
+ * serves no other use
+ * @param barColor the color of the bars. It can be null
+ * @param textColor the color of the text. It can be null
+ * @return the Image
+ * @see #placeBarcode(PdfContentByte cb, Color barColor, Color textColor)
+ */
+ public Image createImageWithBarcode(PdfContentByte cb, Color barColor, Color textColor) {
+ try {
+ return Image.getInstance(createTemplateWithBarcode(cb, barColor, textColor));
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /** Creates a java.awt.Image
. This image only
+ * contains the bars without any text.
+ * @param foreground the color of the bars
+ * @param background the color of the background
+ * @return the image
+ */
+ public abstract java.awt.Image createAwtImage(Color foreground, Color background);
+
+ /** Gets the amount of ink spreading.
+ * @return the ink spreading
+ *
+ */
+ public float getInkSpreading() {
+ return this.inkSpreading;
+ }
+
+ /** Sets the amount of ink spreading. This value will be subtracted
+ * to the width of each bar. The actual value will depend on the ink
+ * and the printing medium.
+ * @param inkSpreading the ink spreading
+ *
+ */
+ public void setInkSpreading(float inkSpreading) {
+ }
+
+ /**
+ * The alternate text to be used, if present.
+ */
+ protected String altText;
+
+ /**
+ * Gets the alternate text.
+ * @return the alternate text
+ */
+ public String getAltText() {
+ return this.altText;
+ }
+
+ /**
+ * Sets the alternate text. If present, this text will be used instead of the
+ * text derived from the supplied code.
+ * @param altText the alternate text
+ */
+ public void setAltText(String altText) {
+ this.altText = altText;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/Barcode128.java b/src/main/java/com/lowagie/text/pdf/Barcode128.java
new file mode 100644
index 0000000..f9fb656
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/Barcode128.java
@@ -0,0 +1,825 @@
+/*
+ * $Id: Barcode128.java,v 1.18 2006/02/09 18:07:56 psoares33 Exp $
+ *
+ * Copyright 2002-2006 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Element;
+import com.lowagie.text.ExceptionConverter;
+import java.awt.Color;
+import java.awt.Image;
+import java.awt.Canvas;
+import java.awt.image.MemoryImageSource;
+
+/**
+ * Implements the code 128 and UCC/EAN-128. Other symbologies are allowed in raw mode.
+ *
+ *
+ * The default parameters are:
+ *
+ * x = 0.8f;
+ * font = BaseFont.createFont("Helvetica", "winansi", false);
+ * size = 8;
+ * baseline = size;
+ * barHeight = size * 3;
+ * textAlignment = Element.ALIGN_CENTER;
+ * codeType = CODE128;
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class Barcode128 extends Barcode{
+
+ /** The bars to generate the code.
+ */
+ static byte BARS[][] =
+ {
+ {2, 1, 2, 2, 2, 2},
+ {2, 2, 2, 1, 2, 2},
+ {2, 2, 2, 2, 2, 1},
+ {1, 2, 1, 2, 2, 3},
+ {1, 2, 1, 3, 2, 2},
+ {1, 3, 1, 2, 2, 2},
+ {1, 2, 2, 2, 1, 3},
+ {1, 2, 2, 3, 1, 2},
+ {1, 3, 2, 2, 1, 2},
+ {2, 2, 1, 2, 1, 3},
+ {2, 2, 1, 3, 1, 2},
+ {2, 3, 1, 2, 1, 2},
+ {1, 1, 2, 2, 3, 2},
+ {1, 2, 2, 1, 3, 2},
+ {1, 2, 2, 2, 3, 1},
+ {1, 1, 3, 2, 2, 2},
+ {1, 2, 3, 1, 2, 2},
+ {1, 2, 3, 2, 2, 1},
+ {2, 2, 3, 2, 1, 1},
+ {2, 2, 1, 1, 3, 2},
+ {2, 2, 1, 2, 3, 1},
+ {2, 1, 3, 2, 1, 2},
+ {2, 2, 3, 1, 1, 2},
+ {3, 1, 2, 1, 3, 1},
+ {3, 1, 1, 2, 2, 2},
+ {3, 2, 1, 1, 2, 2},
+ {3, 2, 1, 2, 2, 1},
+ {3, 1, 2, 2, 1, 2},
+ {3, 2, 2, 1, 1, 2},
+ {3, 2, 2, 2, 1, 1},
+ {2, 1, 2, 1, 2, 3},
+ {2, 1, 2, 3, 2, 1},
+ {2, 3, 2, 1, 2, 1},
+ {1, 1, 1, 3, 2, 3},
+ {1, 3, 1, 1, 2, 3},
+ {1, 3, 1, 3, 2, 1},
+ {1, 1, 2, 3, 1, 3},
+ {1, 3, 2, 1, 1, 3},
+ {1, 3, 2, 3, 1, 1},
+ {2, 1, 1, 3, 1, 3},
+ {2, 3, 1, 1, 1, 3},
+ {2, 3, 1, 3, 1, 1},
+ {1, 1, 2, 1, 3, 3},
+ {1, 1, 2, 3, 3, 1},
+ {1, 3, 2, 1, 3, 1},
+ {1, 1, 3, 1, 2, 3},
+ {1, 1, 3, 3, 2, 1},
+ {1, 3, 3, 1, 2, 1},
+ {3, 1, 3, 1, 2, 1},
+ {2, 1, 1, 3, 3, 1},
+ {2, 3, 1, 1, 3, 1},
+ {2, 1, 3, 1, 1, 3},
+ {2, 1, 3, 3, 1, 1},
+ {2, 1, 3, 1, 3, 1},
+ {3, 1, 1, 1, 2, 3},
+ {3, 1, 1, 3, 2, 1},
+ {3, 3, 1, 1, 2, 1},
+ {3, 1, 2, 1, 1, 3},
+ {3, 1, 2, 3, 1, 1},
+ {3, 3, 2, 1, 1, 1},
+ {3, 1, 4, 1, 1, 1},
+ {2, 2, 1, 4, 1, 1},
+ {4, 3, 1, 1, 1, 1},
+ {1, 1, 1, 2, 2, 4},
+ {1, 1, 1, 4, 2, 2},
+ {1, 2, 1, 1, 2, 4},
+ {1, 2, 1, 4, 2, 1},
+ {1, 4, 1, 1, 2, 2},
+ {1, 4, 1, 2, 2, 1},
+ {1, 1, 2, 2, 1, 4},
+ {1, 1, 2, 4, 1, 2},
+ {1, 2, 2, 1, 1, 4},
+ {1, 2, 2, 4, 1, 1},
+ {1, 4, 2, 1, 1, 2},
+ {1, 4, 2, 2, 1, 1},
+ {2, 4, 1, 2, 1, 1},
+ {2, 2, 1, 1, 1, 4},
+ {4, 1, 3, 1, 1, 1},
+ {2, 4, 1, 1, 1, 2},
+ {1, 3, 4, 1, 1, 1},
+ {1, 1, 1, 2, 4, 2},
+ {1, 2, 1, 1, 4, 2},
+ {1, 2, 1, 2, 4, 1},
+ {1, 1, 4, 2, 1, 2},
+ {1, 2, 4, 1, 1, 2},
+ {1, 2, 4, 2, 1, 1},
+ {4, 1, 1, 2, 1, 2},
+ {4, 2, 1, 1, 1, 2},
+ {4, 2, 1, 2, 1, 1},
+ {2, 1, 2, 1, 4, 1},
+ {2, 1, 4, 1, 2, 1},
+ {4, 1, 2, 1, 2, 1},
+ {1, 1, 1, 1, 4, 3},
+ {1, 1, 1, 3, 4, 1},
+ {1, 3, 1, 1, 4, 1},
+ {1, 1, 4, 1, 1, 3},
+ {1, 1, 4, 3, 1, 1},
+ {4, 1, 1, 1, 1, 3},
+ {4, 1, 1, 3, 1, 1},
+ {1, 1, 3, 1, 4, 1},
+ {1, 1, 4, 1, 3, 1},
+ {3, 1, 1, 1, 4, 1},
+ {4, 1, 1, 1, 3, 1},
+ {2, 1, 1, 4, 1, 2},
+ {2, 1, 1, 2, 1, 4},
+ {2, 1, 1, 2, 3, 2}
+ };
+
+ /** The stop bars.
+ */
+ static byte BARS_STOP[] = {2, 3, 3, 1, 1, 1, 2};
+ /** The charset code change.
+ */
+ public static final char CODE_AB_TO_C = 99;
+ /** The charset code change.
+ */
+ public static final char CODE_AC_TO_B = 100;
+ /** The charset code change.
+ */
+ public static final char CODE_BC_TO_A = 101;
+ /** The code for UCC/EAN-128.
+ */
+ public static final char FNC1_INDEX = 102;
+ /** The start code.
+ */
+ public static final char START_A = 103;
+ /** The start code.
+ */
+ public static final char START_B = 104;
+ /** The start code.
+ */
+ public static final char START_C = 105;
+
+ public static final char FNC1 = '\u00ca';
+ public static final char DEL = '\u00c3';
+ public static final char FNC3 = '\u00c4';
+ public static final char FNC2 = '\u00c5';
+ public static final char SHIFT = '\u00c6';
+ public static final char CODE_C = '\u00c7';
+ public static final char CODE_A = '\u00c8';
+ public static final char FNC4 = '\u00c8';
+ public static final char STARTA = '\u00cb';
+ public static final char STARTB = '\u00cc';
+ public static final char STARTC = '\u00cd';
+
+ private static final IntHashtable ais = new IntHashtable();
+ /** Creates new Barcode128 */
+ public Barcode128() {
+ try {
+ x = 0.8f;
+ font = BaseFont.createFont("Helvetica", "winansi", false);
+ size = 8;
+ baseline = size;
+ barHeight = size * 3;
+ textAlignment = Element.ALIGN_CENTER;
+ codeType = CODE128;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Removes the FNC1 codes in the text.
+ * @param code the text to clean
+ * @return the cleaned text
+ */
+ public static String removeFNC1(String code) {
+ int len = code.length();
+ StringBuffer buf = new StringBuffer(len);
+ for (int k = 0; k < len; ++k) {
+ char c = code.charAt(k);
+ if (c >= 32 && c <= 126)
+ buf.append(c);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Gets the human readable text of a sequence of AI.
+ * @param code the text
+ * @return the human readable text
+ */
+ public static String getHumanReadableUCCEAN(String code) {
+ StringBuffer buf = new StringBuffer();
+ String fnc1 = String.valueOf(FNC1);
+ try {
+ while (true) {
+ if (code.startsWith(fnc1)) {
+ code = code.substring(1);
+ continue;
+ }
+ int n = 0;
+ int idlen = 0;
+ for (int k = 2; k < 5; ++k) {
+ if (code.length() < k)
+ break;
+ if ((n = ais.get(Integer.parseInt(code.substring(0, k)))) != 0) {
+ idlen = k;
+ break;
+ }
+ }
+ if (idlen == 0)
+ break;
+ buf.append('(').append(code.substring(0, idlen)).append(')');
+ code = code.substring(idlen);
+ if (n > 0) {
+ n -= idlen;
+ if (code.length() <= n)
+ break;
+ buf.append(removeFNC1(code.substring(0, n)));
+ code = code.substring(n);
+ }
+ else {
+ int idx = code.indexOf(FNC1);
+ if (idx < 0)
+ break;
+ buf.append(code.substring(0,idx));
+ code = code.substring(idx + 1);
+ }
+ }
+ }
+ catch (Exception e) {
+ //empty
+ }
+ buf.append(removeFNC1(code));
+ return buf.toString();
+ }
+
+ /** Returns true
if the next numDigits
+ * starting from index textIndex
are numeric skipping any FNC1.
+ * @param text the text to check
+ * @param textIndex where to check from
+ * @param numDigits the number of digits to check
+ * @return the check result
+ */
+ static boolean isNextDigits(String text, int textIndex, int numDigits) {
+ int len = text.length();
+ while (textIndex < len && numDigits > 0) {
+ if (text.charAt(textIndex) == FNC1) {
+ ++textIndex;
+ continue;
+ }
+ int n = Math.min(2, numDigits);
+ if (textIndex + n > len)
+ return false;
+ while (n-- > 0) {
+ char c = text.charAt(textIndex++);
+ if (c < '0' || c > '9')
+ return false;
+ --numDigits;
+ }
+ }
+ return numDigits == 0;
+ }
+
+ /** Packs the digits for charset C also considering FNC1. It assumes that all the parameters
+ * are valid.
+ * @param text the text to pack
+ * @param textIndex where to pack from
+ * @param numDigits the number of digits to pack. It is always an even number
+ * @return the packed digits, two digits per character
+ */
+ static String getPackedRawDigits(String text, int textIndex, int numDigits) {
+ String out = "";
+ int start = textIndex;
+ while (numDigits > 0) {
+ if (text.charAt(textIndex) == FNC1) {
+ out += FNC1_INDEX;
+ ++textIndex;
+ continue;
+ }
+ numDigits -= 2;
+ int c1 = text.charAt(textIndex++) - '0';
+ int c2 = text.charAt(textIndex++) - '0';
+ out += (char)(c1 * 10 + c2);
+ }
+ return (char)(textIndex - start) + out;
+ }
+
+ /** Converts the human readable text to the characters needed to
+ * create a barcode. Some optimization is done to get the shortest code.
+ * @param text the text to convert
+ * @param ucc true
if it is an UCC/EAN-128. In this case
+ * the character FNC1 is added
+ * @return the code ready to be fed to getBarsCode128Raw()
+ */
+ public static String getRawText(String text, boolean ucc) {
+ String out = "";
+ int tLen = text.length();
+ if (tLen == 0) {
+ out += START_B;
+ if (ucc)
+ out += FNC1_INDEX;
+ return out;
+ }
+ int c = 0;
+ for (int k = 0; k < tLen; ++k) {
+ c = text.charAt(k);
+ if (c > 127 && c != FNC1)
+ throw new RuntimeException("There are illegal characters for barcode 128 in '" + text + "'.");
+ }
+ c = text.charAt(0);
+ char currentCode = START_B;
+ int index = 0;
+ if (isNextDigits(text, index, 2)) {
+ currentCode = START_C;
+ out += currentCode;
+ if (ucc)
+ out += FNC1_INDEX;
+ String out2 = getPackedRawDigits(text, index, 2);
+ index += (int)out2.charAt(0);
+ out += out2.substring(1);
+ }
+ else if (c < ' ') {
+ currentCode = START_A;
+ out += currentCode;
+ if (ucc)
+ out += FNC1_INDEX;
+ out += (char)(c + 64);
+ ++index;
+ }
+ else {
+ out += currentCode;
+ if (ucc)
+ out += FNC1_INDEX;
+ if (c == FNC1)
+ out += FNC1_INDEX;
+ else
+ out += (char)(c - ' ');
+ ++index;
+ }
+ while (index < tLen) {
+ switch (currentCode) {
+ case START_A:
+ {
+ if (isNextDigits(text, index, 4)) {
+ currentCode = START_C;
+ out += CODE_AB_TO_C;
+ String out2 = getPackedRawDigits(text, index, 4);
+ index += (int)out2.charAt(0);
+ out += out2.substring(1);
+ }
+ else {
+ c = text.charAt(index++);
+ if (c == FNC1)
+ out += FNC1_INDEX;
+ else if (c > '_') {
+ currentCode = START_B;
+ out += CODE_AC_TO_B;
+ out += (char)(c - ' ');
+ }
+ else if (c < ' ')
+ out += (char)(c + 64);
+ else
+ out += (char)(c - ' ');
+ }
+ }
+ break;
+ case START_B:
+ {
+ if (isNextDigits(text, index, 4)) {
+ currentCode = START_C;
+ out += CODE_AB_TO_C;
+ String out2 = getPackedRawDigits(text, index, 4);
+ index += (int)out2.charAt(0);
+ out += out2.substring(1);
+ }
+ else {
+ c = text.charAt(index++);
+ if (c == FNC1)
+ out += FNC1_INDEX;
+ else if (c < ' ') {
+ currentCode = START_A;
+ out += CODE_BC_TO_A;
+ out += (char)(c + 64);
+ }
+ else {
+ out += (char)(c - ' ');
+ }
+ }
+ }
+ break;
+ case START_C:
+ {
+ if (isNextDigits(text, index, 2)) {
+ String out2 = getPackedRawDigits(text, index, 2);
+ index += (int)out2.charAt(0);
+ out += out2.substring(1);
+ }
+ else {
+ c = text.charAt(index++);
+ if (c == FNC1)
+ out += FNC1_INDEX;
+ else if (c < ' ') {
+ currentCode = START_A;
+ out += CODE_BC_TO_A;
+ out += (char)(c + 64);
+ }
+ else {
+ currentCode = START_B;
+ out += CODE_AC_TO_B;
+ out += (char)(c - ' ');
+ }
+ }
+ }
+ break;
+ }
+ }
+ return out;
+ }
+
+ /** Generates the bars. The input has the actual barcodes, not
+ * the human readable text.
+ * @param text the barcode
+ * @return the bars
+ */
+ public static byte[] getBarsCode128Raw(String text) {
+ int idx = text.indexOf('\uffff');
+ if (idx >= 0)
+ text = text.substring(0, idx);
+ int chk = text.charAt(0);
+ for (int k = 1; k < text.length(); ++k)
+ chk += k * text.charAt(k);
+ chk = chk % 103;
+ text += (char)chk;
+ byte bars[] = new byte[(text.length() + 1) * 6 + 7];
+ int k;
+ for (k = 0; k < text.length(); ++k)
+ System.arraycopy(BARS[text.charAt(k)], 0, bars, k * 6, 6);
+ System.arraycopy(BARS_STOP, 0, bars, k * 6, 7);
+ return bars;
+ }
+
+ /** Gets the maximum area that the barcode and the text, if
+ * any, will occupy. The lower left corner is always (0, 0).
+ * @return the size the barcode occupies.
+ */
+ public Rectangle getBarcodeSize() {
+ float fontX = 0;
+ float fontY = 0;
+ String fullCode;
+ if (font != null) {
+ if (baseline > 0)
+ fontY = baseline - font.getFontDescriptor(BaseFont.DESCENT, size);
+ else
+ fontY = -baseline + size;
+ if (codeType == CODE128_RAW) {
+ int idx = code.indexOf('\uffff');
+ if (idx < 0)
+ fullCode = "";
+ else
+ fullCode = code.substring(idx + 1);
+ }
+ else if (codeType == CODE128_UCC)
+ fullCode = getHumanReadableUCCEAN(code);
+ else
+ fullCode = removeFNC1(code);
+ fontX = font.getWidthPoint(altText != null ? altText : fullCode, size);
+ }
+ if (codeType == CODE128_RAW) {
+ int idx = code.indexOf('\uffff');
+ if (idx >= 0)
+ fullCode = code.substring(0, idx);
+ else
+ fullCode = code;
+ }
+ else {
+ fullCode = getRawText(code, codeType == CODE128_UCC);
+ }
+ int len = fullCode.length();
+ float fullWidth = (len + 2) * 11 * x + 2 * x;
+ fullWidth = Math.max(fullWidth, fontX);
+ float fullHeight = barHeight + fontY;
+ return new Rectangle(fullWidth, fullHeight);
+ }
+
+ /** Places the barcode in a PdfContentByte
. The
+ * barcode is always placed at coodinates (0, 0). Use the
+ * translation matrix to move it elsewhere.
+ *
+ * @param cb the
+ *
+ *
+ * barColor
+ * textColor
+ *
+ *
+ *
+ * null
+ * null
+ *
+ *
+ *
+ * barColor
+ * null
+ * barColor
+ *
+ *
+ * null
+ * textColor
+ *
text painted with textColor
+ *
+ *
+ * barColor
+ * textColor
+ * barColor
text painted with textColor
PdfContentByte
where the barcode will be placed
+ * @param barColor the color of the bars. It can be null
+ * @param textColor the color of the text. It can be null
+ * @return the dimensions the barcode occupies
+ */
+ public Rectangle placeBarcode(PdfContentByte cb, Color barColor, Color textColor) {
+ String fullCode;
+ if (codeType == CODE128_RAW) {
+ int idx = code.indexOf('\uffff');
+ if (idx < 0)
+ fullCode = "";
+ else
+ fullCode = code.substring(idx + 1);
+ }
+ else if (codeType == CODE128_UCC)
+ fullCode = getHumanReadableUCCEAN(code);
+ else
+ fullCode = removeFNC1(code);
+ float fontX = 0;
+ if (font != null) {
+ fontX = font.getWidthPoint(fullCode = altText != null ? altText : fullCode, size);
+ }
+ String bCode;
+ if (codeType == CODE128_RAW) {
+ int idx = code.indexOf('\uffff');
+ if (idx >= 0)
+ bCode = code.substring(0, idx);
+ else
+ bCode = code;
+ }
+ else {
+ bCode = getRawText(code, codeType == CODE128_UCC);
+ }
+ int len = bCode.length();
+ float fullWidth = (len + 2) * 11 * x + 2 * x;
+ float barStartX = 0;
+ float textStartX = 0;
+ switch (textAlignment) {
+ case Element.ALIGN_LEFT:
+ break;
+ case Element.ALIGN_RIGHT:
+ if (fontX > fullWidth)
+ barStartX = fontX - fullWidth;
+ else
+ textStartX = fullWidth - fontX;
+ break;
+ default:
+ if (fontX > fullWidth)
+ barStartX = (fontX - fullWidth) / 2;
+ else
+ textStartX = (fullWidth - fontX) / 2;
+ break;
+ }
+ float barStartY = 0;
+ float textStartY = 0;
+ if (font != null) {
+ if (baseline <= 0)
+ textStartY = barHeight - baseline;
+ else {
+ textStartY = -font.getFontDescriptor(BaseFont.DESCENT, size);
+ barStartY = textStartY + baseline;
+ }
+ }
+ byte bars[] = getBarsCode128Raw(bCode);
+ boolean print = true;
+ if (barColor != null)
+ cb.setColorFill(barColor);
+ for (int k = 0; k < bars.length; ++k) {
+ float w = bars[k] * x;
+ if (print)
+ cb.rectangle(barStartX, barStartY, w - inkSpreading, barHeight);
+ print = !print;
+ barStartX += w;
+ }
+ cb.fill();
+ if (font != null) {
+ if (textColor != null)
+ cb.setColorFill(textColor);
+ cb.beginText();
+ cb.setFontAndSize(font, size);
+ cb.setTextMatrix(textStartX, textStartY);
+ cb.showText(fullCode);
+ cb.endText();
+ }
+ return getBarcodeSize();
+ }
+
+ /** Creates a java.awt.Image
. This image only
+ * contains the bars without any text.
+ * @param foreground the color of the bars
+ * @param background the color of the background
+ * @return the image
+ */
+ public java.awt.Image createAwtImage(Color foreground, Color background) {
+ int f = foreground.getRGB();
+ int g = background.getRGB();
+ Canvas canvas = new Canvas();
+ String bCode;
+ if (codeType == CODE128_RAW) {
+ int idx = code.indexOf('\uffff');
+ if (idx >= 0)
+ bCode = code.substring(0, idx);
+ else
+ bCode = code;
+ }
+ else {
+ bCode = getRawText(code, codeType == CODE128_UCC);
+ }
+ int len = bCode.length();
+ int fullWidth = (len + 2) * 11 + 2;
+ byte bars[] = getBarsCode128Raw(bCode);
+
+ boolean print = true;
+ int ptr = 0;
+ int height = (int)barHeight;
+ int pix[] = new int[fullWidth * height];
+ for (int k = 0; k < bars.length; ++k) {
+ int w = bars[k];
+ int c = g;
+ if (print)
+ c = f;
+ print = !print;
+ for (int j = 0; j < w; ++j)
+ pix[ptr++] = c;
+ }
+ for (int k = fullWidth; k < pix.length; k += fullWidth) {
+ System.arraycopy(pix, 0, pix, k, fullWidth);
+ }
+ Image img = canvas.createImage(new MemoryImageSource(fullWidth, height, pix, 0, fullWidth));
+
+ return img;
+ }
+
+ /**
+ * Sets the code to generate. If it's an UCC code and starts with '(' it will
+ * be split by the AI. This code in UCC mode is valid:
+ * (01)00000090311314(10)ABC123(15)060916
+ * @param code the code to generate
+ */
+ public void setCode(String code) {
+ if (getCodeType() == Barcode128.CODE128_UCC && code.startsWith("(")) {
+ int idx = 0;
+ String ret = "";
+ while (idx >= 0) {
+ int end = code.indexOf(')', idx);
+ if (end < 0)
+ throw new IllegalArgumentException("Badly formed UCC string: " + code);
+ String sai = code.substring(idx + 1, end);
+ if (sai.length() < 2)
+ throw new IllegalArgumentException("AI too short: (" + sai + ")");
+ int ai = Integer.parseInt(sai);
+ int len = ais.get(ai);
+ if (len == 0)
+ throw new IllegalArgumentException("AI not found: (" + sai + ")");
+ sai = String.valueOf(ai);
+ if (sai.length() == 1)
+ sai = "0" + sai;
+ idx = code.indexOf('(', end);
+ int next = (idx < 0 ? code.length() : idx);
+ ret += sai + code.substring(end + 1, next);
+ if (len < 0) {
+ if (idx >= 0)
+ ret += FNC1;
+ }
+ else if (next - end - 1 + sai.length() != len)
+ throw new IllegalArgumentException("Invalid AI length: (" + sai + ")");
+ }
+ super.setCode(ret);
+ }
+ else
+ super.setCode(code);
+ }
+
+ static {
+ ais.put(0, 20);
+ ais.put(1, 16);
+ ais.put(2, 16);
+ ais.put(10, -1);
+ ais.put(11, 9);
+ ais.put(12, 8);
+ ais.put(13, 8);
+ ais.put(15, 8);
+ ais.put(17, 8);
+ ais.put(20, 4);
+ ais.put(21, -1);
+ ais.put(22, -1);
+ ais.put(23, -1);
+ ais.put(240, -1);
+ ais.put(241, -1);
+ ais.put(250, -1);
+ ais.put(251, -1);
+ ais.put(252, -1);
+ ais.put(30, -1);
+ for (int k = 3100; k < 3700; ++k)
+ ais.put(k, 10);
+ ais.put(37, -1);
+ for (int k = 3900; k < 3940; ++k)
+ ais.put(k, -1);
+ ais.put(400, -1);
+ ais.put(401, -1);
+ ais.put(402, 20);
+ ais.put(403, -1);
+ for (int k = 410; k < 416; ++k)
+ ais.put(k, 16);
+ ais.put(420, -1);
+ ais.put(421, -1);
+ ais.put(422, 6);
+ ais.put(423, -1);
+ ais.put(424, 6);
+ ais.put(425, 6);
+ ais.put(426, 6);
+ ais.put(7001, 17);
+ ais.put(7002, -1);
+ for (int k = 7030; k < 704; ++k)
+ ais.put(k, -1);
+ ais.put(8001, 18);
+ ais.put(8002, -1);
+ ais.put(8003, -1);
+ ais.put(8004, -1);
+ ais.put(8005, 10);
+ ais.put(8006, 22);
+ ais.put(8007, -1);
+ ais.put(8008, -1);
+ ais.put(8018, 22);
+ ais.put(8020, -1);
+ ais.put(8100, 10);
+ ais.put(8101, 14);
+ ais.put(8102, 6);
+ for (int k = 90; k < 100; ++k)
+ ais.put(k, -1);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/Barcode39.java b/src/main/java/com/lowagie/text/pdf/Barcode39.java
new file mode 100644
index 0000000..cf9ecdf
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/Barcode39.java
@@ -0,0 +1,392 @@
+/*
+ * $Id: Barcode39.java,v 1.18 2006/02/09 18:07:56 psoares33 Exp $
+ *
+ * Copyright 2002-2006 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Element;
+import com.lowagie.text.Rectangle;
+import java.awt.Color;
+import java.awt.Image;
+import java.awt.Canvas;
+import java.awt.image.MemoryImageSource;
+
+/** Implements the code 39 and code 39 extended. The default parameters are:
+ *
+ *x = 0.8f;
+ *n = 2;
+ *font = BaseFont.createFont("Helvetica", "winansi", false);
+ *size = 8;
+ *baseline = size;
+ *barHeight = size * 3;
+ *textAlignment = Element.ALIGN_CENTER;
+ *generateChecksum = false;
+ *checksumText = false;
+ *startStopText = true;
+ *extended = false;
+ *
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class Barcode39 extends Barcode{
+
+ /** The bars to generate the code.
+ */
+ static byte BARS[][] =
+ {
+ {0,0,0,1,1,0,1,0,0},
+ {1,0,0,1,0,0,0,0,1},
+ {0,0,1,1,0,0,0,0,1},
+ {1,0,1,1,0,0,0,0,0},
+ {0,0,0,1,1,0,0,0,1},
+ {1,0,0,1,1,0,0,0,0},
+ {0,0,1,1,1,0,0,0,0},
+ {0,0,0,1,0,0,1,0,1},
+ {1,0,0,1,0,0,1,0,0},
+ {0,0,1,1,0,0,1,0,0},
+ {1,0,0,0,0,1,0,0,1},
+ {0,0,1,0,0,1,0,0,1},
+ {1,0,1,0,0,1,0,0,0},
+ {0,0,0,0,1,1,0,0,1},
+ {1,0,0,0,1,1,0,0,0},
+ {0,0,1,0,1,1,0,0,0},
+ {0,0,0,0,0,1,1,0,1},
+ {1,0,0,0,0,1,1,0,0},
+ {0,0,1,0,0,1,1,0,0},
+ {0,0,0,0,1,1,1,0,0},
+ {1,0,0,0,0,0,0,1,1},
+ {0,0,1,0,0,0,0,1,1},
+ {1,0,1,0,0,0,0,1,0},
+ {0,0,0,0,1,0,0,1,1},
+ {1,0,0,0,1,0,0,1,0},
+ {0,0,1,0,1,0,0,1,0},
+ {0,0,0,0,0,0,1,1,1},
+ {1,0,0,0,0,0,1,1,0},
+ {0,0,1,0,0,0,1,1,0},
+ {0,0,0,0,1,0,1,1,0},
+ {1,1,0,0,0,0,0,0,1},
+ {0,1,1,0,0,0,0,0,1},
+ {1,1,1,0,0,0,0,0,0},
+ {0,1,0,0,1,0,0,0,1},
+ {1,1,0,0,1,0,0,0,0},
+ {0,1,1,0,1,0,0,0,0},
+ {0,1,0,0,0,0,1,0,1},
+ {1,1,0,0,0,0,1,0,0},
+ {0,1,1,0,0,0,1,0,0},
+ {0,1,0,1,0,1,0,0,0},
+ {0,1,0,1,0,0,0,1,0},
+ {0,1,0,0,0,1,0,1,0},
+ {0,0,0,1,0,1,0,1,0},
+ {0,1,0,0,1,0,1,0,0}
+ };
+
+ /** The index chars to BARS
.
+ */
+ static String CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*";
+
+ /** The character combinations to make the code 39 extended.
+ */
+ static String EXTENDED = "%U" +
+ "$A$B$C$D$E$F$G$H$I$J$K$L$M$N$O$P$Q$R$S$T$U$V$W$X$Y$Z" +
+ "%A%B%C%D%E /A/B/C/D/E/F/G/H/I/J/K/L - ./O" +
+ " 0 1 2 3 4 5 6 7 8 9/Z%F%G%H%I%J%V" +
+ " A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" +
+ "%K%L%M%N%O%W" +
+ "+A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z" +
+ "%P%Q%R%S%T";
+
+ /** Creates a new Barcode39.
+ */
+ public Barcode39() {
+ try {
+ x = 0.8f;
+ n = 2;
+ font = BaseFont.createFont("Helvetica", "winansi", false);
+ size = 8;
+ baseline = size;
+ barHeight = size * 3;
+ textAlignment = Element.ALIGN_CENTER;
+ generateChecksum = false;
+ checksumText = false;
+ startStopText = true;
+ extended = false;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /** Creates the bars.
+ * @param text the text to create the bars. This text does not include the start and
+ * stop characters
+ * @return the bars
+ */
+ public static byte[] getBarsCode39(String text) {
+ text = "*" + text + "*";
+ byte bars[] = new byte[text.length() * 10 - 1];
+ for (int k = 0; k < text.length(); ++k) {
+ int idx = CHARS.indexOf(text.charAt(k));
+ if (idx < 0)
+ throw new IllegalArgumentException("The character '" + text.charAt(k) + "' is illegal in code 39.");
+ System.arraycopy(BARS[idx], 0, bars, k * 10, 9);
+ }
+ return bars;
+ }
+
+ /** Converts the extended text into a normal, escaped text,
+ * ready to generate bars.
+ * @param text the extended text
+ * @return the escaped text
+ */
+ public static String getCode39Ex(String text) {
+ String out = "";
+ for (int k = 0; k < text.length(); ++k) {
+ char c = text.charAt(k);
+ if (c > 127)
+ throw new IllegalArgumentException("The character '" + c + "' is illegal in code 39 extended.");
+ char c1 = EXTENDED.charAt(c * 2);
+ char c2 = EXTENDED.charAt(c * 2 + 1);
+ if (c1 != ' ')
+ out += c1;
+ out += c2;
+ }
+ return out;
+ }
+
+ /** Calculates the checksum.
+ * @param text the text
+ * @return the checksum
+ */
+ static char getChecksum(String text) {
+ int chk = 0;
+ for (int k = 0; k < text.length(); ++k) {
+ int idx = CHARS.indexOf(text.charAt(k));
+ if (idx < 0)
+ throw new IllegalArgumentException("The character '" + text.charAt(k) + "' is illegal in code 39.");
+ chk += idx;
+ }
+ return CHARS.charAt(chk % 43);
+ }
+
+ /** Gets the maximum area that the barcode and the text, if
+ * any, will occupy. The lower left corner is always (0, 0).
+ * @return the size the barcode occupies.
+ */
+ public Rectangle getBarcodeSize() {
+ float fontX = 0;
+ float fontY = 0;
+ if (font != null) {
+ if (baseline > 0)
+ fontY = baseline - font.getFontDescriptor(BaseFont.DESCENT, size);
+ else
+ fontY = -baseline + size;
+ String fullCode = code;
+ if (generateChecksum && checksumText)
+ fullCode += getChecksum(fullCode);
+ if (startStopText)
+ fullCode = "*" + fullCode + "*";
+ fontX = font.getWidthPoint(altText != null ? altText : fullCode, size);
+ }
+ String fullCode = code;
+ if (extended)
+ fullCode = getCode39Ex(code);
+ int len = fullCode.length() + 2;
+ if (generateChecksum)
+ ++len;
+ float fullWidth = len * (6 * x + 3 * x * n) + (len - 1) * x;
+ fullWidth = Math.max(fullWidth, fontX);
+ float fullHeight = barHeight + fontY;
+ return new Rectangle(fullWidth, fullHeight);
+ }
+
+ /** Places the barcode in a PdfContentByte
. The
+ * barcode is always placed at coodinates (0, 0). Use the
+ * translation matrix to move it elsewhere.
+ *
+ * @param cb the
+ *
+ *
+ * barColor
+ * textColor
+ *
+ *
+ *
+ * null
+ * null
+ *
+ *
+ *
+ * barColor
+ * null
+ * barColor
+ *
+ *
+ * null
+ * textColor
+ *
text painted with textColor
+ *
+ *
+ * barColor
+ * textColor
+ * barColor
text painted with textColor
PdfContentByte
where the barcode will be placed
+ * @param barColor the color of the bars. It can be null
+ * @param textColor the color of the text. It can be null
+ * @return the dimensions the barcode occupies
+ */
+ public Rectangle placeBarcode(PdfContentByte cb, Color barColor, Color textColor) {
+ String fullCode = code;
+ float fontX = 0;
+ if (font != null) {
+ if (generateChecksum && checksumText)
+ fullCode += getChecksum(fullCode);
+ if (startStopText)
+ fullCode = "*" + fullCode + "*";
+ fontX = font.getWidthPoint(fullCode = altText != null ? altText : fullCode, size);
+ }
+ String bCode = code;
+ if (extended)
+ bCode = getCode39Ex(code);
+ if (generateChecksum)
+ bCode += getChecksum(bCode);
+ int len = bCode.length() + 2;
+ float fullWidth = len * (6 * x + 3 * x * n) + (len - 1) * x;
+ float barStartX = 0;
+ float textStartX = 0;
+ switch (textAlignment) {
+ case Element.ALIGN_LEFT:
+ break;
+ case Element.ALIGN_RIGHT:
+ if (fontX > fullWidth)
+ barStartX = fontX - fullWidth;
+ else
+ textStartX = fullWidth - fontX;
+ break;
+ default:
+ if (fontX > fullWidth)
+ barStartX = (fontX - fullWidth) / 2;
+ else
+ textStartX = (fullWidth - fontX) / 2;
+ break;
+ }
+ float barStartY = 0;
+ float textStartY = 0;
+ if (font != null) {
+ if (baseline <= 0)
+ textStartY = barHeight - baseline;
+ else {
+ textStartY = -font.getFontDescriptor(BaseFont.DESCENT, size);
+ barStartY = textStartY + baseline;
+ }
+ }
+ byte bars[] = getBarsCode39(bCode);
+ boolean print = true;
+ if (barColor != null)
+ cb.setColorFill(barColor);
+ for (int k = 0; k < bars.length; ++k) {
+ float w = (bars[k] == 0 ? x : x * n);
+ if (print)
+ cb.rectangle(barStartX, barStartY, w - inkSpreading, barHeight);
+ print = !print;
+ barStartX += w;
+ }
+ cb.fill();
+ if (font != null) {
+ if (textColor != null)
+ cb.setColorFill(textColor);
+ cb.beginText();
+ cb.setFontAndSize(font, size);
+ cb.setTextMatrix(textStartX, textStartY);
+ cb.showText(fullCode);
+ cb.endText();
+ }
+ return getBarcodeSize();
+ }
+
+ /** Creates a java.awt.Image
. This image only
+ * contains the bars without any text.
+ * @param foreground the color of the bars
+ * @param background the color of the background
+ * @return the image
+ */
+ public java.awt.Image createAwtImage(Color foreground, Color background) {
+ int f = foreground.getRGB();
+ int g = background.getRGB();
+ Canvas canvas = new Canvas();
+
+ String bCode = code;
+ if (extended)
+ bCode = getCode39Ex(code);
+ if (generateChecksum)
+ bCode += getChecksum(bCode);
+ int len = bCode.length() + 2;
+ int nn = (int)n;
+ int fullWidth = len * (6 + 3 * nn) + (len - 1);
+ byte bars[] = getBarsCode39(bCode);
+ boolean print = true;
+ int ptr = 0;
+ int height = (int)barHeight;
+ int pix[] = new int[fullWidth * height];
+ for (int k = 0; k < bars.length; ++k) {
+ int w = (bars[k] == 0 ? 1 : nn);
+ int c = g;
+ if (print)
+ c = f;
+ print = !print;
+ for (int j = 0; j < w; ++j)
+ pix[ptr++] = c;
+ }
+ for (int k = fullWidth; k < pix.length; k += fullWidth) {
+ System.arraycopy(pix, 0, pix, k, fullWidth);
+ }
+ Image img = canvas.createImage(new MemoryImageSource(fullWidth, height, pix, 0, fullWidth));
+
+ return img;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/BarcodeCodabar.java b/src/main/java/com/lowagie/text/pdf/BarcodeCodabar.java
new file mode 100644
index 0000000..7b9537c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/BarcodeCodabar.java
@@ -0,0 +1,345 @@
+/*
+ * $Id: BarcodeCodabar.java,v 1.14 2006/02/09 18:07:56 psoares33 Exp $
+ *
+ * Copyright 2002-2006 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Element;
+import com.lowagie.text.Rectangle;
+import java.awt.Color;
+import java.awt.Image;
+import java.awt.Canvas;
+import java.awt.image.MemoryImageSource;
+
+/** Implements the code codabar. The default parameters are:
+ *
+ *x = 0.8f;
+ *n = 2;
+ *font = BaseFont.createFont("Helvetica", "winansi", false);
+ *size = 8;
+ *baseline = size;
+ *barHeight = size * 3;
+ *textAlignment = Element.ALIGN_CENTER;
+ *generateChecksum = false;
+ *checksumText = false;
+ *startStopText = false;
+ *
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class BarcodeCodabar extends Barcode{
+
+ /** The bars to generate the code.
+ */
+ static byte BARS[][] =
+ {
+ {0,0,0,0,0,1,1}, // 0
+ {0,0,0,0,1,1,0}, // 1
+ {0,0,0,1,0,0,1}, // 2
+ {1,1,0,0,0,0,0}, // 3
+ {0,0,1,0,0,1,0}, // 4
+ {1,0,0,0,0,1,0}, // 5
+ {0,1,0,0,0,0,1}, // 6
+ {0,1,0,0,1,0,0}, // 7
+ {0,1,1,0,0,0,0}, // 8
+ {1,0,0,1,0,0,0}, // 9
+ {0,0,0,1,1,0,0}, // -
+ {0,0,1,1,0,0,0}, // $
+ {1,0,0,0,1,0,1}, // :
+ {1,0,1,0,0,0,1}, // /
+ {1,0,1,0,1,0,0}, // .
+ {0,0,1,0,1,0,1}, // +
+ {0,0,1,1,0,1,0}, // a
+ {0,1,0,1,0,0,1}, // b
+ {0,0,0,1,0,1,1}, // c
+ {0,0,0,1,1,1,0} // d
+ };
+
+ /** The index chars to BARS
.
+ */
+ static String CHARS = "0123456789-$:/.+ABCD";
+
+ static final int START_STOP_IDX = 16;
+ /** Creates a new BarcodeCodabar.
+ */
+ public BarcodeCodabar() {
+ try {
+ x = 0.8f;
+ n = 2;
+ font = BaseFont.createFont("Helvetica", "winansi", false);
+ size = 8;
+ baseline = size;
+ barHeight = size * 3;
+ textAlignment = Element.ALIGN_CENTER;
+ generateChecksum = false;
+ checksumText = false;
+ startStopText = false;
+ codeType = CODABAR;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /** Creates the bars.
+ * @param text the text to create the bars
+ * @return the bars
+ */
+ public static byte[] getBarsCodabar(String text) {
+ text = text.toUpperCase();
+ int len = text.length();
+ if (len < 2)
+ throw new IllegalArgumentException("Codabar must have at least a start and stop character.");
+ if (CHARS.indexOf(text.charAt(0)) < START_STOP_IDX || CHARS.indexOf(text.charAt(len - 1)) < START_STOP_IDX)
+ throw new IllegalArgumentException("Codabar must have one of 'ABCD' as start/stop character.");
+ byte bars[] = new byte[text.length() * 8 - 1];
+ for (int k = 0; k < len; ++k) {
+ int idx = CHARS.indexOf(text.charAt(k));
+ if (idx >= START_STOP_IDX && k > 0 && k < len - 1)
+ throw new IllegalArgumentException("In codabar, start/stop characters are only allowed at the extremes.");
+ if (idx < 0)
+ throw new IllegalArgumentException("The character '" + text.charAt(k) + "' is illegal in codabar.");
+ System.arraycopy(BARS[idx], 0, bars, k * 8, 7);
+ }
+ return bars;
+ }
+
+ public static String calculateChecksum(String code) {
+ if (code.length() < 2)
+ return code;
+ String text = code.toUpperCase();
+ int sum = 0;
+ int len = text.length();
+ for (int k = 0; k < len; ++k)
+ sum += CHARS.indexOf(text.charAt(k));
+ sum = (sum + 15) / 16 * 16 - sum;
+ return code.substring(0, len - 1) + CHARS.charAt(sum) + code.substring(len - 1);
+ }
+
+ /** Gets the maximum area that the barcode and the text, if
+ * any, will occupy. The lower left corner is always (0, 0).
+ * @return the size the barcode occupies.
+ */
+ public Rectangle getBarcodeSize() {
+ float fontX = 0;
+ float fontY = 0;
+ String text = code;
+ if (generateChecksum && checksumText)
+ text = calculateChecksum(code);
+ if (!startStopText)
+ text = text.substring(1, text.length() - 1);
+ if (font != null) {
+ if (baseline > 0)
+ fontY = baseline - font.getFontDescriptor(BaseFont.DESCENT, size);
+ else
+ fontY = -baseline + size;
+ fontX = font.getWidthPoint(altText != null ? altText : text, size);
+ }
+ text = code;
+ if (generateChecksum)
+ text = calculateChecksum(code);
+ byte bars[] = getBarsCodabar(text);
+ int wide = 0;
+ for (int k = 0; k < bars.length; ++k) {
+ wide += (int)bars[k];
+ }
+ int narrow = bars.length - wide;
+ float fullWidth = x * (narrow + wide * n);
+ fullWidth = Math.max(fullWidth, fontX);
+ float fullHeight = barHeight + fontY;
+ return new Rectangle(fullWidth, fullHeight);
+ }
+
+ /** Places the barcode in a PdfContentByte
. The
+ * barcode is always placed at coodinates (0, 0). Use the
+ * translation matrix to move it elsewhere.
+ *
+ * @param cb the
+ *
+ *
+ * barColor
+ * textColor
+ *
+ *
+ *
+ * null
+ * null
+ *
+ *
+ *
+ * barColor
+ * null
+ * barColor
+ *
+ *
+ * null
+ * textColor
+ *
text painted with textColor
+ *
+ *
+ * barColor
+ * textColor
+ * barColor
text painted with textColor
PdfContentByte
where the barcode will be placed
+ * @param barColor the color of the bars. It can be null
+ * @param textColor the color of the text. It can be null
+ * @return the dimensions the barcode occupies
+ */
+ public Rectangle placeBarcode(PdfContentByte cb, Color barColor, Color textColor) {
+ String fullCode = code;
+ if (generateChecksum && checksumText)
+ fullCode = calculateChecksum(code);
+ if (!startStopText)
+ fullCode = fullCode.substring(1, fullCode.length() - 1);
+ float fontX = 0;
+ if (font != null) {
+ fontX = font.getWidthPoint(fullCode = altText != null ? altText : fullCode, size);
+ }
+ byte bars[] = getBarsCodabar(generateChecksum ? calculateChecksum(code) : code);
+ int wide = 0;
+ for (int k = 0; k < bars.length; ++k) {
+ wide += (int)bars[k];
+ }
+ int narrow = bars.length - wide;
+ float fullWidth = x * (narrow + wide * n);
+ float barStartX = 0;
+ float textStartX = 0;
+ switch (textAlignment) {
+ case Element.ALIGN_LEFT:
+ break;
+ case Element.ALIGN_RIGHT:
+ if (fontX > fullWidth)
+ barStartX = fontX - fullWidth;
+ else
+ textStartX = fullWidth - fontX;
+ break;
+ default:
+ if (fontX > fullWidth)
+ barStartX = (fontX - fullWidth) / 2;
+ else
+ textStartX = (fullWidth - fontX) / 2;
+ break;
+ }
+ float barStartY = 0;
+ float textStartY = 0;
+ if (font != null) {
+ if (baseline <= 0)
+ textStartY = barHeight - baseline;
+ else {
+ textStartY = -font.getFontDescriptor(BaseFont.DESCENT, size);
+ barStartY = textStartY + baseline;
+ }
+ }
+ boolean print = true;
+ if (barColor != null)
+ cb.setColorFill(barColor);
+ for (int k = 0; k < bars.length; ++k) {
+ float w = (bars[k] == 0 ? x : x * n);
+ if (print)
+ cb.rectangle(barStartX, barStartY, w - inkSpreading, barHeight);
+ print = !print;
+ barStartX += w;
+ }
+ cb.fill();
+ if (font != null) {
+ if (textColor != null)
+ cb.setColorFill(textColor);
+ cb.beginText();
+ cb.setFontAndSize(font, size);
+ cb.setTextMatrix(textStartX, textStartY);
+ cb.showText(fullCode);
+ cb.endText();
+ }
+ return getBarcodeSize();
+ }
+
+ /** Creates a java.awt.Image
. This image only
+ * contains the bars without any text.
+ * @param foreground the color of the bars
+ * @param background the color of the background
+ * @return the image
+ */
+ public java.awt.Image createAwtImage(Color foreground, Color background) {
+ int f = foreground.getRGB();
+ int g = background.getRGB();
+ Canvas canvas = new Canvas();
+
+ String fullCode = code;
+ if (generateChecksum && checksumText)
+ fullCode = calculateChecksum(code);
+ if (!startStopText)
+ fullCode = fullCode.substring(1, fullCode.length() - 1);
+ byte bars[] = getBarsCodabar(generateChecksum ? calculateChecksum(code) : code);
+ int wide = 0;
+ for (int k = 0; k < bars.length; ++k) {
+ wide += (int)bars[k];
+ }
+ int narrow = bars.length - wide;
+ int fullWidth = narrow + wide * (int)n;
+ boolean print = true;
+ int ptr = 0;
+ int height = (int)barHeight;
+ int pix[] = new int[fullWidth * height];
+ for (int k = 0; k < bars.length; ++k) {
+ int w = (bars[k] == 0 ? 1 : (int)n);
+ int c = g;
+ if (print)
+ c = f;
+ print = !print;
+ for (int j = 0; j < w; ++j)
+ pix[ptr++] = c;
+ }
+ for (int k = fullWidth; k < pix.length; k += fullWidth) {
+ System.arraycopy(pix, 0, pix, k, fullWidth);
+ }
+ Image img = canvas.createImage(new MemoryImageSource(fullWidth, height, pix, 0, fullWidth));
+
+ return img;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/BarcodeEAN.java b/src/main/java/com/lowagie/text/pdf/BarcodeEAN.java
new file mode 100644
index 0000000..c6d5f28
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/BarcodeEAN.java
@@ -0,0 +1,718 @@
+/*
+ * Copyright 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.ExceptionConverter;
+import java.util.Arrays;
+import java.awt.Color;
+import java.awt.Image;
+import java.awt.Canvas;
+import java.awt.image.MemoryImageSource;
+
+/** Generates barcodes in several formats: EAN13, EAN8, UPCA, UPCE,
+ * supplemental 2 and 5. The default parameters are:
+ *
+ *x = 0.8f;
+ *font = BaseFont.createFont("Helvetica", "winansi", false);
+ *size = 8;
+ *baseline = size;
+ *barHeight = size * 3;
+ *guardBars = true;
+ *codeType = EAN13;
+ *code = "";
+ *
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class BarcodeEAN extends Barcode{
+
+ /** The bar positions that are guard bars.*/
+ static int GUARD_EMPTY[] = {};
+ /** The bar positions that are guard bars.*/
+ static int GUARD_UPCA[] = {0, 2, 4, 6, 28, 30, 52, 54, 56, 58};
+ /** The bar positions that are guard bars.*/
+ static int GUARD_EAN13[] = {0, 2, 28, 30, 56, 58};
+ /** The bar positions that are guard bars.*/
+ static int GUARD_EAN8[] = {0, 2, 20, 22, 40, 42};
+ /** The bar positions that are guard bars.*/
+ static int GUARD_UPCE[] = {0, 2, 28, 30, 32};
+ /** The x coordinates to place the text.*/
+ static float TEXTPOS_EAN13[] = {6.5f, 13.5f, 20.5f, 27.5f, 34.5f, 41.5f, 53.5f, 60.5f, 67.5f, 74.5f, 81.5f, 88.5f};
+ /** The x coordinates to place the text.*/
+ static float TEXTPOS_EAN8[] = {6.5f, 13.5f, 20.5f, 27.5f, 39.5f, 46.5f, 53.5f, 60.5f};
+ /** The basic bar widths.*/
+ static byte BARS[][] =
+ {
+ {3, 2, 1, 1}, // 0
+ {2, 2, 2, 1}, // 1
+ {2, 1, 2, 2}, // 2
+ {1, 4, 1, 1}, // 3
+ {1, 1, 3, 2}, // 4
+ {1, 2, 3, 1}, // 5
+ {1, 1, 1, 4}, // 6
+ {1, 3, 1, 2}, // 7
+ {1, 2, 1, 3}, // 8
+ {3, 1, 1, 2} // 9
+ };
+
+ /** The total number of bars for EAN13.*/
+ static final int TOTALBARS_EAN13 = 11 + 12 * 4;
+ /** The total number of bars for EAN8.*/
+ static final int TOTALBARS_EAN8 = 11 + 8 * 4;
+ /** The total number of bars for UPCE.*/
+ static final int TOTALBARS_UPCE = 9 + 6 * 4;
+ /** The total number of bars for supplemental 2.*/
+ static final int TOTALBARS_SUPP2 = 13;
+ /** The total number of bars for supplemental 5.*/
+ static final int TOTALBARS_SUPP5 = 31;
+ /** Marker for odd parity.*/
+ static final int ODD = 0;
+ /** Marker for even parity.*/
+ static final int EVEN = 1;
+
+ /** Sequence of parities to be used with EAN13.*/
+ static byte PARITY13[][] =
+ {
+ {ODD, ODD, ODD, ODD, ODD, ODD}, // 0
+ {ODD, ODD, EVEN, ODD, EVEN, EVEN}, // 1
+ {ODD, ODD, EVEN, EVEN, ODD, EVEN}, // 2
+ {ODD, ODD, EVEN, EVEN, EVEN, ODD}, // 3
+ {ODD, EVEN, ODD, ODD, EVEN, EVEN}, // 4
+ {ODD, EVEN, EVEN, ODD, ODD, EVEN}, // 5
+ {ODD, EVEN, EVEN, EVEN, ODD, ODD}, // 6
+ {ODD, EVEN, ODD, EVEN, ODD, EVEN}, // 7
+ {ODD, EVEN, ODD, EVEN, EVEN, ODD}, // 8
+ {ODD, EVEN, EVEN, ODD, EVEN, ODD} // 9
+ };
+
+ /** Sequence of parities to be used with supplemental 2.*/
+ static byte PARITY2[][] =
+ {
+ {ODD, ODD}, // 0
+ {ODD, EVEN}, // 1
+ {EVEN, ODD}, // 2
+ {EVEN, EVEN} // 3
+ };
+
+ /** Sequence of parities to be used with supplemental 2.*/
+ static byte PARITY5[][] =
+ {
+ {EVEN, EVEN, ODD, ODD, ODD}, // 0
+ {EVEN, ODD, EVEN, ODD, ODD}, // 1
+ {EVEN, ODD, ODD, EVEN, ODD}, // 2
+ {EVEN, ODD, ODD, ODD, EVEN}, // 3
+ {ODD, EVEN, EVEN, ODD, ODD}, // 4
+ {ODD, ODD, EVEN, EVEN, ODD}, // 5
+ {ODD, ODD, ODD, EVEN, EVEN}, // 6
+ {ODD, EVEN, ODD, EVEN, ODD}, // 7
+ {ODD, EVEN, ODD, ODD, EVEN}, // 8
+ {ODD, ODD, EVEN, ODD, EVEN} // 9
+ };
+
+ /** Sequence of parities to be used with UPCE.*/
+ static byte PARITYE[][] =
+ {
+ {EVEN, EVEN, EVEN, ODD, ODD, ODD}, // 0
+ {EVEN, EVEN, ODD, EVEN, ODD, ODD}, // 1
+ {EVEN, EVEN, ODD, ODD, EVEN, ODD}, // 2
+ {EVEN, EVEN, ODD, ODD, ODD, EVEN}, // 3
+ {EVEN, ODD, EVEN, EVEN, ODD, ODD}, // 4
+ {EVEN, ODD, ODD, EVEN, EVEN, ODD}, // 5
+ {EVEN, ODD, ODD, ODD, EVEN, EVEN}, // 6
+ {EVEN, ODD, EVEN, ODD, EVEN, ODD}, // 7
+ {EVEN, ODD, EVEN, ODD, ODD, EVEN}, // 8
+ {EVEN, ODD, ODD, EVEN, ODD, EVEN} // 9
+ };
+
+ /** Creates new BarcodeEAN */
+ public BarcodeEAN() {
+ try {
+ x = 0.8f;
+ font = BaseFont.createFont("Helvetica", "winansi", false);
+ size = 8;
+ baseline = size;
+ barHeight = size * 3;
+ guardBars = true;
+ codeType = EAN13;
+ code = "";
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /** Calculates the EAN parity character.
+ * @param code the code
+ * @return the parity character
+ */
+ public static int calculateEANParity(String code) {
+ int mul = 3;
+ int total = 0;
+ for (int k = code.length() - 1; k >= 0; --k) {
+ int n = code.charAt(k) - '0';
+ total += mul * n;
+ mul ^= 2;
+ }
+ return (10 - (total % 10)) % 10;
+ }
+
+ /** Converts an UPCA code into an UPCE code. If the code can not
+ * be converted a null
is returned.
+ * @param text the code to convert. It must have 12 numeric characters
+ * @return the 8 converted digits or null
if the
+ * code could not be converted
+ */
+ static public String convertUPCAtoUPCE(String text) {
+ if (text.length() != 12 || !(text.startsWith("0") || text.startsWith("1")))
+ return null;
+ if (text.substring(3, 6).equals("000") || text.substring(3, 6).equals("100")
+ || text.substring(3, 6).equals("200")) {
+ if (text.substring(6, 8).equals("00"))
+ return text.substring(0, 1) + text.substring(1, 3) + text.substring(8, 11) + text.substring(3, 4) + text.substring(11);
+ }
+ else if (text.substring(4, 6).equals("00")) {
+ if (text.substring(6, 9).equals("000"))
+ return text.substring(0, 1) + text.substring(1, 4) + text.substring(9, 11) + "3" + text.substring(11);
+ }
+ else if (text.substring(5, 6).equals("0")) {
+ if (text.substring(6, 10).equals("0000"))
+ return text.substring(0, 1) + text.substring(1, 5) + text.substring(10, 11) + "4" + text.substring(11);
+ }
+ else if (text.charAt(10) >= '5') {
+ if (text.substring(6, 10).equals("0000"))
+ return text.substring(0, 1) + text.substring(1, 6) + text.substring(10, 11) + text.substring(11);
+ }
+ return null;
+ }
+
+ /** Creates the bars for the barcode EAN13 and UPCA.
+ * @param _code the text with 13 digits
+ * @return the barcode
+ */
+ public static byte[] getBarsEAN13(String _code) {
+ int code[] = new int[_code.length()];
+ for (int k = 0; k < code.length; ++k)
+ code[k] = _code.charAt(k) - '0';
+ byte bars[] = new byte[TOTALBARS_EAN13];
+ int pb = 0;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ byte sequence[] = PARITY13[code[0]];
+ for (int k = 0; k < sequence.length; ++k) {
+ int c = code[k + 1];
+ byte stripes[] = BARS[c];
+ if (sequence[k] == ODD) {
+ bars[pb++] = stripes[0];
+ bars[pb++] = stripes[1];
+ bars[pb++] = stripes[2];
+ bars[pb++] = stripes[3];
+ }
+ else {
+ bars[pb++] = stripes[3];
+ bars[pb++] = stripes[2];
+ bars[pb++] = stripes[1];
+ bars[pb++] = stripes[0];
+ }
+ }
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ for (int k = 7; k < 13; ++k) {
+ int c = code[k];
+ byte stripes[] = BARS[c];
+ bars[pb++] = stripes[0];
+ bars[pb++] = stripes[1];
+ bars[pb++] = stripes[2];
+ bars[pb++] = stripes[3];
+ }
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ return bars;
+ }
+
+ /** Creates the bars for the barcode EAN8.
+ * @param _code the text with 8 digits
+ * @return the barcode
+ */
+ public static byte[] getBarsEAN8(String _code) {
+ int code[] = new int[_code.length()];
+ for (int k = 0; k < code.length; ++k)
+ code[k] = _code.charAt(k) - '0';
+ byte bars[] = new byte[TOTALBARS_EAN8];
+ int pb = 0;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ for (int k = 0; k < 4; ++k) {
+ int c = code[k];
+ byte stripes[] = BARS[c];
+ bars[pb++] = stripes[0];
+ bars[pb++] = stripes[1];
+ bars[pb++] = stripes[2];
+ bars[pb++] = stripes[3];
+ }
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ for (int k = 4; k < 8; ++k) {
+ int c = code[k];
+ byte stripes[] = BARS[c];
+ bars[pb++] = stripes[0];
+ bars[pb++] = stripes[1];
+ bars[pb++] = stripes[2];
+ bars[pb++] = stripes[3];
+ }
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ return bars;
+ }
+
+ /** Creates the bars for the barcode UPCE.
+ * @param _code the text with 8 digits
+ * @return the barcode
+ */
+ public static byte[] getBarsUPCE(String _code) {
+ int code[] = new int[_code.length()];
+ for (int k = 0; k < code.length; ++k)
+ code[k] = _code.charAt(k) - '0';
+ byte bars[] = new byte[TOTALBARS_UPCE];
+ boolean flip = (code[0] != 0);
+ int pb = 0;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ byte sequence[] = PARITYE[code[code.length - 1]];
+ for (int k = 1; k < code.length - 1; ++k) {
+ int c = code[k];
+ byte stripes[] = BARS[c];
+ if (sequence[k - 1] == (flip ? EVEN : ODD)) {
+ bars[pb++] = stripes[0];
+ bars[pb++] = stripes[1];
+ bars[pb++] = stripes[2];
+ bars[pb++] = stripes[3];
+ }
+ else {
+ bars[pb++] = stripes[3];
+ bars[pb++] = stripes[2];
+ bars[pb++] = stripes[1];
+ bars[pb++] = stripes[0];
+ }
+ }
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ return bars;
+ }
+
+ /** Creates the bars for the barcode supplemental 2.
+ * @param _code the text with 2 digits
+ * @return the barcode
+ */
+ public static byte[] getBarsSupplemental2(String _code) {
+ int code[] = new int[2];
+ for (int k = 0; k < code.length; ++k)
+ code[k] = _code.charAt(k) - '0';
+ byte bars[] = new byte[TOTALBARS_SUPP2];
+ int pb = 0;
+ int parity = (code[0] * 10 + code[1]) % 4;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 2;
+ byte sequence[] = PARITY2[parity];
+ for (int k = 0; k < sequence.length; ++k) {
+ if (k == 1) {
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ }
+ int c = code[k];
+ byte stripes[] = BARS[c];
+ if (sequence[k] == ODD) {
+ bars[pb++] = stripes[0];
+ bars[pb++] = stripes[1];
+ bars[pb++] = stripes[2];
+ bars[pb++] = stripes[3];
+ }
+ else {
+ bars[pb++] = stripes[3];
+ bars[pb++] = stripes[2];
+ bars[pb++] = stripes[1];
+ bars[pb++] = stripes[0];
+ }
+ }
+ return bars;
+ }
+
+ /** Creates the bars for the barcode supplemental 5.
+ * @param _code the text with 5 digits
+ * @return the barcode
+ */
+ public static byte[] getBarsSupplemental5(String _code) {
+ int code[] = new int[5];
+ for (int k = 0; k < code.length; ++k)
+ code[k] = _code.charAt(k) - '0';
+ byte bars[] = new byte[TOTALBARS_SUPP5];
+ int pb = 0;
+ int parity = (((code[0] + code[2] + code[4]) * 3) + ((code[1] + code[3]) * 9)) % 10;
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ bars[pb++] = 2;
+ byte sequence[] = PARITY5[parity];
+ for (int k = 0; k < sequence.length; ++k) {
+ if (k != 0) {
+ bars[pb++] = 1;
+ bars[pb++] = 1;
+ }
+ int c = code[k];
+ byte stripes[] = BARS[c];
+ if (sequence[k] == ODD) {
+ bars[pb++] = stripes[0];
+ bars[pb++] = stripes[1];
+ bars[pb++] = stripes[2];
+ bars[pb++] = stripes[3];
+ }
+ else {
+ bars[pb++] = stripes[3];
+ bars[pb++] = stripes[2];
+ bars[pb++] = stripes[1];
+ bars[pb++] = stripes[0];
+ }
+ }
+ return bars;
+ }
+
+ /** Gets the maximum area that the barcode and the text, if
+ * any, will occupy. The lower left corner is always (0, 0).
+ * @return the size the barcode occupies.
+ */
+ public Rectangle getBarcodeSize() {
+ float width = 0;
+ float height = barHeight;
+ if (font != null) {
+ if (baseline <= 0)
+ height += -baseline + size;
+ else
+ height += baseline - font.getFontDescriptor(BaseFont.DESCENT, size);
+ }
+ switch (codeType) {
+ case EAN13:
+ width = x * (11 + 12 * 7);
+ if (font != null) {
+ width += font.getWidthPoint(code.charAt(0), size);
+ }
+ break;
+ case EAN8:
+ width = x * (11 + 8 * 7);
+ break;
+ case UPCA:
+ width = x * (11 + 12 * 7);
+ if (font != null) {
+ width += font.getWidthPoint(code.charAt(0), size) + font.getWidthPoint(code.charAt(11), size);
+ }
+ break;
+ case UPCE:
+ width = x * (9 + 6 * 7);
+ if (font != null) {
+ width += font.getWidthPoint(code.charAt(0), size) + font.getWidthPoint(code.charAt(7), size);
+ }
+ break;
+ case SUPP2:
+ width = x * (6 + 2 * 7);
+ break;
+ case SUPP5:
+ width = x * (4 + 5 * 7 + 4 * 2);
+ break;
+ default:
+ throw new RuntimeException("Invalid code type.");
+ }
+ return new Rectangle(width, height);
+ }
+
+ /** Places the barcode in a PdfContentByte
. The
+ * barcode is always placed at coodinates (0, 0). Use the
+ * translation matrix to move it elsewhere.
+ *
+ * @param cb the
+ *
+ *
+ * barColor
+ * textColor
+ *
+ *
+ *
+ * null
+ * null
+ *
+ *
+ *
+ * barColor
+ * null
+ * barColor
+ *
+ *
+ * null
+ * textColor
+ *
text painted with textColor
+ *
+ *
+ * barColor
+ * textColor
+ * barColor
text painted with textColor
PdfContentByte
where the barcode will be placed
+ * @param barColor the color of the bars. It can be null
+ * @param textColor the color of the text. It can be null
+ * @return the dimensions the barcode occupies
+ */
+ public Rectangle placeBarcode(PdfContentByte cb, Color barColor, Color textColor) {
+ Rectangle rect = getBarcodeSize();
+ float barStartX = 0;
+ float barStartY = 0;
+ float textStartY = 0;
+ if (font != null) {
+ if (baseline <= 0)
+ textStartY = barHeight - baseline;
+ else {
+ textStartY = -font.getFontDescriptor(BaseFont.DESCENT, size);
+ barStartY = textStartY + baseline;
+ }
+ }
+ switch (codeType) {
+ case EAN13:
+ case UPCA:
+ case UPCE:
+ if (font != null)
+ barStartX += font.getWidthPoint(code.charAt(0), size);
+ break;
+ }
+ byte bars[] = null;
+ int guard[] = GUARD_EMPTY;
+ switch (codeType) {
+ case EAN13:
+ bars = getBarsEAN13(code);
+ guard = GUARD_EAN13;
+ break;
+ case EAN8:
+ bars = getBarsEAN8(code);
+ guard = GUARD_EAN8;
+ break;
+ case UPCA:
+ bars = getBarsEAN13("0" + code);
+ guard = GUARD_UPCA;
+ break;
+ case UPCE:
+ bars = getBarsUPCE(code);
+ guard = GUARD_UPCE;
+ break;
+ case SUPP2:
+ bars = getBarsSupplemental2(code);
+ break;
+ case SUPP5:
+ bars = getBarsSupplemental5(code);
+ break;
+ }
+ float keepBarX = barStartX;
+ boolean print = true;
+ float gd = 0;
+ if (font != null && baseline > 0 && guardBars) {
+ gd = baseline / 2;
+ }
+ if (barColor != null)
+ cb.setColorFill(barColor);
+ for (int k = 0; k < bars.length; ++k) {
+ float w = bars[k] * x;
+ if (print) {
+ if (Arrays.binarySearch(guard, k) >= 0)
+ cb.rectangle(barStartX, barStartY - gd, w - inkSpreading, barHeight + gd);
+ else
+ cb.rectangle(barStartX, barStartY, w - inkSpreading, barHeight);
+ }
+ print = !print;
+ barStartX += w;
+ }
+ cb.fill();
+ if (font != null) {
+ if (textColor != null)
+ cb.setColorFill(textColor);
+ cb.beginText();
+ cb.setFontAndSize(font, size);
+ switch (codeType) {
+ case EAN13:
+ cb.setTextMatrix(0, textStartY);
+ cb.showText(code.substring(0, 1));
+ for (int k = 1; k < 13; ++k) {
+ String c = code.substring(k, k + 1);
+ float len = font.getWidthPoint(c, size);
+ float pX = keepBarX + TEXTPOS_EAN13[k - 1] * x - len / 2;
+ cb.setTextMatrix(pX, textStartY);
+ cb.showText(c);
+ }
+ break;
+ case EAN8:
+ for (int k = 0; k < 8; ++k) {
+ String c = code.substring(k, k + 1);
+ float len = font.getWidthPoint(c, size);
+ float pX = TEXTPOS_EAN8[k] * x - len / 2;
+ cb.setTextMatrix(pX, textStartY);
+ cb.showText(c);
+ }
+ break;
+ case UPCA:
+ cb.setTextMatrix(0, textStartY);
+ cb.showText(code.substring(0, 1));
+ for (int k = 1; k < 11; ++k) {
+ String c = code.substring(k, k + 1);
+ float len = font.getWidthPoint(c, size);
+ float pX = keepBarX + TEXTPOS_EAN13[k] * x - len / 2;
+ cb.setTextMatrix(pX, textStartY);
+ cb.showText(c);
+ }
+ cb.setTextMatrix(keepBarX + x * (11 + 12 * 7), textStartY);
+ cb.showText(code.substring(11, 12));
+ break;
+ case UPCE:
+ cb.setTextMatrix(0, textStartY);
+ cb.showText(code.substring(0, 1));
+ for (int k = 1; k < 7; ++k) {
+ String c = code.substring(k, k + 1);
+ float len = font.getWidthPoint(c, size);
+ float pX = keepBarX + TEXTPOS_EAN13[k - 1] * x - len / 2;
+ cb.setTextMatrix(pX, textStartY);
+ cb.showText(c);
+ }
+ cb.setTextMatrix(keepBarX + x * (9 + 6 * 7), textStartY);
+ cb.showText(code.substring(7, 8));
+ break;
+ case SUPP2:
+ case SUPP5:
+ for (int k = 0; k < code.length(); ++k) {
+ String c = code.substring(k, k + 1);
+ float len = font.getWidthPoint(c, size);
+ float pX = (7.5f + (9 * k)) * x - len / 2;
+ cb.setTextMatrix(pX, textStartY);
+ cb.showText(c);
+ }
+ break;
+ }
+ cb.endText();
+ }
+ return rect;
+ }
+
+ /** Creates a java.awt.Image
. This image only
+ * contains the bars without any text.
+ * @param foreground the color of the bars
+ * @param background the color of the background
+ * @return the image
+ */
+ public java.awt.Image createAwtImage(Color foreground, Color background) {
+ int f = foreground.getRGB();
+ int g = background.getRGB();
+ Canvas canvas = new Canvas();
+
+ int width = 0;
+ byte bars[] = null;
+ switch (codeType) {
+ case EAN13:
+ bars = getBarsEAN13(code);
+ width = 11 + 12 * 7;
+ break;
+ case EAN8:
+ bars = getBarsEAN8(code);
+ width = 11 + 8 * 7;
+ break;
+ case UPCA:
+ bars = getBarsEAN13("0" + code);
+ width = 11 + 12 * 7;
+ break;
+ case UPCE:
+ bars = getBarsUPCE(code);
+ width = 9 + 6 * 7;
+ break;
+ case SUPP2:
+ bars = getBarsSupplemental2(code);
+ width = 6 + 2 * 7;
+ break;
+ case SUPP5:
+ bars = getBarsSupplemental5(code);
+ width = 4 + 5 * 7 + 4 * 2;
+ break;
+ default:
+ throw new RuntimeException("Invalid code type.");
+ }
+
+ boolean print = true;
+ int ptr = 0;
+ int height = (int)barHeight;
+ int pix[] = new int[width * height];
+ for (int k = 0; k < bars.length; ++k) {
+ int w = bars[k];
+ int c = g;
+ if (print)
+ c = f;
+ print = !print;
+ for (int j = 0; j < w; ++j)
+ pix[ptr++] = c;
+ }
+ for (int k = width; k < pix.length; k += width) {
+ System.arraycopy(pix, 0, pix, k, width);
+ }
+ Image img = canvas.createImage(new MemoryImageSource(width, height, pix, 0, width));
+
+ return img;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/BarcodeEANSUPP.java b/src/main/java/com/lowagie/text/pdf/BarcodeEANSUPP.java
new file mode 100644
index 0000000..7e0d794
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/BarcodeEANSUPP.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+import com.lowagie.text.Rectangle;
+import java.awt.Color;
+
+/** This class takes 2 barcodes, an EAN/UPC and a supplemental
+ * and creates a single barcode with both combined in the
+ * expected layout. The UPC/EAN should have a positive text
+ * baseline and the supplemental a negative one (in the supplemental
+ * the text is on the top of the barcode.
+ *n = 8; // horizontal distance between the two barcodes
+ *
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class BarcodeEANSUPP extends Barcode{
+
+ /** The barcode with the EAN/UPC.
+ */
+ protected Barcode ean;
+ /** The barcode with the supplemental.
+ */
+ protected Barcode supp;
+
+ /** Creates new combined barcode.
+ * @param ean the EAN/UPC barcode
+ * @param supp the supplemental barcode
+ */
+ public BarcodeEANSUPP(Barcode ean, Barcode supp) {
+ n = 8; // horizontal distance between the two barcodes
+ this.ean = ean;
+ this.supp = supp;
+ }
+
+ /** Gets the maximum area that the barcode and the text, if
+ * any, will occupy. The lower left corner is always (0, 0).
+ * @return the size the barcode occupies.
+ */
+ public Rectangle getBarcodeSize() {
+ Rectangle rect = ean.getBarcodeSize();
+ rect.setRight(rect.width() + supp.getBarcodeSize().width() + n);
+ return rect;
+ }
+
+ /** Places the barcode in a PdfContentByte
. The
+ * barcode is always placed at coodinates (0, 0). Use the
+ * translation matrix to move it elsewhere.
+ *
+ * @param cb the
+ *
+ *
+ * barColor
+ * textColor
+ *
+ *
+ *
+ * null
+ * null
+ *
+ *
+ *
+ * barColor
+ * null
+ * barColor
+ *
+ *
+ * null
+ * textColor
+ *
text painted with textColor
+ *
+ *
+ * barColor
+ * textColor
+ * barColor
text painted with textColor
PdfContentByte
where the barcode will be placed
+ * @param barColor the color of the bars. It can be null
+ * @param textColor the color of the text. It can be null
+ * @return the dimensions the barcode occupies
+ */
+ public Rectangle placeBarcode(PdfContentByte cb, Color barColor, Color textColor) {
+ if (supp.getFont() != null)
+ supp.setBarHeight(ean.getBarHeight() + supp.getBaseline() - supp.getFont().getFontDescriptor(BaseFont.CAPHEIGHT, supp.getSize()));
+ else
+ supp.setBarHeight(ean.getBarHeight());
+ Rectangle eanR = ean.getBarcodeSize();
+ cb.saveState();
+ ean.placeBarcode(cb, barColor, textColor);
+ cb.restoreState();
+ cb.saveState();
+ cb.concatCTM(1, 0, 0, 1, eanR.width() + n, eanR.height() - ean.getBarHeight());
+ supp.placeBarcode(cb, barColor, textColor);
+ cb.restoreState();
+ return getBarcodeSize();
+ }
+
+ /** Creates a java.awt.Image
. This image only
+ * contains the bars without any text.
+ * @param foreground the color of the bars
+ * @param background the color of the background
+ * @return the image
+ */
+ public java.awt.Image createAwtImage(Color foreground, Color background) {
+ throw new UnsupportedOperationException("The two barcodes must be composed externally.");
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/BarcodeInter25.java b/src/main/java/com/lowagie/text/pdf/BarcodeInter25.java
new file mode 100644
index 0000000..f8700d6
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/BarcodeInter25.java
@@ -0,0 +1,338 @@
+/*
+ * $Id: BarcodeInter25.java,v 1.17 2006/02/09 18:07:56 psoares33 Exp $
+ *
+ * Copyright 2002-2006 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Element;
+import com.lowagie.text.Rectangle;
+import java.awt.Color;
+import java.awt.Image;
+import java.awt.Canvas;
+import java.awt.image.MemoryImageSource;
+
+/** Implements the code interleaved 2 of 5. The text can include
+ * non numeric characters that are printed but do not generate bars.
+ * The default parameters are:
+ *
+ *x = 0.8f;
+ *n = 2;
+ *font = BaseFont.createFont("Helvetica", "winansi", false);
+ *size = 8;
+ *baseline = size;
+ *barHeight = size * 3;
+ *textAlignment = Element.ALIGN_CENTER;
+ *generateChecksum = false;
+ *checksumText = false;
+ *
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class BarcodeInter25 extends Barcode{
+
+ /** The bars to generate the code.
+ */
+ static byte BARS[][] =
+ {
+ {0,0,1,1,0},
+ {1,0,0,0,1},
+ {0,1,0,0,1},
+ {1,1,0,0,0},
+ {0,0,1,0,1},
+ {1,0,1,0,0},
+ {0,1,1,0,0},
+ {0,0,0,1,1},
+ {1,0,0,1,0},
+ {0,1,0,1,0}
+ };
+
+ /** Creates new BarcodeInter25 */
+ public BarcodeInter25() {
+ try {
+ x = 0.8f;
+ n = 2;
+ font = BaseFont.createFont("Helvetica", "winansi", false);
+ size = 8;
+ baseline = size;
+ barHeight = size * 3;
+ textAlignment = Element.ALIGN_CENTER;
+ generateChecksum = false;
+ checksumText = false;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /** Deletes all the non numeric characters from text
.
+ * @param text the text
+ * @return a String
with only numeric characters
+ */
+ public static String keepNumbers(String text) {
+ StringBuffer sb = new StringBuffer();
+ for (int k = 0; k < text.length(); ++k) {
+ char c = text.charAt(k);
+ if (c >= '0' && c <= '9')
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+
+ /** Calculates the checksum.
+ * @param text the numeric text
+ * @return the checksum
+ */
+ public static char getChecksum(String text) {
+ int mul = 3;
+ int total = 0;
+ for (int k = text.length() - 1; k >= 0; --k) {
+ int n = text.charAt(k) - '0';
+ total += mul * n;
+ mul ^= 2;
+ }
+ return (char)(((10 - (total % 10)) % 10) + '0');
+ }
+
+ /** Creates the bars for the barcode.
+ * @param text the text. It can contain non numeric characters
+ * @return the barcode
+ */
+ public static byte[] getBarsInter25(String text) {
+ text = keepNumbers(text);
+ if ((text.length() & 1) != 0)
+ throw new IllegalArgumentException("The text length must be even.");
+ byte bars[] = new byte[text.length() * 5 + 7];
+ int pb = 0;
+ bars[pb++] = 0;
+ bars[pb++] = 0;
+ bars[pb++] = 0;
+ bars[pb++] = 0;
+ int len = text.length() / 2;
+ for (int k = 0; k < len; ++k) {
+ int c1 = text.charAt(k * 2) - '0';
+ int c2 = text.charAt(k * 2 + 1) - '0';
+ byte b1[] = BARS[c1];
+ byte b2[] = BARS[c2];
+ for (int j = 0; j < 5; ++j) {
+ bars[pb++] = b1[j];
+ bars[pb++] = b2[j];
+ }
+ }
+ bars[pb++] = 1;
+ bars[pb++] = 0;
+ bars[pb++] = 0;
+ return bars;
+ }
+
+ /** Gets the maximum area that the barcode and the text, if
+ * any, will occupy. The lower left corner is always (0, 0).
+ * @return the size the barcode occupies.
+ */
+ public Rectangle getBarcodeSize() {
+ float fontX = 0;
+ float fontY = 0;
+ if (font != null) {
+ if (baseline > 0)
+ fontY = baseline - font.getFontDescriptor(BaseFont.DESCENT, size);
+ else
+ fontY = -baseline + size;
+ String fullCode = code;
+ if (generateChecksum && checksumText)
+ fullCode += getChecksum(fullCode);
+ fontX = font.getWidthPoint(altText != null ? altText : fullCode, size);
+ }
+ String fullCode = keepNumbers(code);
+ int len = fullCode.length();
+ if (generateChecksum)
+ ++len;
+ float fullWidth = len * (3 * x + 2 * x * n) + (6 + n ) * x;
+ fullWidth = Math.max(fullWidth, fontX);
+ float fullHeight = barHeight + fontY;
+ return new Rectangle(fullWidth, fullHeight);
+ }
+
+ /** Places the barcode in a PdfContentByte
. The
+ * barcode is always placed at coodinates (0, 0). Use the
+ * translation matrix to move it elsewhere.
+ *
+ * @param cb the
+ *
+ *
+ * barColor
+ * textColor
+ *
+ *
+ *
+ * null
+ * null
+ *
+ *
+ *
+ * barColor
+ * null
+ * barColor
+ *
+ *
+ * null
+ * textColor
+ *
text painted with textColor
+ *
+ *
+ * barColor
+ * textColor
+ * barColor
text painted with textColor
PdfContentByte
where the barcode will be placed
+ * @param barColor the color of the bars. It can be null
+ * @param textColor the color of the text. It can be null
+ * @return the dimensions the barcode occupies
+ */
+ public Rectangle placeBarcode(PdfContentByte cb, Color barColor, Color textColor) {
+ String fullCode = code;
+ float fontX = 0;
+ if (font != null) {
+ if (generateChecksum && checksumText)
+ fullCode += getChecksum(fullCode);
+ fontX = font.getWidthPoint(fullCode = altText != null ? altText : fullCode, size);
+ }
+ String bCode = keepNumbers(code);
+ if (generateChecksum)
+ bCode += getChecksum(bCode);
+ int len = bCode.length();
+ float fullWidth = len * (3 * x + 2 * x * n) + (6 + n ) * x;
+ float barStartX = 0;
+ float textStartX = 0;
+ switch (textAlignment) {
+ case Element.ALIGN_LEFT:
+ break;
+ case Element.ALIGN_RIGHT:
+ if (fontX > fullWidth)
+ barStartX = fontX - fullWidth;
+ else
+ textStartX = fullWidth - fontX;
+ break;
+ default:
+ if (fontX > fullWidth)
+ barStartX = (fontX - fullWidth) / 2;
+ else
+ textStartX = (fullWidth - fontX) / 2;
+ break;
+ }
+ float barStartY = 0;
+ float textStartY = 0;
+ if (font != null) {
+ if (baseline <= 0)
+ textStartY = barHeight - baseline;
+ else {
+ textStartY = -font.getFontDescriptor(BaseFont.DESCENT, size);
+ barStartY = textStartY + baseline;
+ }
+ }
+ byte bars[] = getBarsInter25(bCode);
+ boolean print = true;
+ if (barColor != null)
+ cb.setColorFill(barColor);
+ for (int k = 0; k < bars.length; ++k) {
+ float w = (bars[k] == 0 ? x : x * n);
+ if (print)
+ cb.rectangle(barStartX, barStartY, w - inkSpreading, barHeight);
+ print = !print;
+ barStartX += w;
+ }
+ cb.fill();
+ if (font != null) {
+ if (textColor != null)
+ cb.setColorFill(textColor);
+ cb.beginText();
+ cb.setFontAndSize(font, size);
+ cb.setTextMatrix(textStartX, textStartY);
+ cb.showText(fullCode);
+ cb.endText();
+ }
+ return getBarcodeSize();
+ }
+
+ /** Creates a java.awt.Image
. This image only
+ * contains the bars without any text.
+ * @param foreground the color of the bars
+ * @param background the color of the background
+ * @return the image
+ */
+ public java.awt.Image createAwtImage(Color foreground, Color background) {
+ int f = foreground.getRGB();
+ int g = background.getRGB();
+ Canvas canvas = new Canvas();
+
+ String bCode = keepNumbers(code);
+ if (generateChecksum)
+ bCode += getChecksum(bCode);
+ int len = bCode.length();
+ int nn = (int)n;
+ int fullWidth = len * (3 + 2 * nn) + (6 + nn );
+ byte bars[] = getBarsInter25(bCode);
+ boolean print = true;
+ int ptr = 0;
+ int height = (int)barHeight;
+ int pix[] = new int[fullWidth * height];
+ for (int k = 0; k < bars.length; ++k) {
+ int w = (bars[k] == 0 ? 1 : nn);
+ int c = g;
+ if (print)
+ c = f;
+ print = !print;
+ for (int j = 0; j < w; ++j)
+ pix[ptr++] = c;
+ }
+ for (int k = fullWidth; k < pix.length; k += fullWidth) {
+ System.arraycopy(pix, 0, pix, k, fullWidth);
+ }
+ Image img = canvas.createImage(new MemoryImageSource(fullWidth, height, pix, 0, fullWidth));
+
+ return img;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/BarcodePDF417.java b/src/main/java/com/lowagie/text/pdf/BarcodePDF417.java
new file mode 100644
index 0000000..c0d3c6e
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/BarcodePDF417.java
@@ -0,0 +1,1604 @@
+/*
+ *
+ * Copyright 2003 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.Image;
+import com.lowagie.text.BadElementException;
+import java.util.ArrayList;
+import java.io.UnsupportedEncodingException;
+import com.lowagie.text.pdf.codec.CCITTG4Encoder;
+import java.awt.Color;
+import java.awt.Canvas;
+import java.awt.image.MemoryImageSource;
+
+/** Generates the 2D barcode PDF417. Supports dimensioning auto-sizing, fixed
+ * and variable sizes, automatic and manual error levels, raw codeword input,
+ * codeword size optimization and bitmap inversion. The output can
+ * be a CCITT G4 Image
or a raw bitmap.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class BarcodePDF417 {
+
+ /** Auto-size is made based on aspectRatio
and yHeight
. */
+ public static final int PDF417_USE_ASPECT_RATIO = 0;
+ /** The size of the barcode will be at least codeColumns*codeRows
. */
+ public static final int PDF417_FIXED_RECTANGLE = 1;
+ /** The size will be at least codeColumns
+ * with a variable number of codeRows
.
+ */
+ public static final int PDF417_FIXED_COLUMNS = 2;
+ /** The size will be at least codeRows
+ * with a variable number of codeColumns
.
+ */
+ public static final int PDF417_FIXED_ROWS = 4;
+ /** The error level correction is set automatically according
+ * to ISO 15438 recomendations.
+ */
+ public static final int PDF417_AUTO_ERROR_LEVEL = 0;
+ /** The error level correction is set by the user. It can be 0 to 8. */
+ public static final int PDF417_USE_ERROR_LEVEL = 16;
+ /**
+ * One single binary segment is used
+ */
+ public static final int PDF417_FORCE_BINARY = 32;
+ /** No text
interpretation is done and the content of codewords
+ * is used directly.
+ */
+ public static final int PDF417_USE_RAW_CODEWORDS = 64;
+ /** Inverts the output bits of the raw bitmap that is normally
+ * bit one for black. It has only effect for the raw bitmap.
+ */
+ public static final int PDF417_INVERT_BITMAP = 128;
+ /** Use Macro PDF417 Encoding
+ * @see #setMacroFileId(String)
+ * @see #setMacroSegmentId(int)
+ * @see #setMacroSegmentCount(int)
+ */
+ public static final int PDF417_USE_MACRO = 256;
+
+
+ private int macroSegmentCount=0;
+ private int macroSegmentId=-1;
+ private String macroFileId;
+ protected int bitPtr;
+ protected int cwPtr;
+ protected SegmentList segmentList;
+
+ /** Creates a new BarcodePDF417
with the default settings. */
+ public BarcodePDF417() {
+ setDefaultParameters();
+ }
+
+ /**
+ * Sets the segment id for macro PDF417 encoding
+ * @param id the id (starting at 0)
+ * @see #setMacroSegmentCount(int)
+ */
+ public void setMacroSegmentId(int id) {
+ this.macroSegmentId = id;
+ }
+
+ /**
+ * Sets the segment count for macro PDF417 encoding
+ * @param cnt the number of macro segments
+ * @see #setMacroSegmentId(int)
+ */
+ public void setMacroSegmentCount(int cnt) {
+ this.macroSegmentCount = cnt;
+ }
+
+ /**
+ * Sets the File ID for macro PDF417 encoding
+ * @param id the file id
+ */
+ public void setMacroFileId(String id) {
+ this.macroFileId = id;
+ }
+
+ protected boolean checkSegmentType(Segment segment, char type) {
+ if (segment == null)
+ return false;
+ return segment.type == type;
+ }
+
+ protected int getSegmentLength(Segment segment) {
+ if (segment == null)
+ return 0;
+ return segment.end - segment.start;
+ }
+
+ /** Set the default settings that correspond to PDF417_USE_ASPECT_RATIO
+ * and PDF417_AUTO_ERROR_LEVEL
.
+ */
+ public void setDefaultParameters() {
+ options = 0;
+ outBits = null;
+ text = new byte[0];
+ yHeight = 3;
+ aspectRatio = 0.5f;
+ }
+
+ protected void outCodeword17(int codeword) {
+ int bytePtr = bitPtr / 8;
+ int bit = bitPtr - bytePtr * 8;
+ outBits[bytePtr++] |= codeword >> (9 + bit);
+ outBits[bytePtr++] |= codeword >> (1 + bit);
+ codeword <<= 8;
+ outBits[bytePtr] |= codeword >> (1 + bit);
+ bitPtr += 17;
+ }
+
+ protected void outCodeword18(int codeword) {
+ int bytePtr = bitPtr / 8;
+ int bit = bitPtr - bytePtr * 8;
+ outBits[bytePtr++] |= codeword >> (10 + bit);
+ outBits[bytePtr++] |= codeword >> (2 + bit);
+ codeword <<= 8;
+ outBits[bytePtr] |= codeword >> (2 + bit);
+ if (bit == 7)
+ outBits[++bytePtr] |= 0x80;
+ bitPtr += 18;
+ }
+
+ protected void outCodeword(int codeword) {
+ outCodeword17(codeword);
+ }
+
+ protected void outStopPattern() {
+ outCodeword18(STOP_PATTERN);
+ }
+
+ protected void outStartPattern() {
+ outCodeword17(START_PATTERN);
+ }
+
+ protected void outPaintCode() {
+ int codePtr = 0;
+ bitColumns = START_CODE_SIZE * (codeColumns + 3) + STOP_SIZE;
+ int lenBits = ((bitColumns - 1) / 8 + 1) * codeRows;
+ outBits = new byte[lenBits];
+ for (int row = 0; row < codeRows; ++row) {
+ bitPtr = ((bitColumns - 1) / 8 + 1) * 8 * row;
+ int rowMod = row % 3;
+ int cluster[] = CLUSTERS[rowMod];
+ outStartPattern();
+ int edge = 0;
+ switch (rowMod) {
+ case 0:
+ edge = 30 * (row / 3) + ((codeRows - 1) / 3);
+ break;
+ case 1:
+ edge = 30 * (row / 3) + errorLevel * 3 + ((codeRows - 1) % 3);
+ break;
+ default:
+ edge = 30 * (row / 3) + codeColumns - 1;
+ break;
+ }
+ outCodeword(cluster[edge]);
+
+ for (int column = 0; column < codeColumns; ++column) {
+ outCodeword(cluster[codewords[codePtr++]]);
+ }
+
+ switch (rowMod) {
+ case 0:
+ edge = 30 * (row / 3) + codeColumns - 1;
+ break;
+ case 1:
+ edge = 30 * (row / 3) + ((codeRows - 1) / 3);
+ break;
+ default:
+ edge = 30 * (row / 3) + errorLevel * 3 + ((codeRows - 1) % 3);
+ break;
+ }
+ outCodeword(cluster[edge]);
+ outStopPattern();
+ }
+ if ((options & PDF417_INVERT_BITMAP) != 0) {
+ for (int k = 0; k < outBits.length; ++k)
+ outBits[k] ^= 0xff;
+ }
+ }
+
+ protected void calculateErrorCorrection(int dest) {
+ if (errorLevel < 0 || errorLevel > 8)
+ errorLevel = 0;
+ int A[] = ERROR_LEVEL[errorLevel];
+ int Alength = 2 << errorLevel;
+ for (int k = 0; k < Alength; ++k)
+ codewords[dest + k] = 0;
+ int lastE = Alength - 1;
+ for (int k = 0; k < lenCodewords; ++k) {
+ int t1 = codewords[k] + codewords[dest];
+ for (int e = 0; e <= lastE; ++e) {
+ int t2 = (t1 * A[lastE - e]) % MOD;
+ int t3 = MOD - t2;
+ codewords[dest + e] = ((e == lastE ? 0 : codewords[dest + e + 1]) + t3) % MOD;
+ }
+ }
+ for (int k = 0; k < Alength; ++k)
+ codewords[dest + k] = (MOD - codewords[dest + k]) % MOD;
+ }
+
+ private static int getTextTypeAndValue(byte[] input, int maxLength, int idx) {
+ if (idx >= maxLength)
+ return 0;
+ char c = (char)(input[idx] & 0xff);
+ if (c >= 'A' && c <= 'Z')
+ return (ALPHA + c - 'A');
+ if (c >= 'a' && c <= 'z')
+ return (LOWER + c - 'a');
+ if (c == ' ')
+ return (ALPHA + LOWER + MIXED + SPACE);
+ int ms = MIXED_SET.indexOf(c);
+ int ps = PUNCTUATION_SET.indexOf(c);
+ if (ms < 0 && ps < 0)
+ return (ISBYTE + c);
+ if (ms == ps)
+ return (MIXED + PUNCTUATION + ms);
+ if (ms >= 0)
+ return (MIXED + ms);
+ return (PUNCTUATION + ps);
+ }
+
+ protected int getTextTypeAndValue(int maxLength, int idx) {
+ return getTextTypeAndValue(text, maxLength,idx);
+ }
+
+ private void textCompaction(byte[] input, int start, int length) {
+ int dest[] = new int[ABSOLUTE_MAX_TEXT_SIZE * 2];
+ int mode = ALPHA;
+ int ptr = 0;
+ int fullBytes = 0;
+ int v = 0;
+ int k;
+ int size;
+ length += start;
+ for (k = start; k < length; ++k) {
+ v = getTextTypeAndValue(input, length, k);
+ if ((v & mode) != 0) {
+ dest[ptr++] = v & 0xff;
+ continue;
+ }
+ if ((v & ISBYTE) != 0) {
+ if ((ptr & 1) != 0) {
+ dest[ptr++] = (mode & PUNCTUATION) != 0 ? PAL : PS;
+ mode = (mode & PUNCTUATION) != 0 ? ALPHA : mode;
+ }
+ dest[ptr++] = BYTESHIFT;
+ dest[ptr++] = v & 0xff;
+ fullBytes += 2;
+ continue;
+ }
+ switch (mode) {
+ case ALPHA:
+ if ((v & LOWER) != 0) {
+ dest[ptr++] = LL;
+ dest[ptr++] = v & 0xff;
+ mode = LOWER;
+ }
+ else if ((v & MIXED) != 0) {
+ dest[ptr++] = ML;
+ dest[ptr++] = v & 0xff;
+ mode = MIXED;
+ }
+ else if ((getTextTypeAndValue(input, length, k + 1) & getTextTypeAndValue(input, length, k + 2) & PUNCTUATION) != 0) {
+ dest[ptr++] = ML;
+ dest[ptr++] = PL;
+ dest[ptr++] = v & 0xff;
+ mode = PUNCTUATION;
+ }
+ else {
+ dest[ptr++] = PS;
+ dest[ptr++] = v & 0xff;
+ }
+ break;
+ case LOWER:
+ if ((v & ALPHA) != 0) {
+ if ((getTextTypeAndValue(input, length, k + 1) & getTextTypeAndValue(input, length, k + 2) & ALPHA) != 0) {
+ dest[ptr++] = ML;
+ dest[ptr++] = AL;
+ mode = ALPHA;
+ }
+ else {
+ dest[ptr++] = AS;
+ }
+ dest[ptr++] = v & 0xff;
+ }
+ else if ((v & MIXED) != 0) {
+ dest[ptr++] = ML;
+ dest[ptr++] = v & 0xff;
+ mode = MIXED;
+ }
+ else if ((getTextTypeAndValue(input, length, k + 1) & getTextTypeAndValue(input, length, k + 2) & PUNCTUATION) != 0) {
+ dest[ptr++] = ML;
+ dest[ptr++] = PL;
+ dest[ptr++] = v & 0xff;
+ mode = PUNCTUATION;
+ }
+ else {
+ dest[ptr++] = PS;
+ dest[ptr++] = v & 0xff;
+ }
+ break;
+ case MIXED:
+ if ((v & LOWER) != 0) {
+ dest[ptr++] = LL;
+ dest[ptr++] = v & 0xff;
+ mode = LOWER;
+ }
+ else if ((v & ALPHA) != 0) {
+ dest[ptr++] = AL;
+ dest[ptr++] = v & 0xff;
+ mode = ALPHA;
+ }
+ else if ((getTextTypeAndValue(input, length, k + 1) & getTextTypeAndValue(input, length, k + 2) & PUNCTUATION) != 0) {
+ dest[ptr++] = PL;
+ dest[ptr++] = v & 0xff;
+ mode = PUNCTUATION;
+ }
+ else {
+ dest[ptr++] = PS;
+ dest[ptr++] = v & 0xff;
+ }
+ break;
+ case PUNCTUATION:
+ dest[ptr++] = PAL;
+ mode = ALPHA;
+ --k;
+ break;
+ }
+ }
+ if ((ptr & 1) != 0)
+ dest[ptr++] = PS;
+ size = (ptr + fullBytes) / 2;
+ if (size + cwPtr > MAX_DATA_CODEWORDS) {
+ throw new IndexOutOfBoundsException("The text is too big.");
+ }
+ length = ptr;
+ ptr = 0;
+ while (ptr < length) {
+ v = dest[ptr++];
+ if (v >= 30) {
+ codewords[cwPtr++] = v;
+ codewords[cwPtr++] = dest[ptr++];
+ }
+ else
+ codewords[cwPtr++] = v * 30 + dest[ptr++];
+ }
+ }
+ protected void textCompaction(int start, int length) {
+ textCompaction(text, start, length);
+ }
+
+ protected void basicNumberCompaction(int start, int length) {
+ basicNumberCompaction(text, start, length);
+ }
+
+ private void basicNumberCompaction(byte[] input, int start, int length) {
+ int ret = cwPtr;
+ int retLast = length / 3;
+ int ni, k;
+ cwPtr += retLast + 1;
+ for (k = 0; k <= retLast; ++k)
+ codewords[ret + k] = 0;
+ codewords[ret + retLast] = 1;
+ length += start;
+ for (ni = start; ni < length; ++ni) {
+ // multiply by 10
+ for (k = retLast; k >= 0; --k)
+ codewords[ret + k] *= 10;
+ // add the digit
+ codewords[ret + retLast] += input[ni] - '0';
+ // propagate carry
+ for (k = retLast; k > 0; --k) {
+ codewords[ret + k - 1] += codewords[ret + k] / 900;
+ codewords[ret + k] %= 900;
+ }
+ }
+ }
+
+ private void numberCompaction(byte[] input, int start, int length) {
+ int full = (length / 44) * 15;
+ int size = length % 44;
+ int k;
+ if (size == 0)
+ size = full;
+ else
+ size = full + size / 3 + 1;
+ if (size + cwPtr > MAX_DATA_CODEWORDS) {
+ throw new IndexOutOfBoundsException("The text is too big.");
+ }
+ length += start;
+ for (k = start; k < length; k += 44) {
+ size = length - k < 44 ? length - k : 44;
+ basicNumberCompaction(input, k, size);
+ }
+ }
+
+ protected void numberCompaction(int start, int length) {
+ numberCompaction(text, start, length);
+ }
+
+ protected void byteCompaction6(int start) {
+ int length = 6;
+ int ret = cwPtr;
+ int retLast = 4;
+ int ni, k;
+ cwPtr += retLast + 1;
+ for (k = 0; k <= retLast ; ++k)
+ codewords[ret + k] = 0;
+ length += start;
+ for (ni = start; ni < length; ++ni) {
+ // multiply by 256
+ for (k = retLast; k >= 0; --k)
+ codewords[ret + k] *= 256;
+ // add the digit
+ codewords[ret + retLast] += (int)text[ni] & 0xff;
+ // propagate carry
+ for (k = retLast; k > 0; --k) {
+ codewords[ret + k - 1] += codewords[ret + k] / 900;
+ codewords[ret + k] %= 900;
+ }
+ }
+ }
+
+ void byteCompaction(int start, int length) {
+ int k, j;
+ int size = (length / 6) * 5 + (length % 6);
+ if (size + cwPtr > MAX_DATA_CODEWORDS) {
+ throw new IndexOutOfBoundsException("The text is too big.");
+ }
+ length += start;
+ for (k = start; k < length; k += 6) {
+ size = length - k < 44 ? length - k : 6;
+ if (size < 6) {
+ for (j = 0; j < size; ++j)
+ codewords[cwPtr++] = (int)text[k + j] & 0xff;
+ }
+ else {
+ byteCompaction6(k);
+ }
+ }
+ }
+
+ void breakString() {
+ int textLength = text.length;
+ int lastP = 0;
+ int startN = 0;
+ int nd = 0;
+ char c = 0;
+ int k, j;
+ boolean lastTxt, txt;
+ Segment v;
+ Segment vp;
+ Segment vn;
+
+ if ((options & PDF417_FORCE_BINARY) != 0) {
+ segmentList.add('B', 0, textLength);
+ return;
+ }
+ for (k = 0; k < textLength; ++k) {
+ c = (char)(text[k] & 0xff);
+ if (c >= '0' && c <= '9') {
+ if (nd == 0)
+ startN = k;
+ ++nd;
+ continue;
+ }
+ if (nd >= 13) {
+ if (lastP != startN) {
+ c = (char)(text[lastP] & 0xff);
+ lastTxt = (c >= ' ' && c < 127) || c == '\r' || c == '\n' || c == '\t';
+ for (j = lastP; j < startN; ++j) {
+ c = (char)(text[j] & 0xff);
+ txt = (c >= ' ' && c < 127) || c == '\r' || c == '\n' || c == '\t';
+ if (txt != lastTxt) {
+ segmentList.add(lastTxt ? 'T' : 'B', lastP, j);
+ lastP = j;
+ lastTxt = txt;
+ }
+ }
+ segmentList.add(lastTxt ? 'T' : 'B', lastP, startN);
+ }
+ segmentList.add('N', startN, k);
+ lastP = k;
+ }
+ nd = 0;
+ }
+ if (nd < 13)
+ startN = textLength;
+ if (lastP != startN) {
+ c = (char)(text[lastP] & 0xff);
+ lastTxt = (c >= ' ' && c < 127) || c == '\r' || c == '\n' || c == '\t';
+ for (j = lastP; j < startN; ++j) {
+ c = (char)(text[j] & 0xff);
+ txt = (c >= ' ' && c < 127) || c == '\r' || c == '\n' || c == '\t';
+ if (txt != lastTxt) {
+ segmentList.add(lastTxt ? 'T' : 'B', lastP, j);
+ lastP = j;
+ lastTxt = txt;
+ }
+ }
+ segmentList.add(lastTxt ? 'T' : 'B', lastP, startN);
+ }
+ if (nd >= 13)
+ segmentList.add('N', startN, textLength);
+ //optimize
+ //merge short binary
+ for (k = 0; k < segmentList.size(); ++k) {
+ v = segmentList.get(k);
+ vp = segmentList.get(k - 1);
+ vn = segmentList.get(k + 1);;
+ if (checkSegmentType(v, 'B') && getSegmentLength(v) == 1) {
+ if (checkSegmentType(vp, 'T') && checkSegmentType(vn, 'T')
+ && getSegmentLength(vp) + getSegmentLength(vn) >= 3) {
+ vp.end = vn.end;
+ segmentList.remove(k);
+ segmentList.remove(k);
+ k = -1;
+ continue;
+ }
+ }
+ }
+ //merge text sections
+ for (k = 0; k < segmentList.size(); ++k) {
+ v = segmentList.get(k);
+ vp = segmentList.get(k - 1);
+ vn = segmentList.get(k + 1);;
+ if (checkSegmentType(v, 'T') && getSegmentLength(v) >= 5) {
+ boolean redo = false;
+ if ((checkSegmentType(vp, 'B') && getSegmentLength(vp) == 1) || checkSegmentType(vp, 'T')) {
+ redo = true;
+ v.start = vp.start;
+ segmentList.remove(k - 1);
+ --k;
+ }
+ if ((checkSegmentType(vn, 'B') && getSegmentLength(vn) == 1) || checkSegmentType(vn, 'T')) {
+ redo = true;
+ v.end = vn.end;
+ segmentList.remove(k + 1);
+ }
+ if (redo) {
+ k = -1;
+ continue;
+ }
+ }
+ }
+ //merge binary sections
+ for (k = 0; k < segmentList.size(); ++k) {
+ v = segmentList.get(k);
+ vp = segmentList.get(k - 1);
+ vn = segmentList.get(k + 1);;
+ if (checkSegmentType(v, 'B')) {
+ boolean redo = false;
+ if ((checkSegmentType(vp, 'T') && getSegmentLength(vp) < 5) || checkSegmentType(vp, 'B')) {
+ redo = true;
+ v.start = vp.start;
+ segmentList.remove(k - 1);
+ --k;
+ }
+ if ((checkSegmentType(vn, 'T') && getSegmentLength(vn) < 5) || checkSegmentType(vn, 'B')) {
+ redo = true;
+ v.end = vn.end;
+ segmentList.remove(k + 1);
+ }
+ if (redo) {
+ k = -1;
+ continue;
+ }
+ }
+ }
+ // check if all numbers
+ if (segmentList.size() == 1 && (v = segmentList.get(0)).type == 'T' && getSegmentLength(v) >= 8) {
+ for (k = v.start; k < v.end; ++k) {
+ c = (char)(text[k] & 0xff);
+ if (c < '0' || c > '9')
+ break;
+ }
+ if (k == v.end)
+ v.type = 'N';
+ }
+ }
+
+ protected void assemble() {
+ int k;
+ if (segmentList.size() == 0)
+ return;
+ cwPtr = 1;
+ for (k = 0; k < segmentList.size(); ++k) {
+ Segment v = segmentList.get(k);
+ switch (v.type) {
+ case 'T':
+ if (k != 0)
+ codewords[cwPtr++] = TEXT_MODE;
+ textCompaction(v.start, getSegmentLength(v));
+ break;
+ case 'N':
+ codewords[cwPtr++] = NUMERIC_MODE;
+ numberCompaction(v.start, getSegmentLength(v));
+ break;
+ case 'B':
+ codewords[cwPtr++] = (getSegmentLength(v) % 6) != 0 ? BYTE_MODE : BYTE_MODE_6;
+ byteCompaction(v.start, getSegmentLength(v));
+ break;
+ }
+ }
+
+ if ((options & PDF417_USE_MACRO) != 0) {
+ macroCodes();
+ }
+
+ }
+
+ private void macroCodes() {
+ if (macroSegmentId < 0) {
+ throw new IllegalStateException("macroSegmentId must be >=0");
+ }
+ if (macroSegmentId >= macroSegmentCount) {
+ throw new IllegalStateException("macroSegmentId must be < macroSemgentCount");
+ }
+ if (macroSegmentCount < 1) {
+ throw new IllegalStateException("macroSemgentCount must be > 0");
+ }
+
+ codewords[cwPtr++] = MACRO_SEGMENT_ID;
+ append(macroSegmentId, 5);
+
+ if (macroFileId != null) {
+ append(macroFileId);
+ }
+
+ codewords[cwPtr++] = MACRO_SEGMENT_COUNT;
+ codewords[cwPtr++] = 1;
+ append(macroSegmentCount, 5);
+
+ if (macroSegmentId >= macroSegmentCount-1) {
+ codewords[cwPtr++] = MACRO_LAST_SEGMENT;
+ }
+
+ }
+
+ private void append(int in, int len) {
+ StringBuffer sb = new StringBuffer(len+1);
+ sb.append(Integer.toString(in));
+ for(int i = sb.length(); i < len; i++) {
+ sb.insert(0, "0");
+ }
+
+ byte[] bytes = PdfEncodings.convertToBytes(sb.toString(), "cp437");
+ numberCompaction(bytes, 0, bytes.length);
+ }
+
+ private void append(String s) {
+ byte[] bytes = PdfEncodings.convertToBytes(s, "cp437");
+ textCompaction(bytes, 0, bytes.length);
+ }
+
+
+ protected static int maxPossibleErrorLevel(int remain) {
+ int level = 8;
+ int size = 512;
+ while (level > 0) {
+ if (remain >= size)
+ return level;
+ --level;
+ size >>= 1;
+ }
+ return 0;
+ }
+
+ protected void dumpList() {
+ if (segmentList.size() == 0)
+ return;
+ for (int k = 0; k < segmentList.size(); ++k) {
+ Segment v = segmentList.get(k);
+ int len = getSegmentLength(v);
+ char c[] = new char[len];
+ for (int j = 0; j < len; ++j) {
+ c[j] = (char)(text[v.start + j] & 0xff);
+ if (c[j] == '\r')
+ c[j] = '\n';
+ }
+ System.out.println("" + v.type + new String(c));
+ }
+ }
+
+ protected int getMaxSquare() {
+ if (codeColumns > 21) {
+ codeColumns = 29;
+ codeRows = 32;
+ }
+ else {
+ codeColumns = 16;
+ codeRows = 58;
+ }
+ return MAX_DATA_CODEWORDS + 2;
+ }
+
+ /** Paints the barcode. If no exception was thrown a valid barcode is available. */
+ public void paintCode() {
+ int maxErr, lenErr, tot, pad;
+ if ((options & PDF417_USE_RAW_CODEWORDS) != 0) {
+ if (lenCodewords > MAX_DATA_CODEWORDS || lenCodewords < 1 || lenCodewords != codewords[0]) {
+ throw new IllegalArgumentException("Invalid codeword size.");
+ }
+ }
+ else {
+ if (text == null)
+ throw new NullPointerException("Text cannot be null.");
+ if (text.length > ABSOLUTE_MAX_TEXT_SIZE) {
+ throw new IndexOutOfBoundsException("The text is too big.");
+ }
+ segmentList = new SegmentList();
+ breakString();
+ //dumpList();
+ assemble();
+ segmentList = null;
+ codewords[0] = lenCodewords = cwPtr;
+ }
+ maxErr = maxPossibleErrorLevel(MAX_DATA_CODEWORDS + 2 - lenCodewords);
+ if ((options & PDF417_USE_ERROR_LEVEL) == 0) {
+ if (lenCodewords < 41)
+ errorLevel = 2;
+ else if (lenCodewords < 161)
+ errorLevel = 3;
+ else if (lenCodewords < 321)
+ errorLevel = 4;
+ else
+ errorLevel = 5;
+ }
+ if (errorLevel < 0)
+ errorLevel = 0;
+ else if (errorLevel > maxErr)
+ errorLevel = maxErr;
+ if (codeColumns < 1)
+ codeColumns = 1;
+ else if (codeColumns > 30)
+ codeColumns = 30;
+ if (codeRows < 3)
+ codeRows = 3;
+ else if (codeRows > 90)
+ codeRows = 90;
+ lenErr = 2 << errorLevel;
+ boolean fixedColumn = (options & PDF417_FIXED_ROWS) == 0;
+ boolean skipRowColAdjust = false;
+ tot = lenCodewords + lenErr;
+ if ((options & PDF417_FIXED_RECTANGLE) != 0) {
+ tot = codeColumns * codeRows;
+ if (tot > MAX_DATA_CODEWORDS + 2) {
+ tot = getMaxSquare();
+ }
+ if (tot < lenCodewords + lenErr)
+ tot = lenCodewords + lenErr;
+ else
+ skipRowColAdjust = true;
+ }
+ else if ((options & (PDF417_FIXED_COLUMNS | PDF417_FIXED_ROWS)) == 0) {
+ double c, b;
+ fixedColumn = true;
+ if (aspectRatio < 0.001)
+ aspectRatio = 0.001f;
+ else if (aspectRatio > 1000)
+ aspectRatio = 1000;
+ b = 73 * aspectRatio - 4;
+ c = (-b + Math.sqrt(b * b + 4 * 17 * aspectRatio * (lenCodewords + lenErr) * yHeight)) / (2 * 17 * aspectRatio);
+ codeColumns = (int)(c + 0.5);
+ if (codeColumns < 1)
+ codeColumns = 1;
+ else if (codeColumns > 30)
+ codeColumns = 30;
+ }
+ if (!skipRowColAdjust) {
+ if (fixedColumn) {
+ codeRows = (tot - 1) / codeColumns + 1;
+ if (codeRows < 3)
+ codeRows = 3;
+ else if (codeRows > 90) {
+ codeRows = 90;
+ codeColumns = (tot - 1) / 90 + 1;
+ }
+ }
+ else {
+ codeColumns = (tot - 1) / codeRows + 1;
+ if (codeColumns > 30) {
+ codeColumns = 30;
+ codeRows = (tot - 1) / 30 + 1;
+ }
+ }
+ tot = codeRows * codeColumns;
+ }
+ if (tot > MAX_DATA_CODEWORDS + 2) {
+ tot = getMaxSquare();
+ }
+ errorLevel = maxPossibleErrorLevel(tot - lenCodewords);
+ lenErr = 2 << errorLevel;
+ pad = tot - lenErr - lenCodewords;
+ cwPtr = lenCodewords;
+ while (pad-- != 0)
+ codewords[cwPtr++] = TEXT_MODE;
+ codewords[0] = lenCodewords = cwPtr;
+ calculateErrorCorrection(lenCodewords);
+ lenCodewords = tot;
+ outPaintCode();
+ }
+
+ /** Gets an Image
with the barcode. The image will have to be
+ * scaled in the Y direction by yHeight
for the barcode
+ * to have the right printing aspect.
+ * @return the barcode Image
+ * @throws BadElementException on error
+ */
+ public Image getImage() throws BadElementException {
+ paintCode();
+ byte g4[] = CCITTG4Encoder.compress(outBits, bitColumns, codeRows);
+ return Image.getInstance(bitColumns, codeRows, false, Image.CCITTG4, (options & PDF417_INVERT_BITMAP) == 0 ? 0 : Image.CCITT_BLACKIS1, g4, null);
+ }
+
+ /** Creates a java.awt.Image
.
+ * @param foreground the color of the bars
+ * @param background the color of the background
+ * @return the image
+ */
+ public java.awt.Image createAwtImage(Color foreground, Color background) {
+ int f = foreground.getRGB();
+ int g = background.getRGB();
+ Canvas canvas = new Canvas();
+
+ paintCode();
+ int h = (int)yHeight;
+ int pix[] = new int[bitColumns * codeRows * h];
+ int stride = (bitColumns + 7) / 8;
+ int ptr = 0;
+ for (int k = 0; k < codeRows; ++k) {
+ int p = k * stride;
+ for (int j = 0; j < bitColumns; ++j) {
+ int b = outBits[p + (j / 8)] & 0xff;
+ b <<= j % 8;
+ pix[ptr++] = (b & 0x80) == 0 ? g : f;
+ }
+ for (int j = 1; j < h; ++j) {
+ System.arraycopy(pix, ptr - bitColumns, pix, ptr + bitColumns * (j - 1), bitColumns);
+ }
+ ptr += bitColumns * (h - 1);
+ }
+
+ java.awt.Image img = canvas.createImage(new MemoryImageSource(bitColumns, codeRows * h, pix, 0, bitColumns));
+ return img;
+ }
+
+ /** Gets the raw image bits of the barcode. The image will have to
+ * be scaled in the Y direction by yHeight
.
+ * @return The raw barcode image
+ */
+ public byte[] getOutBits() {
+ return this.outBits;
+ }
+
+ /** Gets the number of X pixels of outBits
.
+ * @return the number of X pixels of outBits
+ */
+ public int getBitColumns() {
+ return this.bitColumns;
+ }
+
+ /** Gets the number of Y pixels of outBits
.
+ * It is also the number of rows in the barcode.
+ * @return the number of Y pixels of outBits
+ */
+ public int getCodeRows() {
+ return this.codeRows;
+ }
+
+ /** Sets the number of barcode rows. This number may be changed
+ * to keep the barcode valid.
+ * @param codeRows the number of barcode rows
+ */
+ public void setCodeRows(int codeRows) {
+ this.codeRows = codeRows;
+ }
+
+ /** Gets the number of barcode data columns.
+ * @return he number of barcode data columns
+ */
+ public int getCodeColumns() {
+ return this.codeColumns;
+ }
+
+ /** Sets the number of barcode data columns.
+ * This number may be changed to keep the barcode valid.
+ * @param codeColumns the number of barcode data columns
+ */
+ public void setCodeColumns(int codeColumns) {
+ this.codeColumns = codeColumns;
+ }
+
+ /** Gets the codeword array. This array is always 928 elements long.
+ * It can be writen to if the option PDF417_USE_RAW_CODEWORDS
+ * is set.
+ * @return the codeword array
+ */
+ public int[] getCodewords() {
+ return this.codewords;
+ }
+
+ /** Gets the length of the codewords.
+ * @return the length of the codewords
+ */
+ public int getLenCodewords() {
+ return this.lenCodewords;
+ }
+
+ /** Sets the length of the codewords.
+ * @param lenCodewords the length of the codewords
+ */
+ public void setLenCodewords(int lenCodewords) {
+ this.lenCodewords = lenCodewords;
+ }
+
+ /** Gets the error level correction used for the barcode. It may different
+ * from the previously set value.
+ * @return the error level correction used for the barcode
+ */
+ public int getErrorLevel() {
+ return this.errorLevel;
+ }
+
+ /** Sets the error level correction for the barcode.
+ * @param errorLevel the error level correction for the barcode
+ */
+ public void setErrorLevel(int errorLevel) {
+ this.errorLevel = errorLevel;
+ }
+
+ /** Gets the bytes that form the barcode. This bytes should
+ * be interpreted in the codepage Cp437.
+ * @return the bytes that form the barcode
+ */
+ public byte[] getText() {
+ return this.text;
+ }
+
+ /** Sets the bytes that form the barcode. This bytes should
+ * be interpreted in the codepage Cp437.
+ * @param text the bytes that form the barcode
+ */
+ public void setText(byte[] text) {
+ this.text = text;
+ }
+
+ /** Sets the text that will form the barcode. This text is converted
+ * to bytes using the encoding Cp437.
+ * @param s the text that will form the barcode
+ * @throws UnsupportedEncodingException if the encoding Cp437 is not supported
+ */
+ public void setText(String s) {
+ this.text = PdfEncodings.convertToBytes(s, "cp437");
+ }
+
+ /** Gets the options to generate the barcode.
+ * @return the options to generate the barcode
+ */
+ public int getOptions() {
+ return this.options;
+ }
+
+ /** Sets the options to generate the barcode. This can be all
+ * the PDF417_*
constants.
+ * @param options the options to generate the barcode
+ */
+ public void setOptions(int options) {
+ this.options = options;
+ }
+
+ /** Gets the barcode aspect ratio.
+ * @return the barcode aspect ratio
+ */
+ public float getAspectRatio() {
+ return this.aspectRatio;
+ }
+
+ /** Sets the barcode aspect ratio. A ratio or 0.5 will make the
+ * barcode width twice as large as the height.
+ * @param aspectRatio the barcode aspect ratio
+ */
+ public void setAspectRatio(float aspectRatio) {
+ this.aspectRatio = aspectRatio;
+ }
+
+ /** Gets the Y pixel height relative to X.
+ * @return the Y pixel height relative to X
+ */
+ public float getYHeight() {
+ return this.yHeight;
+ }
+
+ /** Sets the Y pixel height relative to X. It is usually 3.
+ * @param yHeight the Y pixel height relative to X
+ */
+ public void setYHeight(float yHeight) {
+ this.yHeight = yHeight;
+ }
+
+ protected static final int START_PATTERN = 0x1fea8;
+ protected static final int STOP_PATTERN = 0x3fa29;
+ protected static final int START_CODE_SIZE = 17;
+ protected static final int STOP_SIZE = 18;
+ protected static final int MOD = 929;
+ protected static final int ALPHA = 0x10000;
+ protected static final int LOWER = 0x20000;
+ protected static final int MIXED = 0x40000;
+ protected static final int PUNCTUATION = 0x80000;
+ protected static final int ISBYTE = 0x100000;
+ protected static final int BYTESHIFT = 913;
+ protected static final int PL = 25;
+ protected static final int LL = 27;
+ protected static final int AS = 27;
+ protected static final int ML = 28;
+ protected static final int AL = 28;
+ protected static final int PS = 29;
+ protected static final int PAL = 29;
+ protected static final int SPACE = 26;
+ protected static final int TEXT_MODE = 900;
+ protected static final int BYTE_MODE_6 = 924;
+ protected static final int BYTE_MODE = 901;
+ protected static final int NUMERIC_MODE = 902;
+ protected static final int ABSOLUTE_MAX_TEXT_SIZE = 5420;
+ protected static final int MAX_DATA_CODEWORDS = 926;
+ protected static final int MACRO_SEGMENT_ID=928;
+ protected static final int MACRO_SEGMENT_COUNT=923;
+ protected static final int MACRO_LAST_SEGMENT=922;
+
+ static String MIXED_SET = "0123456789&\r\t,:#-.$/+%*=^";
+ static String PUNCTUATION_SET = ";<>@[\\]_`~!\r\t,:\n-.$/\"|*()?{}'";
+
+ static int CLUSTERS[][] =
+ {{
+ 0x1d5c0, 0x1eaf0, 0x1f57c, 0x1d4e0, 0x1ea78, 0x1f53e, 0x1a8c0, 0x1d470,
+ 0x1a860, 0x15040, 0x1a830, 0x15020, 0x1adc0, 0x1d6f0, 0x1eb7c, 0x1ace0,
+ 0x1d678, 0x1eb3e, 0x158c0, 0x1ac70, 0x15860, 0x15dc0, 0x1aef0, 0x1d77c,
+ 0x15ce0, 0x1ae78, 0x1d73e, 0x15c70, 0x1ae3c, 0x15ef0, 0x1af7c, 0x15e78,
+ 0x1af3e, 0x15f7c, 0x1f5fa, 0x1d2e0, 0x1e978, 0x1f4be, 0x1a4c0, 0x1d270,
+ 0x1e93c, 0x1a460, 0x1d238, 0x14840, 0x1a430, 0x1d21c, 0x14820, 0x1a418,
+ 0x14810, 0x1a6e0, 0x1d378, 0x1e9be, 0x14cc0, 0x1a670, 0x1d33c, 0x14c60,
+ 0x1a638, 0x1d31e, 0x14c30, 0x1a61c, 0x14ee0, 0x1a778, 0x1d3be, 0x14e70,
+ 0x1a73c, 0x14e38, 0x1a71e, 0x14f78, 0x1a7be, 0x14f3c, 0x14f1e, 0x1a2c0,
+ 0x1d170, 0x1e8bc, 0x1a260, 0x1d138, 0x1e89e, 0x14440, 0x1a230, 0x1d11c,
+ 0x14420, 0x1a218, 0x14410, 0x14408, 0x146c0, 0x1a370, 0x1d1bc, 0x14660,
+ 0x1a338, 0x1d19e, 0x14630, 0x1a31c, 0x14618, 0x1460c, 0x14770, 0x1a3bc,
+ 0x14738, 0x1a39e, 0x1471c, 0x147bc, 0x1a160, 0x1d0b8, 0x1e85e, 0x14240,
+ 0x1a130, 0x1d09c, 0x14220, 0x1a118, 0x1d08e, 0x14210, 0x1a10c, 0x14208,
+ 0x1a106, 0x14360, 0x1a1b8, 0x1d0de, 0x14330, 0x1a19c, 0x14318, 0x1a18e,
+ 0x1430c, 0x14306, 0x1a1de, 0x1438e, 0x14140, 0x1a0b0, 0x1d05c, 0x14120,
+ 0x1a098, 0x1d04e, 0x14110, 0x1a08c, 0x14108, 0x1a086, 0x14104, 0x141b0,
+ 0x14198, 0x1418c, 0x140a0, 0x1d02e, 0x1a04c, 0x1a046, 0x14082, 0x1cae0,
+ 0x1e578, 0x1f2be, 0x194c0, 0x1ca70, 0x1e53c, 0x19460, 0x1ca38, 0x1e51e,
+ 0x12840, 0x19430, 0x12820, 0x196e0, 0x1cb78, 0x1e5be, 0x12cc0, 0x19670,
+ 0x1cb3c, 0x12c60, 0x19638, 0x12c30, 0x12c18, 0x12ee0, 0x19778, 0x1cbbe,
+ 0x12e70, 0x1973c, 0x12e38, 0x12e1c, 0x12f78, 0x197be, 0x12f3c, 0x12fbe,
+ 0x1dac0, 0x1ed70, 0x1f6bc, 0x1da60, 0x1ed38, 0x1f69e, 0x1b440, 0x1da30,
+ 0x1ed1c, 0x1b420, 0x1da18, 0x1ed0e, 0x1b410, 0x1da0c, 0x192c0, 0x1c970,
+ 0x1e4bc, 0x1b6c0, 0x19260, 0x1c938, 0x1e49e, 0x1b660, 0x1db38, 0x1ed9e,
+ 0x16c40, 0x12420, 0x19218, 0x1c90e, 0x16c20, 0x1b618, 0x16c10, 0x126c0,
+ 0x19370, 0x1c9bc, 0x16ec0, 0x12660, 0x19338, 0x1c99e, 0x16e60, 0x1b738,
+ 0x1db9e, 0x16e30, 0x12618, 0x16e18, 0x12770, 0x193bc, 0x16f70, 0x12738,
+ 0x1939e, 0x16f38, 0x1b79e, 0x16f1c, 0x127bc, 0x16fbc, 0x1279e, 0x16f9e,
+ 0x1d960, 0x1ecb8, 0x1f65e, 0x1b240, 0x1d930, 0x1ec9c, 0x1b220, 0x1d918,
+ 0x1ec8e, 0x1b210, 0x1d90c, 0x1b208, 0x1b204, 0x19160, 0x1c8b8, 0x1e45e,
+ 0x1b360, 0x19130, 0x1c89c, 0x16640, 0x12220, 0x1d99c, 0x1c88e, 0x16620,
+ 0x12210, 0x1910c, 0x16610, 0x1b30c, 0x19106, 0x12204, 0x12360, 0x191b8,
+ 0x1c8de, 0x16760, 0x12330, 0x1919c, 0x16730, 0x1b39c, 0x1918e, 0x16718,
+ 0x1230c, 0x12306, 0x123b8, 0x191de, 0x167b8, 0x1239c, 0x1679c, 0x1238e,
+ 0x1678e, 0x167de, 0x1b140, 0x1d8b0, 0x1ec5c, 0x1b120, 0x1d898, 0x1ec4e,
+ 0x1b110, 0x1d88c, 0x1b108, 0x1d886, 0x1b104, 0x1b102, 0x12140, 0x190b0,
+ 0x1c85c, 0x16340, 0x12120, 0x19098, 0x1c84e, 0x16320, 0x1b198, 0x1d8ce,
+ 0x16310, 0x12108, 0x19086, 0x16308, 0x1b186, 0x16304, 0x121b0, 0x190dc,
+ 0x163b0, 0x12198, 0x190ce, 0x16398, 0x1b1ce, 0x1638c, 0x12186, 0x16386,
+ 0x163dc, 0x163ce, 0x1b0a0, 0x1d858, 0x1ec2e, 0x1b090, 0x1d84c, 0x1b088,
+ 0x1d846, 0x1b084, 0x1b082, 0x120a0, 0x19058, 0x1c82e, 0x161a0, 0x12090,
+ 0x1904c, 0x16190, 0x1b0cc, 0x19046, 0x16188, 0x12084, 0x16184, 0x12082,
+ 0x120d8, 0x161d8, 0x161cc, 0x161c6, 0x1d82c, 0x1d826, 0x1b042, 0x1902c,
+ 0x12048, 0x160c8, 0x160c4, 0x160c2, 0x18ac0, 0x1c570, 0x1e2bc, 0x18a60,
+ 0x1c538, 0x11440, 0x18a30, 0x1c51c, 0x11420, 0x18a18, 0x11410, 0x11408,
+ 0x116c0, 0x18b70, 0x1c5bc, 0x11660, 0x18b38, 0x1c59e, 0x11630, 0x18b1c,
+ 0x11618, 0x1160c, 0x11770, 0x18bbc, 0x11738, 0x18b9e, 0x1171c, 0x117bc,
+ 0x1179e, 0x1cd60, 0x1e6b8, 0x1f35e, 0x19a40, 0x1cd30, 0x1e69c, 0x19a20,
+ 0x1cd18, 0x1e68e, 0x19a10, 0x1cd0c, 0x19a08, 0x1cd06, 0x18960, 0x1c4b8,
+ 0x1e25e, 0x19b60, 0x18930, 0x1c49c, 0x13640, 0x11220, 0x1cd9c, 0x1c48e,
+ 0x13620, 0x19b18, 0x1890c, 0x13610, 0x11208, 0x13608, 0x11360, 0x189b8,
+ 0x1c4de, 0x13760, 0x11330, 0x1cdde, 0x13730, 0x19b9c, 0x1898e, 0x13718,
+ 0x1130c, 0x1370c, 0x113b8, 0x189de, 0x137b8, 0x1139c, 0x1379c, 0x1138e,
+ 0x113de, 0x137de, 0x1dd40, 0x1eeb0, 0x1f75c, 0x1dd20, 0x1ee98, 0x1f74e,
+ 0x1dd10, 0x1ee8c, 0x1dd08, 0x1ee86, 0x1dd04, 0x19940, 0x1ccb0, 0x1e65c,
+ 0x1bb40, 0x19920, 0x1eedc, 0x1e64e, 0x1bb20, 0x1dd98, 0x1eece, 0x1bb10,
+ 0x19908, 0x1cc86, 0x1bb08, 0x1dd86, 0x19902, 0x11140, 0x188b0, 0x1c45c,
+ 0x13340, 0x11120, 0x18898, 0x1c44e, 0x17740, 0x13320, 0x19998, 0x1ccce,
+ 0x17720, 0x1bb98, 0x1ddce, 0x18886, 0x17710, 0x13308, 0x19986, 0x17708,
+ 0x11102, 0x111b0, 0x188dc, 0x133b0, 0x11198, 0x188ce, 0x177b0, 0x13398,
+ 0x199ce, 0x17798, 0x1bbce, 0x11186, 0x13386, 0x111dc, 0x133dc, 0x111ce,
+ 0x177dc, 0x133ce, 0x1dca0, 0x1ee58, 0x1f72e, 0x1dc90, 0x1ee4c, 0x1dc88,
+ 0x1ee46, 0x1dc84, 0x1dc82, 0x198a0, 0x1cc58, 0x1e62e, 0x1b9a0, 0x19890,
+ 0x1ee6e, 0x1b990, 0x1dccc, 0x1cc46, 0x1b988, 0x19884, 0x1b984, 0x19882,
+ 0x1b982, 0x110a0, 0x18858, 0x1c42e, 0x131a0, 0x11090, 0x1884c, 0x173a0,
+ 0x13190, 0x198cc, 0x18846, 0x17390, 0x1b9cc, 0x11084, 0x17388, 0x13184,
+ 0x11082, 0x13182, 0x110d8, 0x1886e, 0x131d8, 0x110cc, 0x173d8, 0x131cc,
+ 0x110c6, 0x173cc, 0x131c6, 0x110ee, 0x173ee, 0x1dc50, 0x1ee2c, 0x1dc48,
+ 0x1ee26, 0x1dc44, 0x1dc42, 0x19850, 0x1cc2c, 0x1b8d0, 0x19848, 0x1cc26,
+ 0x1b8c8, 0x1dc66, 0x1b8c4, 0x19842, 0x1b8c2, 0x11050, 0x1882c, 0x130d0,
+ 0x11048, 0x18826, 0x171d0, 0x130c8, 0x19866, 0x171c8, 0x1b8e6, 0x11042,
+ 0x171c4, 0x130c2, 0x171c2, 0x130ec, 0x171ec, 0x171e6, 0x1ee16, 0x1dc22,
+ 0x1cc16, 0x19824, 0x19822, 0x11028, 0x13068, 0x170e8, 0x11022, 0x13062,
+ 0x18560, 0x10a40, 0x18530, 0x10a20, 0x18518, 0x1c28e, 0x10a10, 0x1850c,
+ 0x10a08, 0x18506, 0x10b60, 0x185b8, 0x1c2de, 0x10b30, 0x1859c, 0x10b18,
+ 0x1858e, 0x10b0c, 0x10b06, 0x10bb8, 0x185de, 0x10b9c, 0x10b8e, 0x10bde,
+ 0x18d40, 0x1c6b0, 0x1e35c, 0x18d20, 0x1c698, 0x18d10, 0x1c68c, 0x18d08,
+ 0x1c686, 0x18d04, 0x10940, 0x184b0, 0x1c25c, 0x11b40, 0x10920, 0x1c6dc,
+ 0x1c24e, 0x11b20, 0x18d98, 0x1c6ce, 0x11b10, 0x10908, 0x18486, 0x11b08,
+ 0x18d86, 0x10902, 0x109b0, 0x184dc, 0x11bb0, 0x10998, 0x184ce, 0x11b98,
+ 0x18dce, 0x11b8c, 0x10986, 0x109dc, 0x11bdc, 0x109ce, 0x11bce, 0x1cea0,
+ 0x1e758, 0x1f3ae, 0x1ce90, 0x1e74c, 0x1ce88, 0x1e746, 0x1ce84, 0x1ce82,
+ 0x18ca0, 0x1c658, 0x19da0, 0x18c90, 0x1c64c, 0x19d90, 0x1cecc, 0x1c646,
+ 0x19d88, 0x18c84, 0x19d84, 0x18c82, 0x19d82, 0x108a0, 0x18458, 0x119a0,
+ 0x10890, 0x1c66e, 0x13ba0, 0x11990, 0x18ccc, 0x18446, 0x13b90, 0x19dcc,
+ 0x10884, 0x13b88, 0x11984, 0x10882, 0x11982, 0x108d8, 0x1846e, 0x119d8,
+ 0x108cc, 0x13bd8, 0x119cc, 0x108c6, 0x13bcc, 0x119c6, 0x108ee, 0x119ee,
+ 0x13bee, 0x1ef50, 0x1f7ac, 0x1ef48, 0x1f7a6, 0x1ef44, 0x1ef42, 0x1ce50,
+ 0x1e72c, 0x1ded0, 0x1ef6c, 0x1e726, 0x1dec8, 0x1ef66, 0x1dec4, 0x1ce42,
+ 0x1dec2, 0x18c50, 0x1c62c, 0x19cd0, 0x18c48, 0x1c626, 0x1bdd0, 0x19cc8,
+ 0x1ce66, 0x1bdc8, 0x1dee6, 0x18c42, 0x1bdc4, 0x19cc2, 0x1bdc2, 0x10850,
+ 0x1842c, 0x118d0, 0x10848, 0x18426, 0x139d0, 0x118c8, 0x18c66, 0x17bd0,
+ 0x139c8, 0x19ce6, 0x10842, 0x17bc8, 0x1bde6, 0x118c2, 0x17bc4, 0x1086c,
+ 0x118ec, 0x10866, 0x139ec, 0x118e6, 0x17bec, 0x139e6, 0x17be6, 0x1ef28,
+ 0x1f796, 0x1ef24, 0x1ef22, 0x1ce28, 0x1e716, 0x1de68, 0x1ef36, 0x1de64,
+ 0x1ce22, 0x1de62, 0x18c28, 0x1c616, 0x19c68, 0x18c24, 0x1bce8, 0x19c64,
+ 0x18c22, 0x1bce4, 0x19c62, 0x1bce2, 0x10828, 0x18416, 0x11868, 0x18c36,
+ 0x138e8, 0x11864, 0x10822, 0x179e8, 0x138e4, 0x11862, 0x179e4, 0x138e2,
+ 0x179e2, 0x11876, 0x179f6, 0x1ef12, 0x1de34, 0x1de32, 0x19c34, 0x1bc74,
+ 0x1bc72, 0x11834, 0x13874, 0x178f4, 0x178f2, 0x10540, 0x10520, 0x18298,
+ 0x10510, 0x10508, 0x10504, 0x105b0, 0x10598, 0x1058c, 0x10586, 0x105dc,
+ 0x105ce, 0x186a0, 0x18690, 0x1c34c, 0x18688, 0x1c346, 0x18684, 0x18682,
+ 0x104a0, 0x18258, 0x10da0, 0x186d8, 0x1824c, 0x10d90, 0x186cc, 0x10d88,
+ 0x186c6, 0x10d84, 0x10482, 0x10d82, 0x104d8, 0x1826e, 0x10dd8, 0x186ee,
+ 0x10dcc, 0x104c6, 0x10dc6, 0x104ee, 0x10dee, 0x1c750, 0x1c748, 0x1c744,
+ 0x1c742, 0x18650, 0x18ed0, 0x1c76c, 0x1c326, 0x18ec8, 0x1c766, 0x18ec4,
+ 0x18642, 0x18ec2, 0x10450, 0x10cd0, 0x10448, 0x18226, 0x11dd0, 0x10cc8,
+ 0x10444, 0x11dc8, 0x10cc4, 0x10442, 0x11dc4, 0x10cc2, 0x1046c, 0x10cec,
+ 0x10466, 0x11dec, 0x10ce6, 0x11de6, 0x1e7a8, 0x1e7a4, 0x1e7a2, 0x1c728,
+ 0x1cf68, 0x1e7b6, 0x1cf64, 0x1c722, 0x1cf62, 0x18628, 0x1c316, 0x18e68,
+ 0x1c736, 0x19ee8, 0x18e64, 0x18622, 0x19ee4, 0x18e62, 0x19ee2, 0x10428,
+ 0x18216, 0x10c68, 0x18636, 0x11ce8, 0x10c64, 0x10422, 0x13de8, 0x11ce4,
+ 0x10c62, 0x13de4, 0x11ce2, 0x10436, 0x10c76, 0x11cf6, 0x13df6, 0x1f7d4,
+ 0x1f7d2, 0x1e794, 0x1efb4, 0x1e792, 0x1efb2, 0x1c714, 0x1cf34, 0x1c712,
+ 0x1df74, 0x1cf32, 0x1df72, 0x18614, 0x18e34, 0x18612, 0x19e74, 0x18e32,
+ 0x1bef4
+ }, {
+ 0x1f560, 0x1fab8, 0x1ea40, 0x1f530, 0x1fa9c, 0x1ea20, 0x1f518, 0x1fa8e,
+ 0x1ea10, 0x1f50c, 0x1ea08, 0x1f506, 0x1ea04, 0x1eb60, 0x1f5b8, 0x1fade,
+ 0x1d640, 0x1eb30, 0x1f59c, 0x1d620, 0x1eb18, 0x1f58e, 0x1d610, 0x1eb0c,
+ 0x1d608, 0x1eb06, 0x1d604, 0x1d760, 0x1ebb8, 0x1f5de, 0x1ae40, 0x1d730,
+ 0x1eb9c, 0x1ae20, 0x1d718, 0x1eb8e, 0x1ae10, 0x1d70c, 0x1ae08, 0x1d706,
+ 0x1ae04, 0x1af60, 0x1d7b8, 0x1ebde, 0x15e40, 0x1af30, 0x1d79c, 0x15e20,
+ 0x1af18, 0x1d78e, 0x15e10, 0x1af0c, 0x15e08, 0x1af06, 0x15f60, 0x1afb8,
+ 0x1d7de, 0x15f30, 0x1af9c, 0x15f18, 0x1af8e, 0x15f0c, 0x15fb8, 0x1afde,
+ 0x15f9c, 0x15f8e, 0x1e940, 0x1f4b0, 0x1fa5c, 0x1e920, 0x1f498, 0x1fa4e,
+ 0x1e910, 0x1f48c, 0x1e908, 0x1f486, 0x1e904, 0x1e902, 0x1d340, 0x1e9b0,
+ 0x1f4dc, 0x1d320, 0x1e998, 0x1f4ce, 0x1d310, 0x1e98c, 0x1d308, 0x1e986,
+ 0x1d304, 0x1d302, 0x1a740, 0x1d3b0, 0x1e9dc, 0x1a720, 0x1d398, 0x1e9ce,
+ 0x1a710, 0x1d38c, 0x1a708, 0x1d386, 0x1a704, 0x1a702, 0x14f40, 0x1a7b0,
+ 0x1d3dc, 0x14f20, 0x1a798, 0x1d3ce, 0x14f10, 0x1a78c, 0x14f08, 0x1a786,
+ 0x14f04, 0x14fb0, 0x1a7dc, 0x14f98, 0x1a7ce, 0x14f8c, 0x14f86, 0x14fdc,
+ 0x14fce, 0x1e8a0, 0x1f458, 0x1fa2e, 0x1e890, 0x1f44c, 0x1e888, 0x1f446,
+ 0x1e884, 0x1e882, 0x1d1a0, 0x1e8d8, 0x1f46e, 0x1d190, 0x1e8cc, 0x1d188,
+ 0x1e8c6, 0x1d184, 0x1d182, 0x1a3a0, 0x1d1d8, 0x1e8ee, 0x1a390, 0x1d1cc,
+ 0x1a388, 0x1d1c6, 0x1a384, 0x1a382, 0x147a0, 0x1a3d8, 0x1d1ee, 0x14790,
+ 0x1a3cc, 0x14788, 0x1a3c6, 0x14784, 0x14782, 0x147d8, 0x1a3ee, 0x147cc,
+ 0x147c6, 0x147ee, 0x1e850, 0x1f42c, 0x1e848, 0x1f426, 0x1e844, 0x1e842,
+ 0x1d0d0, 0x1e86c, 0x1d0c8, 0x1e866, 0x1d0c4, 0x1d0c2, 0x1a1d0, 0x1d0ec,
+ 0x1a1c8, 0x1d0e6, 0x1a1c4, 0x1a1c2, 0x143d0, 0x1a1ec, 0x143c8, 0x1a1e6,
+ 0x143c4, 0x143c2, 0x143ec, 0x143e6, 0x1e828, 0x1f416, 0x1e824, 0x1e822,
+ 0x1d068, 0x1e836, 0x1d064, 0x1d062, 0x1a0e8, 0x1d076, 0x1a0e4, 0x1a0e2,
+ 0x141e8, 0x1a0f6, 0x141e4, 0x141e2, 0x1e814, 0x1e812, 0x1d034, 0x1d032,
+ 0x1a074, 0x1a072, 0x1e540, 0x1f2b0, 0x1f95c, 0x1e520, 0x1f298, 0x1f94e,
+ 0x1e510, 0x1f28c, 0x1e508, 0x1f286, 0x1e504, 0x1e502, 0x1cb40, 0x1e5b0,
+ 0x1f2dc, 0x1cb20, 0x1e598, 0x1f2ce, 0x1cb10, 0x1e58c, 0x1cb08, 0x1e586,
+ 0x1cb04, 0x1cb02, 0x19740, 0x1cbb0, 0x1e5dc, 0x19720, 0x1cb98, 0x1e5ce,
+ 0x19710, 0x1cb8c, 0x19708, 0x1cb86, 0x19704, 0x19702, 0x12f40, 0x197b0,
+ 0x1cbdc, 0x12f20, 0x19798, 0x1cbce, 0x12f10, 0x1978c, 0x12f08, 0x19786,
+ 0x12f04, 0x12fb0, 0x197dc, 0x12f98, 0x197ce, 0x12f8c, 0x12f86, 0x12fdc,
+ 0x12fce, 0x1f6a0, 0x1fb58, 0x16bf0, 0x1f690, 0x1fb4c, 0x169f8, 0x1f688,
+ 0x1fb46, 0x168fc, 0x1f684, 0x1f682, 0x1e4a0, 0x1f258, 0x1f92e, 0x1eda0,
+ 0x1e490, 0x1fb6e, 0x1ed90, 0x1f6cc, 0x1f246, 0x1ed88, 0x1e484, 0x1ed84,
+ 0x1e482, 0x1ed82, 0x1c9a0, 0x1e4d8, 0x1f26e, 0x1dba0, 0x1c990, 0x1e4cc,
+ 0x1db90, 0x1edcc, 0x1e4c6, 0x1db88, 0x1c984, 0x1db84, 0x1c982, 0x1db82,
+ 0x193a0, 0x1c9d8, 0x1e4ee, 0x1b7a0, 0x19390, 0x1c9cc, 0x1b790, 0x1dbcc,
+ 0x1c9c6, 0x1b788, 0x19384, 0x1b784, 0x19382, 0x1b782, 0x127a0, 0x193d8,
+ 0x1c9ee, 0x16fa0, 0x12790, 0x193cc, 0x16f90, 0x1b7cc, 0x193c6, 0x16f88,
+ 0x12784, 0x16f84, 0x12782, 0x127d8, 0x193ee, 0x16fd8, 0x127cc, 0x16fcc,
+ 0x127c6, 0x16fc6, 0x127ee, 0x1f650, 0x1fb2c, 0x165f8, 0x1f648, 0x1fb26,
+ 0x164fc, 0x1f644, 0x1647e, 0x1f642, 0x1e450, 0x1f22c, 0x1ecd0, 0x1e448,
+ 0x1f226, 0x1ecc8, 0x1f666, 0x1ecc4, 0x1e442, 0x1ecc2, 0x1c8d0, 0x1e46c,
+ 0x1d9d0, 0x1c8c8, 0x1e466, 0x1d9c8, 0x1ece6, 0x1d9c4, 0x1c8c2, 0x1d9c2,
+ 0x191d0, 0x1c8ec, 0x1b3d0, 0x191c8, 0x1c8e6, 0x1b3c8, 0x1d9e6, 0x1b3c4,
+ 0x191c2, 0x1b3c2, 0x123d0, 0x191ec, 0x167d0, 0x123c8, 0x191e6, 0x167c8,
+ 0x1b3e6, 0x167c4, 0x123c2, 0x167c2, 0x123ec, 0x167ec, 0x123e6, 0x167e6,
+ 0x1f628, 0x1fb16, 0x162fc, 0x1f624, 0x1627e, 0x1f622, 0x1e428, 0x1f216,
+ 0x1ec68, 0x1f636, 0x1ec64, 0x1e422, 0x1ec62, 0x1c868, 0x1e436, 0x1d8e8,
+ 0x1c864, 0x1d8e4, 0x1c862, 0x1d8e2, 0x190e8, 0x1c876, 0x1b1e8, 0x1d8f6,
+ 0x1b1e4, 0x190e2, 0x1b1e2, 0x121e8, 0x190f6, 0x163e8, 0x121e4, 0x163e4,
+ 0x121e2, 0x163e2, 0x121f6, 0x163f6, 0x1f614, 0x1617e, 0x1f612, 0x1e414,
+ 0x1ec34, 0x1e412, 0x1ec32, 0x1c834, 0x1d874, 0x1c832, 0x1d872, 0x19074,
+ 0x1b0f4, 0x19072, 0x1b0f2, 0x120f4, 0x161f4, 0x120f2, 0x161f2, 0x1f60a,
+ 0x1e40a, 0x1ec1a, 0x1c81a, 0x1d83a, 0x1903a, 0x1b07a, 0x1e2a0, 0x1f158,
+ 0x1f8ae, 0x1e290, 0x1f14c, 0x1e288, 0x1f146, 0x1e284, 0x1e282, 0x1c5a0,
+ 0x1e2d8, 0x1f16e, 0x1c590, 0x1e2cc, 0x1c588, 0x1e2c6, 0x1c584, 0x1c582,
+ 0x18ba0, 0x1c5d8, 0x1e2ee, 0x18b90, 0x1c5cc, 0x18b88, 0x1c5c6, 0x18b84,
+ 0x18b82, 0x117a0, 0x18bd8, 0x1c5ee, 0x11790, 0x18bcc, 0x11788, 0x18bc6,
+ 0x11784, 0x11782, 0x117d8, 0x18bee, 0x117cc, 0x117c6, 0x117ee, 0x1f350,
+ 0x1f9ac, 0x135f8, 0x1f348, 0x1f9a6, 0x134fc, 0x1f344, 0x1347e, 0x1f342,
+ 0x1e250, 0x1f12c, 0x1e6d0, 0x1e248, 0x1f126, 0x1e6c8, 0x1f366, 0x1e6c4,
+ 0x1e242, 0x1e6c2, 0x1c4d0, 0x1e26c, 0x1cdd0, 0x1c4c8, 0x1e266, 0x1cdc8,
+ 0x1e6e6, 0x1cdc4, 0x1c4c2, 0x1cdc2, 0x189d0, 0x1c4ec, 0x19bd0, 0x189c8,
+ 0x1c4e6, 0x19bc8, 0x1cde6, 0x19bc4, 0x189c2, 0x19bc2, 0x113d0, 0x189ec,
+ 0x137d0, 0x113c8, 0x189e6, 0x137c8, 0x19be6, 0x137c4, 0x113c2, 0x137c2,
+ 0x113ec, 0x137ec, 0x113e6, 0x137e6, 0x1fba8, 0x175f0, 0x1bafc, 0x1fba4,
+ 0x174f8, 0x1ba7e, 0x1fba2, 0x1747c, 0x1743e, 0x1f328, 0x1f996, 0x132fc,
+ 0x1f768, 0x1fbb6, 0x176fc, 0x1327e, 0x1f764, 0x1f322, 0x1767e, 0x1f762,
+ 0x1e228, 0x1f116, 0x1e668, 0x1e224, 0x1eee8, 0x1f776, 0x1e222, 0x1eee4,
+ 0x1e662, 0x1eee2, 0x1c468, 0x1e236, 0x1cce8, 0x1c464, 0x1dde8, 0x1cce4,
+ 0x1c462, 0x1dde4, 0x1cce2, 0x1dde2, 0x188e8, 0x1c476, 0x199e8, 0x188e4,
+ 0x1bbe8, 0x199e4, 0x188e2, 0x1bbe4, 0x199e2, 0x1bbe2, 0x111e8, 0x188f6,
+ 0x133e8, 0x111e4, 0x177e8, 0x133e4, 0x111e2, 0x177e4, 0x133e2, 0x177e2,
+ 0x111f6, 0x133f6, 0x1fb94, 0x172f8, 0x1b97e, 0x1fb92, 0x1727c, 0x1723e,
+ 0x1f314, 0x1317e, 0x1f734, 0x1f312, 0x1737e, 0x1f732, 0x1e214, 0x1e634,
+ 0x1e212, 0x1ee74, 0x1e632, 0x1ee72, 0x1c434, 0x1cc74, 0x1c432, 0x1dcf4,
+ 0x1cc72, 0x1dcf2, 0x18874, 0x198f4, 0x18872, 0x1b9f4, 0x198f2, 0x1b9f2,
+ 0x110f4, 0x131f4, 0x110f2, 0x173f4, 0x131f2, 0x173f2, 0x1fb8a, 0x1717c,
+ 0x1713e, 0x1f30a, 0x1f71a, 0x1e20a, 0x1e61a, 0x1ee3a, 0x1c41a, 0x1cc3a,
+ 0x1dc7a, 0x1883a, 0x1987a, 0x1b8fa, 0x1107a, 0x130fa, 0x171fa, 0x170be,
+ 0x1e150, 0x1f0ac, 0x1e148, 0x1f0a6, 0x1e144, 0x1e142, 0x1c2d0, 0x1e16c,
+ 0x1c2c8, 0x1e166, 0x1c2c4, 0x1c2c2, 0x185d0, 0x1c2ec, 0x185c8, 0x1c2e6,
+ 0x185c4, 0x185c2, 0x10bd0, 0x185ec, 0x10bc8, 0x185e6, 0x10bc4, 0x10bc2,
+ 0x10bec, 0x10be6, 0x1f1a8, 0x1f8d6, 0x11afc, 0x1f1a4, 0x11a7e, 0x1f1a2,
+ 0x1e128, 0x1f096, 0x1e368, 0x1e124, 0x1e364, 0x1e122, 0x1e362, 0x1c268,
+ 0x1e136, 0x1c6e8, 0x1c264, 0x1c6e4, 0x1c262, 0x1c6e2, 0x184e8, 0x1c276,
+ 0x18de8, 0x184e4, 0x18de4, 0x184e2, 0x18de2, 0x109e8, 0x184f6, 0x11be8,
+ 0x109e4, 0x11be4, 0x109e2, 0x11be2, 0x109f6, 0x11bf6, 0x1f9d4, 0x13af8,
+ 0x19d7e, 0x1f9d2, 0x13a7c, 0x13a3e, 0x1f194, 0x1197e, 0x1f3b4, 0x1f192,
+ 0x13b7e, 0x1f3b2, 0x1e114, 0x1e334, 0x1e112, 0x1e774, 0x1e332, 0x1e772,
+ 0x1c234, 0x1c674, 0x1c232, 0x1cef4, 0x1c672, 0x1cef2, 0x18474, 0x18cf4,
+ 0x18472, 0x19df4, 0x18cf2, 0x19df2, 0x108f4, 0x119f4, 0x108f2, 0x13bf4,
+ 0x119f2, 0x13bf2, 0x17af0, 0x1bd7c, 0x17a78, 0x1bd3e, 0x17a3c, 0x17a1e,
+ 0x1f9ca, 0x1397c, 0x1fbda, 0x17b7c, 0x1393e, 0x17b3e, 0x1f18a, 0x1f39a,
+ 0x1f7ba, 0x1e10a, 0x1e31a, 0x1e73a, 0x1ef7a, 0x1c21a, 0x1c63a, 0x1ce7a,
+ 0x1defa, 0x1843a, 0x18c7a, 0x19cfa, 0x1bdfa, 0x1087a, 0x118fa, 0x139fa,
+ 0x17978, 0x1bcbe, 0x1793c, 0x1791e, 0x138be, 0x179be, 0x178bc, 0x1789e,
+ 0x1785e, 0x1e0a8, 0x1e0a4, 0x1e0a2, 0x1c168, 0x1e0b6, 0x1c164, 0x1c162,
+ 0x182e8, 0x1c176, 0x182e4, 0x182e2, 0x105e8, 0x182f6, 0x105e4, 0x105e2,
+ 0x105f6, 0x1f0d4, 0x10d7e, 0x1f0d2, 0x1e094, 0x1e1b4, 0x1e092, 0x1e1b2,
+ 0x1c134, 0x1c374, 0x1c132, 0x1c372, 0x18274, 0x186f4, 0x18272, 0x186f2,
+ 0x104f4, 0x10df4, 0x104f2, 0x10df2, 0x1f8ea, 0x11d7c, 0x11d3e, 0x1f0ca,
+ 0x1f1da, 0x1e08a, 0x1e19a, 0x1e3ba, 0x1c11a, 0x1c33a, 0x1c77a, 0x1823a,
+ 0x1867a, 0x18efa, 0x1047a, 0x10cfa, 0x11dfa, 0x13d78, 0x19ebe, 0x13d3c,
+ 0x13d1e, 0x11cbe, 0x13dbe, 0x17d70, 0x1bebc, 0x17d38, 0x1be9e, 0x17d1c,
+ 0x17d0e, 0x13cbc, 0x17dbc, 0x13c9e, 0x17d9e, 0x17cb8, 0x1be5e, 0x17c9c,
+ 0x17c8e, 0x13c5e, 0x17cde, 0x17c5c, 0x17c4e, 0x17c2e, 0x1c0b4, 0x1c0b2,
+ 0x18174, 0x18172, 0x102f4, 0x102f2, 0x1e0da, 0x1c09a, 0x1c1ba, 0x1813a,
+ 0x1837a, 0x1027a, 0x106fa, 0x10ebe, 0x11ebc, 0x11e9e, 0x13eb8, 0x19f5e,
+ 0x13e9c, 0x13e8e, 0x11e5e, 0x13ede, 0x17eb0, 0x1bf5c, 0x17e98, 0x1bf4e,
+ 0x17e8c, 0x17e86, 0x13e5c, 0x17edc, 0x13e4e, 0x17ece, 0x17e58, 0x1bf2e,
+ 0x17e4c, 0x17e46, 0x13e2e, 0x17e6e, 0x17e2c, 0x17e26, 0x10f5e, 0x11f5c,
+ 0x11f4e, 0x13f58, 0x19fae, 0x13f4c, 0x13f46, 0x11f2e, 0x13f6e, 0x13f2c,
+ 0x13f26
+ }, {
+ 0x1abe0, 0x1d5f8, 0x153c0, 0x1a9f0, 0x1d4fc, 0x151e0, 0x1a8f8, 0x1d47e,
+ 0x150f0, 0x1a87c, 0x15078, 0x1fad0, 0x15be0, 0x1adf8, 0x1fac8, 0x159f0,
+ 0x1acfc, 0x1fac4, 0x158f8, 0x1ac7e, 0x1fac2, 0x1587c, 0x1f5d0, 0x1faec,
+ 0x15df8, 0x1f5c8, 0x1fae6, 0x15cfc, 0x1f5c4, 0x15c7e, 0x1f5c2, 0x1ebd0,
+ 0x1f5ec, 0x1ebc8, 0x1f5e6, 0x1ebc4, 0x1ebc2, 0x1d7d0, 0x1ebec, 0x1d7c8,
+ 0x1ebe6, 0x1d7c4, 0x1d7c2, 0x1afd0, 0x1d7ec, 0x1afc8, 0x1d7e6, 0x1afc4,
+ 0x14bc0, 0x1a5f0, 0x1d2fc, 0x149e0, 0x1a4f8, 0x1d27e, 0x148f0, 0x1a47c,
+ 0x14878, 0x1a43e, 0x1483c, 0x1fa68, 0x14df0, 0x1a6fc, 0x1fa64, 0x14cf8,
+ 0x1a67e, 0x1fa62, 0x14c7c, 0x14c3e, 0x1f4e8, 0x1fa76, 0x14efc, 0x1f4e4,
+ 0x14e7e, 0x1f4e2, 0x1e9e8, 0x1f4f6, 0x1e9e4, 0x1e9e2, 0x1d3e8, 0x1e9f6,
+ 0x1d3e4, 0x1d3e2, 0x1a7e8, 0x1d3f6, 0x1a7e4, 0x1a7e2, 0x145e0, 0x1a2f8,
+ 0x1d17e, 0x144f0, 0x1a27c, 0x14478, 0x1a23e, 0x1443c, 0x1441e, 0x1fa34,
+ 0x146f8, 0x1a37e, 0x1fa32, 0x1467c, 0x1463e, 0x1f474, 0x1477e, 0x1f472,
+ 0x1e8f4, 0x1e8f2, 0x1d1f4, 0x1d1f2, 0x1a3f4, 0x1a3f2, 0x142f0, 0x1a17c,
+ 0x14278, 0x1a13e, 0x1423c, 0x1421e, 0x1fa1a, 0x1437c, 0x1433e, 0x1f43a,
+ 0x1e87a, 0x1d0fa, 0x14178, 0x1a0be, 0x1413c, 0x1411e, 0x141be, 0x140bc,
+ 0x1409e, 0x12bc0, 0x195f0, 0x1cafc, 0x129e0, 0x194f8, 0x1ca7e, 0x128f0,
+ 0x1947c, 0x12878, 0x1943e, 0x1283c, 0x1f968, 0x12df0, 0x196fc, 0x1f964,
+ 0x12cf8, 0x1967e, 0x1f962, 0x12c7c, 0x12c3e, 0x1f2e8, 0x1f976, 0x12efc,
+ 0x1f2e4, 0x12e7e, 0x1f2e2, 0x1e5e8, 0x1f2f6, 0x1e5e4, 0x1e5e2, 0x1cbe8,
+ 0x1e5f6, 0x1cbe4, 0x1cbe2, 0x197e8, 0x1cbf6, 0x197e4, 0x197e2, 0x1b5e0,
+ 0x1daf8, 0x1ed7e, 0x169c0, 0x1b4f0, 0x1da7c, 0x168e0, 0x1b478, 0x1da3e,
+ 0x16870, 0x1b43c, 0x16838, 0x1b41e, 0x1681c, 0x125e0, 0x192f8, 0x1c97e,
+ 0x16de0, 0x124f0, 0x1927c, 0x16cf0, 0x1b67c, 0x1923e, 0x16c78, 0x1243c,
+ 0x16c3c, 0x1241e, 0x16c1e, 0x1f934, 0x126f8, 0x1937e, 0x1fb74, 0x1f932,
+ 0x16ef8, 0x1267c, 0x1fb72, 0x16e7c, 0x1263e, 0x16e3e, 0x1f274, 0x1277e,
+ 0x1f6f4, 0x1f272, 0x16f7e, 0x1f6f2, 0x1e4f4, 0x1edf4, 0x1e4f2, 0x1edf2,
+ 0x1c9f4, 0x1dbf4, 0x1c9f2, 0x1dbf2, 0x193f4, 0x193f2, 0x165c0, 0x1b2f0,
+ 0x1d97c, 0x164e0, 0x1b278, 0x1d93e, 0x16470, 0x1b23c, 0x16438, 0x1b21e,
+ 0x1641c, 0x1640e, 0x122f0, 0x1917c, 0x166f0, 0x12278, 0x1913e, 0x16678,
+ 0x1b33e, 0x1663c, 0x1221e, 0x1661e, 0x1f91a, 0x1237c, 0x1fb3a, 0x1677c,
+ 0x1233e, 0x1673e, 0x1f23a, 0x1f67a, 0x1e47a, 0x1ecfa, 0x1c8fa, 0x1d9fa,
+ 0x191fa, 0x162e0, 0x1b178, 0x1d8be, 0x16270, 0x1b13c, 0x16238, 0x1b11e,
+ 0x1621c, 0x1620e, 0x12178, 0x190be, 0x16378, 0x1213c, 0x1633c, 0x1211e,
+ 0x1631e, 0x121be, 0x163be, 0x16170, 0x1b0bc, 0x16138, 0x1b09e, 0x1611c,
+ 0x1610e, 0x120bc, 0x161bc, 0x1209e, 0x1619e, 0x160b8, 0x1b05e, 0x1609c,
+ 0x1608e, 0x1205e, 0x160de, 0x1605c, 0x1604e, 0x115e0, 0x18af8, 0x1c57e,
+ 0x114f0, 0x18a7c, 0x11478, 0x18a3e, 0x1143c, 0x1141e, 0x1f8b4, 0x116f8,
+ 0x18b7e, 0x1f8b2, 0x1167c, 0x1163e, 0x1f174, 0x1177e, 0x1f172, 0x1e2f4,
+ 0x1e2f2, 0x1c5f4, 0x1c5f2, 0x18bf4, 0x18bf2, 0x135c0, 0x19af0, 0x1cd7c,
+ 0x134e0, 0x19a78, 0x1cd3e, 0x13470, 0x19a3c, 0x13438, 0x19a1e, 0x1341c,
+ 0x1340e, 0x112f0, 0x1897c, 0x136f0, 0x11278, 0x1893e, 0x13678, 0x19b3e,
+ 0x1363c, 0x1121e, 0x1361e, 0x1f89a, 0x1137c, 0x1f9ba, 0x1377c, 0x1133e,
+ 0x1373e, 0x1f13a, 0x1f37a, 0x1e27a, 0x1e6fa, 0x1c4fa, 0x1cdfa, 0x189fa,
+ 0x1bae0, 0x1dd78, 0x1eebe, 0x174c0, 0x1ba70, 0x1dd3c, 0x17460, 0x1ba38,
+ 0x1dd1e, 0x17430, 0x1ba1c, 0x17418, 0x1ba0e, 0x1740c, 0x132e0, 0x19978,
+ 0x1ccbe, 0x176e0, 0x13270, 0x1993c, 0x17670, 0x1bb3c, 0x1991e, 0x17638,
+ 0x1321c, 0x1761c, 0x1320e, 0x1760e, 0x11178, 0x188be, 0x13378, 0x1113c,
+ 0x17778, 0x1333c, 0x1111e, 0x1773c, 0x1331e, 0x1771e, 0x111be, 0x133be,
+ 0x177be, 0x172c0, 0x1b970, 0x1dcbc, 0x17260, 0x1b938, 0x1dc9e, 0x17230,
+ 0x1b91c, 0x17218, 0x1b90e, 0x1720c, 0x17206, 0x13170, 0x198bc, 0x17370,
+ 0x13138, 0x1989e, 0x17338, 0x1b99e, 0x1731c, 0x1310e, 0x1730e, 0x110bc,
+ 0x131bc, 0x1109e, 0x173bc, 0x1319e, 0x1739e, 0x17160, 0x1b8b8, 0x1dc5e,
+ 0x17130, 0x1b89c, 0x17118, 0x1b88e, 0x1710c, 0x17106, 0x130b8, 0x1985e,
+ 0x171b8, 0x1309c, 0x1719c, 0x1308e, 0x1718e, 0x1105e, 0x130de, 0x171de,
+ 0x170b0, 0x1b85c, 0x17098, 0x1b84e, 0x1708c, 0x17086, 0x1305c, 0x170dc,
+ 0x1304e, 0x170ce, 0x17058, 0x1b82e, 0x1704c, 0x17046, 0x1302e, 0x1706e,
+ 0x1702c, 0x17026, 0x10af0, 0x1857c, 0x10a78, 0x1853e, 0x10a3c, 0x10a1e,
+ 0x10b7c, 0x10b3e, 0x1f0ba, 0x1e17a, 0x1c2fa, 0x185fa, 0x11ae0, 0x18d78,
+ 0x1c6be, 0x11a70, 0x18d3c, 0x11a38, 0x18d1e, 0x11a1c, 0x11a0e, 0x10978,
+ 0x184be, 0x11b78, 0x1093c, 0x11b3c, 0x1091e, 0x11b1e, 0x109be, 0x11bbe,
+ 0x13ac0, 0x19d70, 0x1cebc, 0x13a60, 0x19d38, 0x1ce9e, 0x13a30, 0x19d1c,
+ 0x13a18, 0x19d0e, 0x13a0c, 0x13a06, 0x11970, 0x18cbc, 0x13b70, 0x11938,
+ 0x18c9e, 0x13b38, 0x1191c, 0x13b1c, 0x1190e, 0x13b0e, 0x108bc, 0x119bc,
+ 0x1089e, 0x13bbc, 0x1199e, 0x13b9e, 0x1bd60, 0x1deb8, 0x1ef5e, 0x17a40,
+ 0x1bd30, 0x1de9c, 0x17a20, 0x1bd18, 0x1de8e, 0x17a10, 0x1bd0c, 0x17a08,
+ 0x1bd06, 0x17a04, 0x13960, 0x19cb8, 0x1ce5e, 0x17b60, 0x13930, 0x19c9c,
+ 0x17b30, 0x1bd9c, 0x19c8e, 0x17b18, 0x1390c, 0x17b0c, 0x13906, 0x17b06,
+ 0x118b8, 0x18c5e, 0x139b8, 0x1189c, 0x17bb8, 0x1399c, 0x1188e, 0x17b9c,
+ 0x1398e, 0x17b8e, 0x1085e, 0x118de, 0x139de, 0x17bde, 0x17940, 0x1bcb0,
+ 0x1de5c, 0x17920, 0x1bc98, 0x1de4e, 0x17910, 0x1bc8c, 0x17908, 0x1bc86,
+ 0x17904, 0x17902, 0x138b0, 0x19c5c, 0x179b0, 0x13898, 0x19c4e, 0x17998,
+ 0x1bcce, 0x1798c, 0x13886, 0x17986, 0x1185c, 0x138dc, 0x1184e, 0x179dc,
+ 0x138ce, 0x179ce, 0x178a0, 0x1bc58, 0x1de2e, 0x17890, 0x1bc4c, 0x17888,
+ 0x1bc46, 0x17884, 0x17882, 0x13858, 0x19c2e, 0x178d8, 0x1384c, 0x178cc,
+ 0x13846, 0x178c6, 0x1182e, 0x1386e, 0x178ee, 0x17850, 0x1bc2c, 0x17848,
+ 0x1bc26, 0x17844, 0x17842, 0x1382c, 0x1786c, 0x13826, 0x17866, 0x17828,
+ 0x1bc16, 0x17824, 0x17822, 0x13816, 0x17836, 0x10578, 0x182be, 0x1053c,
+ 0x1051e, 0x105be, 0x10d70, 0x186bc, 0x10d38, 0x1869e, 0x10d1c, 0x10d0e,
+ 0x104bc, 0x10dbc, 0x1049e, 0x10d9e, 0x11d60, 0x18eb8, 0x1c75e, 0x11d30,
+ 0x18e9c, 0x11d18, 0x18e8e, 0x11d0c, 0x11d06, 0x10cb8, 0x1865e, 0x11db8,
+ 0x10c9c, 0x11d9c, 0x10c8e, 0x11d8e, 0x1045e, 0x10cde, 0x11dde, 0x13d40,
+ 0x19eb0, 0x1cf5c, 0x13d20, 0x19e98, 0x1cf4e, 0x13d10, 0x19e8c, 0x13d08,
+ 0x19e86, 0x13d04, 0x13d02, 0x11cb0, 0x18e5c, 0x13db0, 0x11c98, 0x18e4e,
+ 0x13d98, 0x19ece, 0x13d8c, 0x11c86, 0x13d86, 0x10c5c, 0x11cdc, 0x10c4e,
+ 0x13ddc, 0x11cce, 0x13dce, 0x1bea0, 0x1df58, 0x1efae, 0x1be90, 0x1df4c,
+ 0x1be88, 0x1df46, 0x1be84, 0x1be82, 0x13ca0, 0x19e58, 0x1cf2e, 0x17da0,
+ 0x13c90, 0x19e4c, 0x17d90, 0x1becc, 0x19e46, 0x17d88, 0x13c84, 0x17d84,
+ 0x13c82, 0x17d82, 0x11c58, 0x18e2e, 0x13cd8, 0x11c4c, 0x17dd8, 0x13ccc,
+ 0x11c46, 0x17dcc, 0x13cc6, 0x17dc6, 0x10c2e, 0x11c6e, 0x13cee, 0x17dee,
+ 0x1be50, 0x1df2c, 0x1be48, 0x1df26, 0x1be44, 0x1be42, 0x13c50, 0x19e2c,
+ 0x17cd0, 0x13c48, 0x19e26, 0x17cc8, 0x1be66, 0x17cc4, 0x13c42, 0x17cc2,
+ 0x11c2c, 0x13c6c, 0x11c26, 0x17cec, 0x13c66, 0x17ce6, 0x1be28, 0x1df16,
+ 0x1be24, 0x1be22, 0x13c28, 0x19e16, 0x17c68, 0x13c24, 0x17c64, 0x13c22,
+ 0x17c62, 0x11c16, 0x13c36, 0x17c76, 0x1be14, 0x1be12, 0x13c14, 0x17c34,
+ 0x13c12, 0x17c32, 0x102bc, 0x1029e, 0x106b8, 0x1835e, 0x1069c, 0x1068e,
+ 0x1025e, 0x106de, 0x10eb0, 0x1875c, 0x10e98, 0x1874e, 0x10e8c, 0x10e86,
+ 0x1065c, 0x10edc, 0x1064e, 0x10ece, 0x11ea0, 0x18f58, 0x1c7ae, 0x11e90,
+ 0x18f4c, 0x11e88, 0x18f46, 0x11e84, 0x11e82, 0x10e58, 0x1872e, 0x11ed8,
+ 0x18f6e, 0x11ecc, 0x10e46, 0x11ec6, 0x1062e, 0x10e6e, 0x11eee, 0x19f50,
+ 0x1cfac, 0x19f48, 0x1cfa6, 0x19f44, 0x19f42, 0x11e50, 0x18f2c, 0x13ed0,
+ 0x19f6c, 0x18f26, 0x13ec8, 0x11e44, 0x13ec4, 0x11e42, 0x13ec2, 0x10e2c,
+ 0x11e6c, 0x10e26, 0x13eec, 0x11e66, 0x13ee6, 0x1dfa8, 0x1efd6, 0x1dfa4,
+ 0x1dfa2, 0x19f28, 0x1cf96, 0x1bf68, 0x19f24, 0x1bf64, 0x19f22, 0x1bf62,
+ 0x11e28, 0x18f16, 0x13e68, 0x11e24, 0x17ee8, 0x13e64, 0x11e22, 0x17ee4,
+ 0x13e62, 0x17ee2, 0x10e16, 0x11e36, 0x13e76, 0x17ef6, 0x1df94, 0x1df92,
+ 0x19f14, 0x1bf34, 0x19f12, 0x1bf32, 0x11e14, 0x13e34, 0x11e12, 0x17e74,
+ 0x13e32, 0x17e72, 0x1df8a, 0x19f0a, 0x1bf1a, 0x11e0a, 0x13e1a, 0x17e3a,
+ 0x1035c, 0x1034e, 0x10758, 0x183ae, 0x1074c, 0x10746, 0x1032e, 0x1076e,
+ 0x10f50, 0x187ac, 0x10f48, 0x187a6, 0x10f44, 0x10f42, 0x1072c, 0x10f6c,
+ 0x10726, 0x10f66, 0x18fa8, 0x1c7d6, 0x18fa4, 0x18fa2, 0x10f28, 0x18796,
+ 0x11f68, 0x18fb6, 0x11f64, 0x10f22, 0x11f62, 0x10716, 0x10f36, 0x11f76,
+ 0x1cfd4, 0x1cfd2, 0x18f94, 0x19fb4, 0x18f92, 0x19fb2, 0x10f14, 0x11f34,
+ 0x10f12, 0x13f74, 0x11f32, 0x13f72, 0x1cfca, 0x18f8a, 0x19f9a, 0x10f0a,
+ 0x11f1a, 0x13f3a, 0x103ac, 0x103a6, 0x107a8, 0x183d6, 0x107a4, 0x107a2,
+ 0x10396, 0x107b6, 0x187d4, 0x187d2, 0x10794, 0x10fb4, 0x10792, 0x10fb2,
+ 0x1c7ea
+ }};
+
+ static int ERROR_LEVEL[][] =
+ {{
+ 27, 917
+ }, {
+ 522, 568, 723, 809
+ }, {
+ 237, 308, 436, 284, 646, 653, 428, 379
+ }, {
+ 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65
+ }, {
+ 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517,
+ 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410
+ }, {
+ 539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612,
+ 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184,
+ 440, 35, 519, 31, 460, 594, 225, 535, 517, 352, 605, 158, 651, 201, 488, 502,
+ 648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543
+ }, {
+ 521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415,
+ 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704,
+ 516, 258, 457, 907, 594, 723, 674, 292, 272, 96, 684, 432, 686, 606, 860, 569,
+ 193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776,
+ 171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898,
+ 784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616,
+ 157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34,
+ 211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539
+ }, {
+ 524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720,
+ 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757,
+ 710, 814, 919, 89, 68, 569, 11, 204, 796, 605, 540, 913, 801, 700, 799, 137,
+ 439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884,
+ 315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521,
+ 307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470,
+ 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90,
+ 2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134,
+ 628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234,
+ 722, 384, 177, 752, 607, 640, 455, 193, 689, 707, 805, 641, 48, 60, 732, 621,
+ 895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528,
+ 503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550,
+ 73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754,
+ 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532,
+ 609, 829, 189, 20, 167, 29, 872, 449, 83, 402, 41, 656, 505, 579, 481, 173,
+ 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10
+ }, {
+ 352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492,
+ 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781,
+ 846, 75, 327, 520, 435, 543, 203, 666, 249, 346, 781, 621, 640, 268, 794, 534,
+ 539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41,
+ 542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741,
+ 290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142,
+ 808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258,
+ 240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303,
+ 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402,
+ 40, 708, 575, 162, 864, 229, 65, 861, 841, 512, 164, 477, 221, 92, 358, 785,
+ 288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543,
+ 152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820,
+ 669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578,
+ 37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911,
+ 283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408,
+ 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729,
+ 624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109, 608, 563, 365, 181, 772,
+ 677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777,
+ 618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45,
+ 787, 680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905,
+ 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341,
+ 242, 797, 838, 837, 720, 224, 307, 631, 61, 87, 560, 310, 756, 665, 397, 808,
+ 851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249,
+ 321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791,
+ 660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437,
+ 375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842,
+ 37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316,
+ 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656,
+ 447, 171, 616, 464, 190, 531, 297, 321, 762, 752, 533, 175, 134, 14, 381, 433,
+ 717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780,
+ 407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647,
+ 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263
+ }};
+
+ /** Holds value of property outBits. */
+ private byte[] outBits;
+
+ /** Holds value of property bitColumns. */
+ private int bitColumns;
+
+ /** Holds value of property codeRows. */
+ private int codeRows;
+
+ /** Holds value of property codeColumns. */
+ private int codeColumns;
+
+ /** Holds value of property codewords. */
+ private int[] codewords = new int[MAX_DATA_CODEWORDS + 2];
+
+ /** Holds value of property lenCodewords. */
+ private int lenCodewords;
+
+ /** Holds value of property errorLevel. */
+ private int errorLevel;
+
+ /** Holds value of property text. */
+ private byte[] text;
+
+ /** Holds value of property options. */
+ private int options;
+
+ /** Holds value of property aspectRatio. */
+ private float aspectRatio;
+
+ /** Holds value of property yHeight. */
+ private float yHeight;
+
+ protected static class Segment {
+ public char type;
+ public int start;
+ public int end;
+
+ public Segment(char type, int start, int end) {
+ this.type = type;
+ this.start = start;
+ this.end = end;
+ }
+ }
+
+ protected static class SegmentList {
+ protected ArrayList list = new ArrayList();
+
+ public void add(char type, int start, int end) {
+ list.add(new Segment(type, start, end));
+ }
+
+ public Segment get(int idx) {
+ if (idx < 0 || idx >= list.size())
+ return null;
+ return (Segment)list.get(idx);
+ }
+
+ public void remove(int idx) {
+ if (idx < 0 || idx >= list.size())
+ return;
+ list.remove(idx);
+ }
+
+ public int size() {
+ return list.size();
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/BarcodePostnet.java b/src/main/java/com/lowagie/text/pdf/BarcodePostnet.java
new file mode 100644
index 0000000..5befa67
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/BarcodePostnet.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+import com.lowagie.text.Rectangle;
+import java.awt.Color;
+import java.awt.Image;
+import java.awt.Canvas;
+import java.awt.image.MemoryImageSource;
+
+/** Implements the Postnet and Planet barcodes. The default parameters are:
+ *
+ *n = 72f / 22f; // distance between bars
+ *x = 0.02f * 72f; // bar width
+ *barHeight = 0.125f * 72f; // height of the tall bars
+ *size = 0.05f * 72f; // height of the short bars
+ *codeType = POSTNET; // type of code
+ *
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class BarcodePostnet extends Barcode{
+
+ /** The bars for each character.
+ */
+ static byte BARS[][] =
+ {
+ {1,1,0,0,0},
+ {0,0,0,1,1},
+ {0,0,1,0,1},
+ {0,0,1,1,0},
+ {0,1,0,0,1},
+ {0,1,0,1,0},
+ {0,1,1,0,0},
+ {1,0,0,0,1},
+ {1,0,0,1,0},
+ {1,0,1,0,0}
+ };
+
+ /** Creates new BarcodePostnet */
+ public BarcodePostnet() {
+ n = 72f / 22f; // distance between bars
+ x = 0.02f * 72f; // bar width
+ barHeight = 0.125f * 72f; // height of the tall bars
+ size = 0.05f * 72f; // height of the short bars
+ codeType = POSTNET; // type of code
+ }
+
+ /** Creates the bars for Postnet.
+ * @param text the code to be created without checksum
+ * @return the bars
+ */
+ public static byte[] getBarsPostnet(String text) {
+ int total = 0;
+ for (int k = text.length() - 1; k >= 0; --k) {
+ int n = text.charAt(k) - '0';
+ total += n;
+ }
+ text += (char)(((10 - (total % 10)) % 10) + '0');
+ byte bars[] = new byte[text.length() * 5 + 2];
+ bars[0] = 1;
+ bars[bars.length - 1] = 1;
+ for (int k = 0; k < text.length(); ++k) {
+ int c = text.charAt(k) - '0';
+ System.arraycopy(BARS[c], 0, bars, k * 5 + 1, 5);
+ }
+ return bars;
+ }
+
+ /** Gets the maximum area that the barcode and the text, if
+ * any, will occupy. The lower left corner is always (0, 0).
+ * @return the size the barcode occupies.
+ */
+ public Rectangle getBarcodeSize() {
+ float width = ((code.length() + 1) * 5 + 1) * n + x;
+ return new Rectangle(width, barHeight);
+ }
+
+ /** Places the barcode in a PdfContentByte
. The
+ * barcode is always placed at coodinates (0, 0). Use the
+ * translation matrix to move it elsewhere.
+ *
+ * @param cb the
+ *
+ *
+ * barColor
+ * textColor
+ *
+ *
+ *
+ * null
+ * null
+ *
+ *
+ *
+ * barColor
+ * null
+ * barColor
+ *
+ *
+ * null
+ * textColor
+ *
text painted with textColor
+ *
+ *
+ * barColor
+ * textColor
+ * barColor
text painted with textColor
PdfContentByte
where the barcode will be placed
+ * @param barColor the color of the bars. It can be null
+ * @param textColor the color of the text. It can be null
+ * @return the dimensions the barcode occupies
+ */
+ public Rectangle placeBarcode(PdfContentByte cb, Color barColor, Color textColor) {
+ if (barColor != null)
+ cb.setColorFill(barColor);
+ byte bars[] = getBarsPostnet(code);
+ byte flip = 1;
+ if (codeType == PLANET) {
+ flip = 0;
+ bars[0] = 0;
+ bars[bars.length - 1] = 0;
+ }
+ float startX = 0;
+ for (int k = 0; k < bars.length; ++k) {
+ cb.rectangle(startX, 0, x - inkSpreading, bars[k] == flip ? barHeight : size);
+ startX += n;
+ }
+ cb.fill();
+ return getBarcodeSize();
+ }
+
+ /** Creates a java.awt.Image
. This image only
+ * contains the bars without any text.
+ * @param foreground the color of the bars
+ * @param background the color of the background
+ * @return the image
+ *
+ */
+ public java.awt.Image createAwtImage(Color foreground, Color background) {
+ int f = foreground.getRGB();
+ int g = background.getRGB();
+ Canvas canvas = new Canvas();
+ int barWidth = (int)x;
+ if (barWidth <= 0)
+ barWidth = 1;
+ int barDistance = (int)n;
+ if (barDistance <= barWidth)
+ barDistance = barWidth + 1;
+ int barShort = (int)size;
+ if (barShort <= 0)
+ barShort = 1;
+ int barTall = (int)barHeight;
+ if (barTall <= barShort)
+ barTall = barShort + 1;
+ int width = ((code.length() + 1) * 5 + 1) * barDistance + barWidth;
+ int pix[] = new int[width * barTall];
+ byte bars[] = getBarsPostnet(code);
+ byte flip = 1;
+ if (codeType == PLANET) {
+ flip = 0;
+ bars[0] = 0;
+ bars[bars.length - 1] = 0;
+ }
+ int idx = 0;
+ for (int k = 0; k < bars.length; ++k) {
+ boolean dot = (bars[k] == flip);
+ for (int j = 0; j < barDistance; ++j) {
+ pix[idx + j] = ((dot && j < barWidth) ? f : g);
+ }
+ idx += barDistance;
+ }
+ int limit = width * (barTall - barShort);
+ for (int k = width; k < limit; k += width)
+ System.arraycopy(pix, 0, pix, k, width);
+ idx = limit;
+ for (int k = 0; k < bars.length; ++k) {
+ for (int j = 0; j < barDistance; ++j) {
+ pix[idx + j] = ((j < barWidth) ? f : g);
+ }
+ idx += barDistance;
+ }
+ for (int k = limit + width; k < pix.length; k += width)
+ System.arraycopy(pix, limit, pix, k, width);
+ Image img = canvas.createImage(new MemoryImageSource(width, barTall, pix, 0, width));
+
+ return img;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/BaseField.java b/src/main/java/com/lowagie/text/pdf/BaseField.java
new file mode 100644
index 0000000..8817bbd
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/BaseField.java
@@ -0,0 +1,665 @@
+/*
+ * Copyright 2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.awt.Color;
+import com.lowagie.text.Element;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Rectangle;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashMap;
+
+/** Common field variables.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public abstract class BaseField {
+
+ /** A thin border with 1 point width. */
+ public static final float BORDER_WIDTH_THIN = 1;
+ /** A medium border with 2 point width. */
+ public static final float BORDER_WIDTH_MEDIUM = 2;
+ /** A thick border with 3 point width. */
+ public static final float BORDER_WIDTH_THICK = 3;
+ /** The field is visible. */
+ public static final int VISIBLE = 0;
+ /** The field is hidden. */
+ public static final int HIDDEN = 1;
+ /** The field is visible but does not print. */
+ public static final int VISIBLE_BUT_DOES_NOT_PRINT = 2;
+ /** The field is hidden but is printable. */
+ public static final int HIDDEN_BUT_PRINTABLE = 3;
+ /** The user may not change the value of the field. */
+ public static final int READ_ONLY = 1;
+ /** The field must have a value at the time it is exported by a submit-form
+ * action.
+ */
+ public static final int REQUIRED = 2;
+ /** The field may contain multiple lines of text.
+ * This flag is only meaningful with text fields.
+ */
+ public static final int MULTILINE = 4;
+ /** The field will not scroll (horizontally for single-line
+ * fields, vertically for multiple-line fields) to accommodate more text
+ * than will fit within its annotation rectangle. Once the field is full, no
+ * further text will be accepted.
+ */
+ public static final int DO_NOT_SCROLL = 8;
+ /** The field is intended for entering a secure password that should
+ * not be echoed visibly to the screen.
+ */
+ public static final int PASSWORD = 16;
+ /** The text entered in the field represents the pathname of
+ * a file whose contents are to be submitted as the value of the field.
+ */
+ public static final int FILE_SELECTION = 32;
+ /** The text entered in the field will not be spell-checked.
+ * This flag is meaningful only in text fields and in combo
+ * fields with the EDIT
flag set.
+ */
+ public static final int DO_NOT_SPELL_CHECK = 64;
+ /** If set the combo box includes an editable text box as well as a drop list; if
+ * clear, it includes only a drop list.
+ * This flag is only meaningful with combo fields.
+ */
+ public static final int EDIT = 128;
+
+ /**
+ * combo box flag.
+ */
+ public static final int COMB = 256;
+
+ protected float borderWidth = BORDER_WIDTH_THIN;
+ protected int borderStyle = PdfBorderDictionary.STYLE_SOLID;
+ protected Color borderColor;
+ protected Color backgroundColor;
+ protected Color textColor;
+ protected BaseFont font;
+ protected float fontSize = 0;
+ protected int alignment = Element.ALIGN_LEFT;
+ protected PdfWriter writer;
+ protected String text;
+ protected Rectangle box;
+
+ /** Holds value of property rotation. */
+ protected int rotation = 0;
+
+ /** Holds value of property visibility. */
+ protected int visibility;
+
+ /** Holds value of property fieldName. */
+ protected String fieldName;
+
+ /** Holds value of property options. */
+ protected int options;
+
+ /** Holds value of property maxCharacterLength. */
+ protected int maxCharacterLength;
+
+ private final static HashMap fieldKeys = new HashMap();
+
+ static {
+ fieldKeys.putAll(PdfCopyFieldsImp.fieldKeys);
+ fieldKeys.put(PdfName.T, new Integer(1));
+ }
+ /** Creates a new TextField
.
+ * @param writer the document PdfWriter
+ * @param box the field location and dimensions
+ * @param fieldName the field name. If null
only the widget keys
+ * will be included in the field allowing it to be used as a kid field.
+ */
+ public BaseField(PdfWriter writer, Rectangle box, String fieldName) {
+ this.writer = writer;
+ this.box = box;
+ this.fieldName = fieldName;
+ }
+
+ protected BaseFont getRealFont() throws IOException, DocumentException {
+ if (font == null)
+ return BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, false);
+ else
+ return font;
+ }
+
+ protected PdfAppearance getBorderAppearance() throws IOException, DocumentException {
+ PdfAppearance app = new PdfContentByte(writer).createAppearance(box.width(), box.height());
+ switch (rotation) {
+ case 90:
+ app.setMatrix(0, 1, -1, 0, box.height(), 0);
+ break;
+ case 180:
+ app.setMatrix(-1, 0, 0, -1, box.width(), box.height());
+ break;
+ case 270:
+ app.setMatrix(0, -1, 1, 0, 0, box.width());
+ break;
+ }
+ // background
+ if (backgroundColor != null) {
+ app.setColorFill(backgroundColor);
+ app.rectangle(0, 0, box.width(), box.height());
+ app.fill();
+ }
+ // border
+ if (borderStyle == PdfBorderDictionary.STYLE_UNDERLINE) {
+ if (borderWidth != 0 && borderColor != null) {
+ app.setColorStroke(borderColor);
+ app.setLineWidth(borderWidth);
+ app.moveTo(0, borderWidth / 2);
+ app.lineTo(box.width(), borderWidth / 2);
+ app.stroke();
+ }
+ }
+ else if (borderStyle == PdfBorderDictionary.STYLE_BEVELED) {
+ if (borderWidth != 0 && borderColor != null) {
+ app.setColorStroke(borderColor);
+ app.setLineWidth(borderWidth);
+ app.rectangle(borderWidth / 2, borderWidth / 2, box.width() - borderWidth, box.height() - borderWidth);
+ app.stroke();
+ }
+ // beveled
+ Color actual = backgroundColor;
+ if (actual == null)
+ actual = Color.white;
+ app.setGrayFill(1);
+ drawTopFrame(app);
+ app.setColorFill(actual.darker());
+ drawBottomFrame(app);
+ }
+ else if (borderStyle == PdfBorderDictionary.STYLE_INSET) {
+ if (borderWidth != 0 && borderColor != null) {
+ app.setColorStroke(borderColor);
+ app.setLineWidth(borderWidth);
+ app.rectangle(borderWidth / 2, borderWidth / 2, box.width() - borderWidth, box.height() - borderWidth);
+ app.stroke();
+ }
+ // inset
+ app.setGrayFill(0.5f);
+ drawTopFrame(app);
+ app.setGrayFill(0.75f);
+ drawBottomFrame(app);
+ }
+ else {
+ if (borderWidth != 0 && borderColor != null) {
+ if (borderStyle == PdfBorderDictionary.STYLE_DASHED)
+ app.setLineDash(3, 0);
+ app.setColorStroke(borderColor);
+ app.setLineWidth(borderWidth);
+ app.rectangle(borderWidth / 2, borderWidth / 2, box.width() - borderWidth, box.height() - borderWidth);
+ app.stroke();
+ if ((options & COMB) != 0 && maxCharacterLength > 1) {
+ float step = box.width() / maxCharacterLength;
+ float yb = borderWidth / 2;
+ float yt = box.height() - borderWidth / 2;
+ for (int k = 1; k < maxCharacterLength; ++k) {
+ float x = step * k;
+ app.moveTo(x, yb);
+ app.lineTo(x, yt);
+ }
+ app.stroke();
+ }
+ }
+ }
+ return app;
+ }
+
+ protected static ArrayList getHardBreaks(String text) {
+ ArrayList arr = new ArrayList();
+ char cs[] = text.toCharArray();
+ int len = cs.length;
+ StringBuffer buf = new StringBuffer();
+ for (int k = 0; k < len; ++k) {
+ char c = cs[k];
+ if (c == '\r') {
+ if (k + 1 < len && cs[k + 1] == '\n')
+ ++k;
+ arr.add(buf.toString());
+ buf = new StringBuffer();
+ }
+ else if (c == '\n') {
+ arr.add(buf.toString());
+ buf = new StringBuffer();
+ }
+ else
+ buf.append(c);
+ }
+ arr.add(buf.toString());
+ return arr;
+ }
+
+ protected static void trimRight(StringBuffer buf) {
+ int len = buf.length();
+ while (true) {
+ if (len == 0)
+ return;
+ if (buf.charAt(--len) != ' ')
+ return;
+ buf.setLength(len);
+ }
+ }
+
+ protected static ArrayList breakLines(ArrayList breaks, BaseFont font, float fontSize, float width) {
+ ArrayList lines = new ArrayList();
+ StringBuffer buf = new StringBuffer();
+ for (int ck = 0; ck < breaks.size(); ++ck) {
+ buf.setLength(0);
+ float w = 0;
+ char cs[] = ((String)breaks.get(ck)).toCharArray();
+ int len = cs.length;
+ // 0 inline first, 1 inline, 2 spaces
+ int state = 0;
+ int lastspace = -1;
+ char c = 0;
+ int refk = 0;
+ for (int k = 0; k < len; ++k) {
+ c = cs[k];
+ switch (state) {
+ case 0:
+ w += font.getWidthPoint(c, fontSize);
+ buf.append(c);
+ if (w > width) {
+ w = 0;
+ if (buf.length() > 1) {
+ --k;
+ buf.setLength(buf.length() - 1);
+ }
+ lines.add(buf.toString());
+ buf.setLength(0);
+ refk = k;
+ if (c == ' ')
+ state = 2;
+ else
+ state = 1;
+ }
+ else {
+ if (c != ' ')
+ state = 1;
+ }
+ break;
+ case 1:
+ w += font.getWidthPoint(c, fontSize);
+ buf.append(c);
+ if (c == ' ')
+ lastspace = k;
+ if (w > width) {
+ w = 0;
+ if (lastspace >= 0) {
+ k = lastspace;
+ buf.setLength(lastspace - refk);
+ trimRight(buf);
+ lines.add(buf.toString());
+ buf.setLength(0);
+ refk = k;
+ lastspace = -1;
+ state = 2;
+ }
+ else {
+ if (buf.length() > 1) {
+ --k;
+ buf.setLength(buf.length() - 1);
+ }
+ lines.add(buf.toString());
+ buf.setLength(0);
+ refk = k;
+ if (c == ' ')
+ state = 2;
+ }
+ }
+ break;
+ case 2:
+ if (c != ' ') {
+ w = 0;
+ --k;
+ state = 1;
+ }
+ break;
+ }
+ }
+ trimRight(buf);
+ lines.add(buf.toString());
+ }
+ return lines;
+ }
+
+ private void drawTopFrame(PdfAppearance app) {
+ app.moveTo(borderWidth, borderWidth);
+ app.lineTo(borderWidth, box.height() - borderWidth);
+ app.lineTo(box.width() - borderWidth, box.height() - borderWidth);
+ app.lineTo(box.width() - 2 * borderWidth, box.height() - 2 * borderWidth);
+ app.lineTo(2 * borderWidth, box.height() - 2 * borderWidth);
+ app.lineTo(2 * borderWidth, 2 * borderWidth);
+ app.lineTo(borderWidth, borderWidth);
+ app.fill();
+ }
+
+ private void drawBottomFrame(PdfAppearance app) {
+ app.moveTo(borderWidth, borderWidth);
+ app.lineTo(box.width() - borderWidth, borderWidth);
+ app.lineTo(box.width() - borderWidth, box.height() - borderWidth);
+ app.lineTo(box.width() - 2 * borderWidth, box.height() - 2 * borderWidth);
+ app.lineTo(box.width() - 2 * borderWidth, 2 * borderWidth);
+ app.lineTo(2 * borderWidth, 2 * borderWidth);
+ app.lineTo(borderWidth, borderWidth);
+ app.fill();
+ }
+ /** Gets the border width in points.
+ * @return the border width in points
+ */
+ public float getBorderWidth() {
+ return this.borderWidth;
+ }
+
+ /** Sets the border width in points. To eliminate the border
+ * set the border color to null
.
+ * @param borderWidth the border width in points
+ */
+ public void setBorderWidth(float borderWidth) {
+ this.borderWidth = borderWidth;
+ }
+
+ /** Gets the border style.
+ * @return the border style
+ */
+ public int getBorderStyle() {
+ return this.borderStyle;
+ }
+
+ /** Sets the border style. The styles are found in PdfBorderDictionary
+ * and can be STYLE_SOLID
, STYLE_DASHED
,
+ * STYLE_BEVELED
, STYLE_INSET
and
+ * STYLE_UNDERLINE
.
+ * @param borderStyle the border style
+ */
+ public void setBorderStyle(int borderStyle) {
+ this.borderStyle = borderStyle;
+ }
+
+ /** Gets the border color.
+ * @return the border color
+ */
+ public Color getBorderColor() {
+ return this.borderColor;
+ }
+
+ /** Sets the border color. Set to null
to remove
+ * the border.
+ * @param borderColor the border color
+ */
+ public void setBorderColor(Color borderColor) {
+ this.borderColor = borderColor;
+ }
+
+ /** Gets the background color.
+ * @return the background color
+ */
+ public Color getBackgroundColor() {
+ return this.backgroundColor;
+ }
+
+ /** Sets the background color. Set to null
for
+ * transparent background.
+ * @param backgroundColor the background color
+ */
+ public void setBackgroundColor(Color backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ }
+
+ /** Gets the text color.
+ * @return the text color
+ */
+ public Color getTextColor() {
+ return this.textColor;
+ }
+
+ /** Sets the text color. If null
the color used
+ * will be black.
+ * @param textColor the text color
+ */
+ public void setTextColor(Color textColor) {
+ this.textColor = textColor;
+ }
+
+ /** Gets the text font.
+ * @return the text font
+ */
+ public BaseFont getFont() {
+ return this.font;
+ }
+
+ /** Sets the text font. If null
then Helvetica
+ * will be used.
+ * @param font the text font
+ */
+ public void setFont(BaseFont font) {
+ this.font = font;
+ }
+
+ /** Gets the font size.
+ * @return the font size
+ */
+ public float getFontSize() {
+ return this.fontSize;
+ }
+
+ /** Sets the font size. If 0 then auto-sizing will be used but
+ * only for text fields.
+ * @param fontSize the font size
+ */
+ public void setFontSize(float fontSize) {
+ this.fontSize = fontSize;
+ }
+
+ /** Gets the text horizontal alignment.
+ * @return the text horizontal alignment
+ */
+ public int getAlignment() {
+ return this.alignment;
+ }
+
+ /** Sets the text horizontal alignment. It can be Element.ALIGN_LEFT
,
+ * Element.ALIGN_CENTER
and Element.ALIGN_RIGHT
.
+ * @param alignment the text horizontal alignment
+ */
+ public void setAlignment(int alignment) {
+ this.alignment = alignment;
+ }
+
+ /** Gets the text.
+ * @return the text
+ */
+ public String getText() {
+ return this.text;
+ }
+
+ /** Sets the text for text fields.
+ * @param text the text
+ */
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ /** Gets the field dimension and position.
+ * @return the field dimension and position
+ */
+ public Rectangle getBox() {
+ return this.box;
+ }
+
+ /** Sets the field dimension and position.
+ * @param box the field dimension and position
+ */
+ public void setBox(Rectangle box) {
+ this.box = box;
+ }
+
+ /** Gets the field rotation.
+ * @return the field rotation
+ */
+ public int getRotation() {
+ return this.rotation;
+ }
+
+ /** Sets the field rotation. This value should be the same as
+ * the page rotation where the field will be shown.
+ * @param rotation the field rotation
+ */
+ public void setRotation(int rotation) {
+ if (rotation % 90 != 0)
+ throw new IllegalArgumentException("Rotation must be a multiple of 90.");
+ rotation %= 360;
+ if (rotation < 0)
+ rotation += 360;
+ this.rotation = rotation;
+ }
+
+ /** Convenience method to set the field rotation the same as the
+ * page rotation.
+ * @param page the page
+ */
+ public void setRotationFromPage(Rectangle page) {
+ setRotation(page.getRotation());
+ }
+
+ /** Gets the field visibility flag.
+ * @return the field visibility flag
+ */
+ public int getVisibility() {
+ return this.visibility;
+ }
+
+ /** Sets the field visibility flag. This flags can be one of
+ * VISIBLE
, HIDDEN
, VISIBLE_BUT_DOES_NOT_PRINT
+ * and HIDDEN_BUT_PRINTABLE
.
+ * @param visibility field visibility flag
+ */
+ public void setVisibility(int visibility) {
+ this.visibility = visibility;
+ }
+
+ /** Gets the field name.
+ * @return the field name
+ */
+ public String getFieldName() {
+ return this.fieldName;
+ }
+
+ /** Sets the field name.
+ * @param fieldName the field name. If null
only the widget keys
+ * will be included in the field allowing it to be used as a kid field.
+ */
+ public void setFieldName(String fieldName) {
+ this.fieldName = fieldName;
+ }
+
+ /** Gets the option flags.
+ * @return the option flags
+ */
+ public int getOptions() {
+ return this.options;
+ }
+
+ /** Sets the option flags. The option flags can be a combination by oring of
+ * READ_ONLY
, REQUIRED
,
+ * MULTILINE
, DO_NOT_SCROLL
,
+ * PASSWORD
, FILE_SELECTION
,
+ * DO_NOT_SPELL_CHECK
and EDIT
.
+ * @param options the option flags
+ */
+ public void setOptions(int options) {
+ this.options = options;
+ }
+
+ /** Gets the maximum length of the field’s text, in characters.
+ * @return the maximum length of the field’s text, in characters.
+ */
+ public int getMaxCharacterLength() {
+ return this.maxCharacterLength;
+ }
+
+ /** Sets the maximum length of the field’s text, in characters.
+ * It is only meaningful for text fields.
+ * @param maxCharacterLength the maximum length of the field’s text, in characters
+ */
+ public void setMaxCharacterLength(int maxCharacterLength) {
+ this.maxCharacterLength = maxCharacterLength;
+ }
+
+ /**
+ * Getter for property writer.
+ * @return Value of property writer.
+ */
+ public PdfWriter getWriter() {
+ return writer;
+ }
+
+ /**
+ * Setter for property writer.
+ * @param writer New value of property writer.
+ */
+ public void setWriter(PdfWriter writer) {
+ this.writer = writer;
+ }
+
+ /**
+ * Moves the field keys from from
to to
. The moved keys
+ * are removed from from
.
+ * @param from the source
+ * @param to the destination. It may be null
+ */
+ public static void moveFields(PdfDictionary from, PdfDictionary to) {
+ for (Iterator i = from.getKeys().iterator(); i.hasNext();) {
+ PdfName key = (PdfName)i.next();
+ if (fieldKeys.containsKey(key)) {
+ if (to != null)
+ to.put(key, from.get(key));
+ i.remove();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/BaseFont.java b/src/main/java/com/lowagie/text/pdf/BaseFont.java
new file mode 100644
index 0000000..a393e38
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/BaseFont.java
@@ -0,0 +1,1137 @@
+/*
+ * $Id: BaseFont.java,v 1.68 2006/02/23 16:45:48 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2000, 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import java.io.*;
+import com.lowagie.text.DocumentException;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Base class for the several font types supported
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+
+public abstract class BaseFont {
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String COURIER = "Courier";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String COURIER_BOLD = "Courier-Bold";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String COURIER_OBLIQUE = "Courier-Oblique";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String COURIER_BOLDOBLIQUE = "Courier-BoldOblique";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String HELVETICA = "Helvetica";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String HELVETICA_BOLD = "Helvetica-Bold";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String HELVETICA_OBLIQUE = "Helvetica-Oblique";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String HELVETICA_BOLDOBLIQUE = "Helvetica-BoldOblique";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String SYMBOL = "Symbol";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String TIMES_ROMAN = "Times-Roman";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String TIMES_BOLD = "Times-Bold";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String TIMES_ITALIC = "Times-Italic";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String TIMES_BOLDITALIC = "Times-BoldItalic";
+
+ /** This is a possible value of a base 14 type 1 font */
+ public static final String ZAPFDINGBATS = "ZapfDingbats";
+
+ /** The maximum height above the baseline reached by glyphs in this
+ * font, excluding the height of glyphs for accented characters.
+ */
+ public static final int ASCENT = 1;
+ /** The y coordinate of the top of flat capital letters, measured from
+ * the baseline.
+ */
+ public static final int CAPHEIGHT = 2;
+ /** The maximum depth below the baseline reached by glyphs in this
+ * font. The value is a negative number.
+ */
+ public static final int DESCENT = 3;
+ /** The angle, expressed in degrees counterclockwise from the vertical,
+ * of the dominant vertical strokes of the font. The value is
+ * negative for fonts that slope to the right, as almost all italic fonts do.
+ */
+ public static final int ITALICANGLE = 4;
+ /** The lower left x glyph coordinate.
+ */
+ public static final int BBOXLLX = 5;
+ /** The lower left y glyph coordinate.
+ */
+ public static final int BBOXLLY = 6;
+ /** The upper right x glyph coordinate.
+ */
+ public static final int BBOXURX = 7;
+ /** The upper right y glyph coordinate.
+ */
+ public static final int BBOXURY = 8;
+
+ /** java.awt.Font property */
+ public static final int AWT_ASCENT = 9;
+ /** java.awt.Font property */
+ public static final int AWT_DESCENT = 10;
+ /** java.awt.Font property */
+ public static final int AWT_LEADING = 11;
+ /** java.awt.Font property */
+ public static final int AWT_MAXADVANCE = 12;
+
+ /** The font is Type 1.
+ */
+ public static final int FONT_TYPE_T1 = 0;
+ /** The font is True Type with a standard encoding.
+ */
+ public static final int FONT_TYPE_TT = 1;
+ /** The font is CJK.
+ */
+ public static final int FONT_TYPE_CJK = 2;
+ /** The font is True Type with a Unicode encoding.
+ */
+ public static final int FONT_TYPE_TTUNI = 3;
+ /** A font already inside the document.
+ */
+ public static final int FONT_TYPE_DOCUMENT = 4;
+ /** A Type3 font.
+ */
+ public static final int FONT_TYPE_T3 = 5;
+ /** The Unicode encoding with horizontal writing.
+ */
+ public static final String IDENTITY_H = "Identity-H";
+ /** The Unicode encoding with vertical writing.
+ */
+ public static final String IDENTITY_V = "Identity-V";
+
+ /** A possible encoding. */
+ public static final String CP1250 = "Cp1250";
+
+ /** A possible encoding. */
+ public static final String CP1252 = "Cp1252";
+
+ /** A possible encoding. */
+ public static final String CP1257 = "Cp1257";
+
+ /** A possible encoding. */
+ public static final String WINANSI = "Cp1252";
+
+ /** A possible encoding. */
+ public static final String MACROMAN = "MacRoman";
+
+ public static final int[] CHAR_RANGE_LATIN = {0, 0x17f, 0x2000, 0x206f, 0x20a0, 0x20cf, 0xfb00, 0xfb06};
+ public static final int[] CHAR_RANGE_ARABIC = {0, 0x7f, 0x0600, 0x067f, 0x20a0, 0x20cf, 0xfb50, 0xfbff, 0xfe70, 0xfeff};
+ public static final int[] CHAR_RANGE_HEBREW = {0, 0x7f, 0x0590, 0x05ff, 0x20a0, 0x20cf, 0xfb1d, 0xfb4f};
+ public static final int[] CHAR_RANGE_CYRILLIC = {0, 0x7f, 0x0400, 0x052f, 0x2000, 0x206f, 0x20a0, 0x20cf};
+
+/** if the font has to be embedded */
+ public static final boolean EMBEDDED = true;
+
+/** if the font doesn't have to be embedded */
+ public static final boolean NOT_EMBEDDED = false;
+/** if the font has to be cached */
+ public static final boolean CACHED = true;
+/** if the font doesn't have to be cached */
+ public static final boolean NOT_CACHED = false;
+
+ /** The path to the font resources. */
+ public static final String RESOURCE_PATH = "com/lowagie/text/pdf/fonts/";
+ /** The fake CID code that represents a newline. */
+ public static final char CID_NEWLINE = '\u7fff';
+
+ protected ArrayList subsetRanges;
+ /** The font type.
+ */
+ int fontType;
+/** a not defined character in a custom PDF encoding */
+ public static final String notdef = ".notdef";
+
+/** table of characters widths for this encoding */
+ protected int widths[] = new int[256];
+
+/** encoding names */
+ protected String differences[] = new String[256];
+/** same as differences but with the unicode codes */
+ protected char unicodeDifferences[] = new char[256];
+
+ protected int charBBoxes[][] = new int[256][];
+/** encoding used with this font */
+ protected String encoding;
+
+/** true if the font is to be embedded in the PDF */
+ protected boolean embedded;
+
+/**
+ * true if the font must use it's built in encoding. In that case the
+ * encoding
is only used to map a char to the position inside
+ * the font, not to the expected char name.
+ */
+ protected boolean fontSpecific = true;
+
+/** cache for the fonts already used. */
+ protected static HashMap fontCache = new HashMap();
+
+/** list of the 14 built in fonts. */
+ protected static final HashMap BuiltinFonts14 = new HashMap();
+
+ /** Forces the output of the width array. Only matters for the 14
+ * built-in fonts.
+ */
+ protected boolean forceWidthsOutput = false;
+
+ /** Converts char
directly to byte
+ * by casting.
+ */
+ protected boolean directTextToByte = false;
+
+ /** Indicates if all the glyphs and widths for that particular
+ * encoding should be included in the document.
+ */
+ protected boolean subset = true;
+
+ protected boolean fastWinansi = false;
+
+ static {
+ BuiltinFonts14.put(COURIER, PdfName.COURIER);
+ BuiltinFonts14.put(COURIER_BOLD, PdfName.COURIER_BOLD);
+ BuiltinFonts14.put(COURIER_BOLDOBLIQUE, PdfName.COURIER_BOLDOBLIQUE);
+ BuiltinFonts14.put(COURIER_OBLIQUE, PdfName.COURIER_OBLIQUE);
+ BuiltinFonts14.put(HELVETICA, PdfName.HELVETICA);
+ BuiltinFonts14.put(HELVETICA_BOLD, PdfName.HELVETICA_BOLD);
+ BuiltinFonts14.put(HELVETICA_BOLDOBLIQUE, PdfName.HELVETICA_BOLDOBLIQUE);
+ BuiltinFonts14.put(HELVETICA_OBLIQUE, PdfName.HELVETICA_OBLIQUE);
+ BuiltinFonts14.put(SYMBOL, PdfName.SYMBOL);
+ BuiltinFonts14.put(TIMES_ROMAN, PdfName.TIMES_ROMAN);
+ BuiltinFonts14.put(TIMES_BOLD, PdfName.TIMES_BOLD);
+ BuiltinFonts14.put(TIMES_BOLDITALIC, PdfName.TIMES_BOLDITALIC);
+ BuiltinFonts14.put(TIMES_ITALIC, PdfName.TIMES_ITALIC);
+ BuiltinFonts14.put(ZAPFDINGBATS, PdfName.ZAPFDINGBATS);
+ }
+
+ /** Generates the PDF stream with the Type1 and Truetype fonts returning
+ * a PdfStream.
+ */
+ static class StreamFont extends PdfStream {
+
+ /** Generates the PDF stream with the Type1 and Truetype fonts returning
+ * a PdfStream.
+ * @param contents the content of the stream
+ * @param lengths an array of int that describes the several lengths of each part of the font
+ * @throws DocumentException error in the stream compression
+ */
+ public StreamFont(byte contents[], int lengths[]) throws DocumentException {
+ try {
+ bytes = contents;
+ put(PdfName.LENGTH, new PdfNumber(bytes.length));
+ for (int k = 0; k < lengths.length; ++k) {
+ put(new PdfName("Length" + (k + 1)), new PdfNumber(lengths[k]));
+ }
+ flateCompress();
+ }
+ catch (Exception e) {
+ throw new DocumentException(e);
+ }
+ }
+
+ /**
+ * Generates the PDF stream for a font.
+ * @param contents the content of a stream
+ * @param subType the subtype of the font.
+ * @throws DocumentException
+ */
+ public StreamFont(byte contents[], String subType) throws DocumentException {
+ try {
+ bytes = contents;
+ put(PdfName.LENGTH, new PdfNumber(bytes.length));
+ if (subType != null)
+ put(PdfName.SUBTYPE, new PdfName(subType));
+ flateCompress();
+ }
+ catch (Exception e) {
+ throw new DocumentException(e);
+ }
+ }
+ }
+
+ /**
+ *Creates new BaseFont
+ */
+ protected BaseFont() {
+ }
+
+ /** Creates a new font. This font can be one of the 14 built in types,
+ * a Type1 font referred by an AFM file, a TrueType font (simple or collection) or a CJK font from the
+ * Adobe Asian Font Pack. TrueType fonts and CJK fonts can have an optional style modifier
+ * appended to the name. These modifiers are: Bold, Italic and BoldItalic. An
+ * example would be "STSong-Light,Bold". Note that this modifiers do not work if
+ * the font is embedded. Fonts in TrueType collections are addressed by index such as "msgothic.ttc,1".
+ * This would get the second font (indexes start at 0), in this case "MS PGothic".
+ *
+ *
+ * createFont(name, encoding, embedded, true, null, null);
+ *
+ * @param name the name of the font or it's location on file
+ * @param encoding the encoding to be applied to this font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @return returns a new font. This font may come from the cache
+ * @throws DocumentException the font is invalid
+ * @throws IOException the font file could not be read
+ */
+ public static BaseFont createFont(String name, String encoding, boolean embedded) throws DocumentException, IOException {
+ return createFont(name, encoding, embedded, true, null, null);
+ }
+
+ /** Creates a new font. This font can be one of the 14 built in types,
+ * a Type1 font referred by an AFM file, a TrueType font (simple or collection) or a CJK font from the
+ * Adobe Asian Font Pack. TrueType fonts and CJK fonts can have an optional style modifier
+ * appended to the name. These modifiers are: Bold, Italic and BoldItalic. An
+ * example would be "STSong-Light,Bold". Note that this modifiers do not work if
+ * the font is embedded. Fonts in TrueType collections are addressed by index such as "msgothic.ttc,1".
+ * This would get the second font (indexes start at 0), in this case "MS PGothic".
+ * cached
.
+ * If the byte
arrays are present the font will be
+ * read from them instead of the name. The name is still required to identify
+ * the font type.
+ * @param name the name of the font or it's location on file
+ * @param encoding the encoding to be applied to this font
+ * @param embedded true if the font is to be embedded in the PDF
+ * @param cached true if the font comes from the cache or is added to
+ * the cache if new, false if the font is always created new
+ * @param ttfAfm the true type font or the afm in a byte array
+ * @param pfb the pfb in a byte array
+ * @return returns a new font. This font may come from the cache but only if cached
+ * is true, otherwise it will always be created new
+ * @throws DocumentException the font is invalid
+ * @throws IOException the font file could not be read
+ */
+ public static BaseFont createFont(String name, String encoding, boolean embedded, boolean cached, byte ttfAfm[], byte pfb[]) throws DocumentException, IOException {
+ String nameBase = getBaseName(name);
+ encoding = normalizeEncoding(encoding);
+ boolean isBuiltinFonts14 = BuiltinFonts14.containsKey(name);
+ boolean isCJKFont = isBuiltinFonts14 ? false : CJKFont.isCJKFont(nameBase, encoding);
+ if (isBuiltinFonts14 || isCJKFont)
+ embedded = false;
+ else if (encoding.equals(IDENTITY_H) || encoding.equals(IDENTITY_V))
+ embedded = true;
+ BaseFont fontFound = null;
+ BaseFont fontBuilt = null;
+ String key = name + "\n" + encoding + "\n" + embedded;
+ if (cached) {
+ synchronized (fontCache) {
+ fontFound = (BaseFont)fontCache.get(key);
+ }
+ if (fontFound != null)
+ return fontFound;
+ }
+ if (isBuiltinFonts14 || name.toLowerCase().endsWith(".afm") || name.toLowerCase().endsWith(".pfm")) {
+ fontBuilt = new Type1Font(name, encoding, embedded, ttfAfm, pfb);
+ fontBuilt.fastWinansi = encoding.equals(CP1252);
+ }
+ else if (nameBase.toLowerCase().endsWith(".ttf") || nameBase.toLowerCase().endsWith(".otf") || nameBase.toLowerCase().indexOf(".ttc,") > 0) {
+ if (encoding.equals(IDENTITY_H) || encoding.equals(IDENTITY_V))
+ fontBuilt = new TrueTypeFontUnicode(name, encoding, embedded, ttfAfm);
+ else {
+ fontBuilt = new TrueTypeFont(name, encoding, embedded, ttfAfm);
+ fontBuilt.fastWinansi = encoding.equals(CP1252);
+ }
+ }
+ else if (isCJKFont)
+ fontBuilt = new CJKFont(name, encoding, embedded);
+ else
+ throw new DocumentException("Font '" + name + "' with '" + encoding + "' is not recognized.");
+ if (cached) {
+ synchronized (fontCache) {
+ fontFound = (BaseFont)fontCache.get(key);
+ if (fontFound != null)
+ return fontFound;
+ fontCache.put(key, fontBuilt);
+ }
+ }
+ return fontBuilt;
+ }
+
+ /**
+ * Creates a font based on an existing document font. The created font font may not
+ * behave as expected, depending on the encoding or subset.
+ * @param fontRef the reference to the document font
+ * @return the font
+ */
+ public static BaseFont createFont(PRIndirectReference fontRef) {
+ return new DocumentFont(fontRef);
+ }
+
+ /**
+ * Gets the name without the modifiers Bold, Italic or BoldItalic.
+ * @param name the full name of the font
+ * @return the name without the modifiers Bold, Italic or BoldItalic
+ */
+ protected static String getBaseName(String name) {
+ if (name.endsWith(",Bold"))
+ return name.substring(0, name.length() - 5);
+ else if (name.endsWith(",Italic"))
+ return name.substring(0, name.length() - 7);
+ else if (name.endsWith(",BoldItalic"))
+ return name.substring(0, name.length() - 11);
+ else
+ return name;
+ }
+
+ /**
+ * Normalize the encoding names. "winansi" is changed to "Cp1252" and
+ * "macroman" is changed to "MacRoman".
+ * @param enc the encoding to be normalized
+ * @return the normalized encoding
+ */
+ protected static String normalizeEncoding(String enc) {
+ if (enc.equals("winansi") || enc.equals(""))
+ return CP1252;
+ else if (enc.equals("macroman"))
+ return MACROMAN;
+ else
+ return enc;
+ }
+
+ /**
+ * Creates the widths
and the differences
arrays
+ */
+ protected void createEncoding() {
+ if (fontSpecific) {
+ for (int k = 0; k < 256; ++k) {
+ widths[k] = getRawWidth(k, null);
+ charBBoxes[k] = getRawCharBBox(k, null);
+ }
+ }
+ else {
+ String s;
+ String name;
+ char c;
+ byte b[] = new byte[1];
+ for (int k = 0; k < 256; ++k) {
+ b[0] = (byte)k;
+ s = PdfEncodings.convertToString(b, encoding);
+ if (s.length() > 0) {
+ c = s.charAt(0);
+ }
+ else {
+ c = '?';
+ }
+ name = GlyphList.unicodeToName((int)c);
+ if (name == null)
+ name = notdef;
+ differences[k] = name;
+ unicodeDifferences[k] = c;
+ widths[k] = getRawWidth((int)c, name);
+ charBBoxes[k] = getRawCharBBox((int)c, name);
+ }
+ }
+ }
+
+ /**
+ * Gets the width from the font according to the Unicode char c
+ * or the name
. If the name
is null it's a symbolic font.
+ * @param c the unicode char
+ * @param name the glyph name
+ * @return the width of the char
+ */
+ abstract int getRawWidth(int c, String name);
+
+ /**
+ * Gets the kerning between two Unicode chars.
+ * @param char1 the first char
+ * @param char2 the second char
+ * @return the kerning to be applied in normalized 1000 units
+ */
+ public abstract int getKerning(char char1, char char2);
+
+ /**
+ * Sets the kerning between two Unicode chars.
+ * @param char1 the first char
+ * @param char2 the second char
+ * @param kern the kerning to apply in normalized 1000 units
+ * @return true
if the kerning was applied, false
otherwise
+ */
+ public abstract boolean setKerning(char char1, char char2, int kern);
+
+ /**
+ * Gets the width of a char
in normalized 1000 units.
+ * @param char1 the unicode char
to get the width of
+ * @return the width in normalized 1000 units
+ */
+ public int getWidth(char char1) {
+ if (fastWinansi) {
+ if (char1 < 128 || (char1 >= 160 && char1 <= 255))
+ return widths[char1];
+ return widths[PdfEncodings.winansi.get(char1)];
+ }
+ return getWidth(new String(new char[]{char1}));
+ }
+
+ /**
+ * Gets the width of a String
in normalized 1000 units.
+ * @param text the String
to get the witdth of
+ * @return the width in normalized 1000 units
+ */
+ public int getWidth(String text) {
+ int total = 0;
+ if (fastWinansi) {
+ int len = text.length();
+ for (int k = 0; k < len; ++k) {
+ char char1 = text.charAt(k);
+ if (char1 < 128 || (char1 >= 160 && char1 <= 255))
+ total += widths[char1];
+ else
+ total += widths[PdfEncodings.winansi.get(char1)];
+ }
+ return total;
+ }
+ else {
+ byte mbytes[] = convertToBytes(text);
+ for (int k = 0; k < mbytes.length; ++k)
+ total += widths[0xff & mbytes[k]];
+ }
+ return total;
+ }
+
+/**
+ * Gets the descent of a String
in normalized 1000 units. The descent will always be
+ * less than or equal to zero even if all the characters have an higher descent.
+ * @param text the String
to get the descent of
+ * @return the dexcent in normalized 1000 units
+ */
+ public int getDescent(String text) {
+ int min = 0;
+ char chars[] = text.toCharArray();
+ for (int k = 0; k < chars.length; ++k) {
+ int bbox[] = getCharBBox(chars[k]);
+ if (bbox != null && bbox[1] < min)
+ min = bbox[1];
+ }
+ return min;
+ }
+
+/**
+ * Gets the ascent of a String
in normalized 1000 units. The ascent will always be
+ * greater than or equal to zero even if all the characters have a lower ascent.
+ * @param text the String
to get the ascent of
+ * @return the ascent in normalized 1000 units
+ */
+ public int getAscent(String text) {
+ int max = 0;
+ char chars[] = text.toCharArray();
+ for (int k = 0; k < chars.length; ++k) {
+ int bbox[] = getCharBBox(chars[k]);
+ if (bbox != null && bbox[3] > max)
+ max = bbox[3];
+ }
+ return max;
+ }
+
+/**
+ * Gets the descent of a String
in points. The descent will always be
+ * less than or equal to zero even if all the characters have an higher descent.
+ * @param text the String
to get the descent of
+ * @param fontSize the size of the font
+ * @return the dexcent in points
+ */
+ public float getDescentPoint(String text, float fontSize)
+ {
+ return (float)getDescent(text) * 0.001f * fontSize;
+ }
+
+/**
+ * Gets the ascent of a String
in points. The ascent will always be
+ * greater than or equal to zero even if all the characters have a lower ascent.
+ * @param text the String
to get the ascent of
+ * @param fontSize the size of the font
+ * @return the ascent in points
+ */
+ public float getAscentPoint(String text, float fontSize)
+ {
+ return (float)getAscent(text) * 0.001f * fontSize;
+ }
+// ia>
+
+ /**
+ * Gets the width of a String
in points taking kerning
+ * into account.
+ * @param text the String
to get the witdth of
+ * @param fontSize the font size
+ * @return the width in points
+ */
+ public float getWidthPointKerned(String text, float fontSize) {
+ float size = (float)getWidth(text) * 0.001f * fontSize;
+ if (!hasKernPairs())
+ return size;
+ int len = text.length() - 1;
+ int kern = 0;
+ char c[] = text.toCharArray();
+ for (int k = 0; k < len; ++k) {
+ kern += getKerning(c[k], c[k + 1]);
+ }
+ return size + kern * 0.001f * fontSize;
+ }
+
+ /**
+ * Gets the width of a String
in points.
+ * @param text the String
to get the witdth of
+ * @param fontSize the font size
+ * @return the width in points
+ */
+ public float getWidthPoint(String text, float fontSize) {
+ return (float)getWidth(text) * 0.001f * fontSize;
+ }
+
+ /**
+ * Gets the width of a char
in points.
+ * @param char1 the char
to get the witdth of
+ * @param fontSize the font size
+ * @return the width in points
+ */
+ public float getWidthPoint(char char1, float fontSize) {
+ return getWidth(char1) * 0.001f * fontSize;
+ }
+
+ /**
+ * Converts a String
to a String
to be converted
+ * @return an array of byte
representing the conversion according to the font's encoding
+ */
+ byte[] convertToBytes(String text) {
+ if (directTextToByte)
+ return PdfEncodings.convertToBytes(text, null);
+ return PdfEncodings.convertToBytes(text, encoding);
+ }
+
+ /** Outputs to the writer the font dictionaries and streams.
+ * @param writer the writer for this document
+ * @param ref the font indirect reference
+ * @param params several parameters that depend on the font type
+ * @throws IOException on error
+ * @throws DocumentException error in generating the object
+ */
+ abstract void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException;
+
+ /** Gets the encoding used to convert String
into byte[]
.
+ * @return the encoding name
+ */
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /** Gets the font parameter identified by key
. Valid values
+ * for key
are ASCENT
, AWT_ASCENT
, CAPHEIGHT
,
+ * DESCENT
, AWT_DESCENT
,
+ * ITALICANGLE
, BBOXLLX
, BBOXLLY
, BBOXURX
+ * and BBOXURY
.
+ * @param key the parameter to be extracted
+ * @param fontSize the font size in points
+ * @return the parameter in points
+ */
+ public abstract float getFontDescriptor(int key, float fontSize);
+
+ /** Gets the font type. The font types can be: FONT_TYPE_T1,
+ * FONT_TYPE_TT, FONT_TYPE_CJK and FONT_TYPE_TTUNI.
+ * @return the font type
+ */
+ public int getFontType() {
+ return fontType;
+ }
+
+ /** Gets the embedded flag.
+ * @return true
if the font is embedded.
+ */
+ public boolean isEmbedded() {
+ return embedded;
+ }
+
+ /** Gets the symbolic flag of the font.
+ * @return true
if the font is symbolic
+ */
+ public boolean isFontSpecific() {
+ return fontSpecific;
+ }
+
+ /** Creates a unique subset prefix to be added to the font name when the font is embedded and subset.
+ * @return the subset prefix
+ */
+ public static String createSubsetPrefix() {
+ String s = "";
+ for (int k = 0; k < 6; ++k)
+ s += (char)(Math.random() * 26 + 'A');
+ return s + "+";
+ }
+
+ /** Gets the Unicode character corresponding to the byte output to the pdf stream.
+ * @param index the byte index
+ * @return the Unicode character
+ */
+ char getUnicodeDifferences(int index) {
+ return unicodeDifferences[index];
+ }
+
+ /** Gets the postscript font name.
+ * @return the postscript font name
+ */
+ public abstract String getPostscriptFontName();
+
+ /**
+ * Sets the font name that will appear in the pdf font dictionary.
+ * Use with care as it can easily make a font unreadable if not embedded.
+ * @param name the new font name
+ */
+ public abstract void setPostscriptFontName(String name);
+
+ /** Gets the full name of the font. If it is a True Type font
+ * each array element will have {Platform ID, Platform Encoding ID,
+ * Language ID, font name}. The interpretation of this values can be
+ * found in the Open Type specification, chapter 2, in the 'name' table.
+ * For the other fonts the array has a single element with {"", "", "",
+ * font name}.
+ * @return the full name of the font
+ */
+ public abstract String[][] getFullFontName();
+
+ /** Gets the full name of the font. If it is a True Type font
+ * each array element will have {Platform ID, Platform Encoding ID,
+ * Language ID, font name}. The interpretation of this values can be
+ * found in the Open Type specification, chapter 2, in the 'name' table.
+ * For the other fonts the array has a single element with {"", "", "",
+ * font name}.
+ * @param name the name of the font
+ * @param encoding the encoding of the font
+ * @param ttfAfm the true type font or the afm in a byte array
+ * @throws DocumentException on error
+ * @throws IOException on error
+ * @return the full name of the font
+ */
+ public static String[][] getFullFontName(String name, String encoding, byte ttfAfm[]) throws DocumentException, IOException {
+ String nameBase = getBaseName(name);
+ BaseFont fontBuilt = null;
+ if (nameBase.toLowerCase().endsWith(".ttf") || nameBase.toLowerCase().endsWith(".otf") || nameBase.toLowerCase().indexOf(".ttc,") > 0)
+ fontBuilt = new TrueTypeFont(name, CP1252, false, ttfAfm, true);
+ else
+ fontBuilt = createFont(name, encoding, false, false, ttfAfm, null);
+ return fontBuilt.getFullFontName();
+ }
+
+ /** Gets all the names from the font. Only the required tables are read.
+ * @param name the name of the font
+ * @param encoding the encoding of the font
+ * @param ttfAfm the true type font or the afm in a byte array
+ * @throws DocumentException on error
+ * @throws IOException on error
+ * @return an array of Object[] built with {getPostscriptFontName(), getFamilyFontName(), getFullFontName()}
+ */
+ public static Object[] getAllFontNames(String name, String encoding, byte ttfAfm[]) throws DocumentException, IOException {
+ String nameBase = getBaseName(name);
+ BaseFont fontBuilt = null;
+ if (nameBase.toLowerCase().endsWith(".ttf") || nameBase.toLowerCase().endsWith(".otf") || nameBase.toLowerCase().indexOf(".ttc,") > 0)
+ fontBuilt = new TrueTypeFont(name, CP1252, false, ttfAfm, true);
+ else
+ fontBuilt = createFont(name, encoding, false, false, ttfAfm, null);
+ return new Object[]{fontBuilt.getPostscriptFontName(), fontBuilt.getFamilyFontName(), fontBuilt.getFullFontName()};
+ }
+
+ /** Gets the family name of the font. If it is a True Type font
+ * each array element will have {Platform ID, Platform Encoding ID,
+ * Language ID, font name}. The interpretation of this values can be
+ * found in the Open Type specification, chapter 2, in the 'name' table.
+ * For the other fonts the array has a single element with {"", "", "",
+ * font name}.
+ * @return the family name of the font
+ */
+ public abstract String[][] getFamilyFontName();
+
+ /** Gets the code pages supported by the font. This has only meaning
+ * with True Type fonts.
+ * @return the code pages supported by the font
+ */
+ public String[] getCodePagesSupported() {
+ return new String[0];
+ }
+
+ /** Enumerates the postscript font names present inside a
+ * True Type Collection.
+ * @param ttcFile the file name of the font
+ * @throws DocumentException on error
+ * @throws IOException on error
+ * @return the postscript font names
+ */
+ public static String[] enumerateTTCNames(String ttcFile) throws DocumentException, IOException {
+ return new EnumerateTTC(ttcFile).getNames();
+ }
+
+ /** Enumerates the postscript font names present inside a
+ * True Type Collection.
+ * @param ttcArray the font as a byte
array
+ * @throws DocumentException on error
+ * @throws IOException on error
+ * @return the postscript font names
+ */
+ public static String[] enumerateTTCNames(byte ttcArray[]) throws DocumentException, IOException {
+ return new EnumerateTTC(ttcArray).getNames();
+ }
+
+ /** Gets the font width array.
+ * @return the font width array
+ */
+ public int[] getWidths() {
+ return widths;
+ }
+
+ /** Gets the array with the names of the characters.
+ * @return the array with the names of the characters
+ */
+ public String[] getDifferences() {
+ return differences;
+ }
+
+ /** Gets the array with the unicode characters.
+ * @return the array with the unicode characters
+ */
+ public char[] getUnicodeDifferences() {
+ return unicodeDifferences;
+ }
+
+ /** Gets the state of the property.
+ * @return value of property forceWidthsOutput
+ */
+ public boolean isForceWidthsOutput() {
+ return forceWidthsOutput;
+ }
+
+ /** Set to true
to force the generation of the
+ * widths array.
+ * @param forceWidthsOutput true
to force the generation of the
+ * widths array
+ */
+ public void setForceWidthsOutput(boolean forceWidthsOutput) {
+ this.forceWidthsOutput = forceWidthsOutput;
+ }
+
+ /** Gets the direct conversion of char
to byte
.
+ * @return value of property directTextToByte.
+ * @see #setDirectTextToByte(boolean directTextToByte)
+ */
+ public boolean isDirectTextToByte() {
+ return directTextToByte;
+ }
+
+ /** Sets the conversion of char
directly to byte
+ * by casting. This is a low level feature to put the bytes directly in
+ * the content stream without passing through String.getBytes().
+ * @param directTextToByte New value of property directTextToByte.
+ */
+ public void setDirectTextToByte(boolean directTextToByte) {
+ this.directTextToByte = directTextToByte;
+ }
+
+ /** Indicates if all the glyphs and widths for that particular
+ * encoding should be included in the document.
+ * @return false
to include all the glyphs and widths.
+ */
+ public boolean isSubset() {
+ return subset;
+ }
+
+ /** Indicates if all the glyphs and widths for that particular
+ * encoding should be included in the document. When set to true
+ * only the glyphs used will be included in the font. When set to false
+ * and {@link #addSubsetRange(int[])} was not called the full font will be included
+ * otherwise just the characters ranges will be included.
+ * @param subset new value of property subset
+ */
+ public void setSubset(boolean subset) {
+ this.subset = subset;
+ }
+
+ /** Gets the font resources.
+ * @param key the full name of the resource
+ * @return the InputStream
to get the resource or
+ * null
if not found
+ */
+ public static InputStream getResourceStream(String key) {
+ return getResourceStream(key, null);
+ }
+
+ /** Gets the font resources.
+ * @param key the full name of the resource
+ * @param loader the ClassLoader to load the resource or null to try the ones available
+ * @return the InputStream
to get the resource or
+ * null
if not found
+ */
+ public static InputStream getResourceStream(String key, ClassLoader loader) {
+ if (key.startsWith("/"))
+ key = key.substring(1);
+ InputStream is = null;
+ if (loader != null) {
+ is = loader.getResourceAsStream(key);
+ if (is != null)
+ return is;
+ }
+ // Try to use Context Class Loader to load the properties file.
+ try {
+ java.lang.reflect.Method getCCL =
+ Thread.class.getMethod("getContextClassLoader", new Class[0]);
+ if (getCCL != null) {
+ ClassLoader contextClassLoader =
+ (ClassLoader)getCCL.invoke(Thread.currentThread(),
+ new Object[0]);
+ if (contextClassLoader != null)
+ is = contextClassLoader.getResourceAsStream(key);
+ }
+ } catch (Throwable e) {}
+
+ if (is == null) {
+ is = BaseFont.class.getResourceAsStream("/" + key);
+ }
+ if (is == null) {
+ is = ClassLoader.getSystemResourceAsStream(key);
+ }
+ return is;
+ }
+
+ /** Gets the Unicode equivalent to a CID.
+ * The (inexistent) CID true
if the font has any kerning pairs
+ */
+ public abstract boolean hasKernPairs();
+
+ /**
+ * Checks if a character exists in this font.
+ * @param c the character to check
+ * @return true
if the character has a glyph,
+ * false
otherwise
+ */
+ public boolean charExists(char c) {
+ byte b[] = convertToBytes(new String(new char[]{c}));
+ return b.length > 0;
+ }
+
+ /**
+ * Sets the character advance.
+ * @param c the character
+ * @param advance the character advance normalized to 1000 units
+ * @return true
if the advance was set,
+ * false
otherwise
+ */
+ public boolean setCharAdvance(char c, int advance) {
+ byte b[] = convertToBytes(new String(new char[]{c}));
+ if (b.length == 0)
+ return false;
+ widths[0xff & b[0]] = advance;
+ return true;
+ }
+
+ private static void addFont(PRIndirectReference fontRef, IntHashtable hits, ArrayList fonts) {
+ PdfObject obj = PdfReader.getPdfObject(fontRef);
+ if (!obj.isDictionary())
+ return;
+ PdfDictionary font = (PdfDictionary)obj;
+ PdfName subtype = (PdfName)PdfReader.getPdfObject(font.get(PdfName.SUBTYPE));
+ if (!PdfName.TYPE1.equals(subtype) && !PdfName.TRUETYPE.equals(subtype))
+ return;
+ PdfName name = (PdfName)PdfReader.getPdfObject(font.get(PdfName.BASEFONT));
+ fonts.add(new Object[]{PdfName.decodeName(name.toString()), fontRef});
+ hits.put(fontRef.getNumber(), 1);
+ }
+
+ private static void recourseFonts(PdfDictionary page, IntHashtable hits, ArrayList fonts, int level) {
+ ++level;
+ if (level > 50) // in case we have an endless loop
+ return;
+ PdfDictionary resources = (PdfDictionary)PdfReader.getPdfObject(page.get(PdfName.RESOURCES));
+ if (resources == null)
+ return;
+ PdfDictionary font = (PdfDictionary)PdfReader.getPdfObject(resources.get(PdfName.FONT));
+ if (font != null) {
+ for (Iterator it = font.getKeys().iterator(); it.hasNext();) {
+ PdfObject ft = font.get((PdfName)it.next());
+ if (ft == null || !ft.isIndirect())
+ continue;
+ int hit = ((PRIndirectReference)ft).getNumber();
+ if (hits.containsKey(hit))
+ continue;
+ addFont((PRIndirectReference)ft, hits, fonts);
+ }
+ }
+ PdfDictionary xobj = (PdfDictionary)PdfReader.getPdfObject(resources.get(PdfName.XOBJECT));
+ if (xobj != null) {
+ for (Iterator it = xobj.getKeys().iterator(); it.hasNext();) {
+ recourseFonts((PdfDictionary)PdfReader.getPdfObject(xobj.get((PdfName)it.next())), hits, fonts, level);
+ }
+ }
+ }
+
+ /**
+ * Gets a list of all document fonts. Each element of the ArrayList
+ * contains a Object[]{String,PRIndirectReference}
with the font name
+ * and the indirect reference to it.
+ * @param reader the document where the fonts are to be listed from
+ * @return the list of fonts and references
+ */
+ public static ArrayList getDocumentFonts(PdfReader reader) {
+ IntHashtable hits = new IntHashtable();
+ ArrayList fonts = new ArrayList();
+ int npages = reader.getNumberOfPages();
+ for (int k = 1; k <= npages; ++k)
+ recourseFonts(reader.getPageN(k), hits, fonts, 1);
+ return fonts;
+ }
+
+ /**
+ * Gets a list of the document fonts in a particular page. Each element of the ArrayList
+ * contains a Object[]{String,PRIndirectReference}
with the font name
+ * and the indirect reference to it.
+ * @param reader the document where the fonts are to be listed from
+ * @param page the page to list the fonts from
+ * @return the list of fonts and references
+ */
+ public static ArrayList getDocumentFonts(PdfReader reader, int page) {
+ IntHashtable hits = new IntHashtable();
+ ArrayList fonts = new ArrayList();
+ recourseFonts(reader.getPageN(page), hits, fonts, 1);
+ return fonts;
+ }
+
+ /**
+ * Gets the smallest box enclosing the character contours. It will return
+ * null
if the font has not the information or the character has no
+ * contours, as in the case of the space, for example. Characters with no contours may
+ * also return [0,0,0,0].
+ * @param c the character to get the contour bounding box from
+ * @return an array of four floats with the bounding box in the format [llx,lly,urx,ury] or
+ * null
+ */
+ public int[] getCharBBox(char c) {
+ byte b[] = convertToBytes(new String(new char[]{c}));
+ if (b.length == 0)
+ return null;
+ else
+ return charBBoxes[b[0] & 0xff];
+ }
+
+ protected abstract int[] getRawCharBBox(int c, String name);
+
+ /**
+ * iText expects Arabic Diactrics (tashkeel) to have zero advance but some fonts,
+ * most notably those that come with Windows, like times.ttf, have non-zero
+ * advance for those characters. This method makes those character to have zero
+ * width advance and work correctly in the iText Arabic shaping and reordering
+ * context.
+ */
+ public void correctArabicAdvance() {
+ for (char c = '\u064b'; c <= '\u0658'; ++c)
+ setCharAdvance(c, 0);
+ setCharAdvance('\u0670', 0);
+ for (char c = '\u06d6'; c <= '\u06dc'; ++c)
+ setCharAdvance(c, 0);
+ for (char c = '\u06df'; c <= '\u06e4'; ++c)
+ setCharAdvance(c, 0);
+ for (char c = '\u06e7'; c <= '\u06e8'; ++c)
+ setCharAdvance(c, 0);
+ for (char c = '\u06ea'; c <= '\u06ed'; ++c)
+ setCharAdvance(c, 0);
+ }
+
+ /**
+ * Adds a character range when subsetting. The range is an int
array
+ * where the first element is the start range inclusive and the second element is the
+ * end range inclusive. Several ranges are allowed in the same array.
+ * @param range the character range
+ */
+ public void addSubsetRange(int[] range) {
+ if (subsetRanges == null)
+ subsetRanges = new ArrayList();
+ subsetRanges.add(range);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/BidiLine.java b/src/main/java/com/lowagie/text/pdf/BidiLine.java
new file mode 100644
index 0000000..54dd32e
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/BidiLine.java
@@ -0,0 +1,915 @@
+/*
+ *
+ * Copyright 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.ArrayList;
+
+import com.lowagie.text.Chunk;
+
+/** Does all the line bidirectional processing with PdfChunk assembly.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class BidiLine {
+
+ protected int runDirection;
+ protected int pieceSize = 2048;
+ protected char text[] = new char[pieceSize];
+ protected PdfChunk detailChunks[] = new PdfChunk[pieceSize];
+ protected int totalTextLength = 0;
+
+ protected byte orderLevels[] = new byte[pieceSize];
+ protected int indexChars[] = new int[pieceSize];
+
+ protected ArrayList chunks = new ArrayList();
+ protected int indexChunk = 0;
+ protected int indexChunkChar = 0;
+ protected int currentChar = 0;
+
+ protected int storedRunDirection;
+ protected char storedText[] = new char[0];
+ protected PdfChunk storedDetailChunks[] = new PdfChunk[0];
+ protected int storedTotalTextLength = 0;
+
+ protected byte storedOrderLevels[] = new byte[0];
+ protected int storedIndexChars[] = new int[0];
+
+ protected int storedIndexChunk = 0;
+ protected int storedIndexChunkChar = 0;
+ protected int storedCurrentChar = 0;
+
+ protected boolean shortStore;
+// protected ArabicShaping arabic = new ArabicShaping(ArabicShaping.LETTERS_SHAPE | ArabicShaping.LENGTH_GROW_SHRINK | ArabicShaping.TEXT_DIRECTION_LOGICAL);
+ protected static final IntHashtable mirrorChars = new IntHashtable();
+ protected int arabicOptions;
+
+ /** Creates new BidiLine */
+ public BidiLine() {
+ }
+
+ public BidiLine(BidiLine org) {
+ runDirection = org.runDirection;
+ pieceSize = org.pieceSize;
+ text = (char[])org.text.clone();
+ detailChunks = (PdfChunk[])org.detailChunks.clone();
+ totalTextLength = org.totalTextLength;
+
+ orderLevels = (byte[])org.orderLevels.clone();
+ indexChars = (int[])org.indexChars.clone();
+
+ chunks = new ArrayList(org.chunks);
+ indexChunk = org.indexChunk;
+ indexChunkChar = org.indexChunkChar;
+ currentChar = org.currentChar;
+
+ storedRunDirection = org.storedRunDirection;
+ storedText = (char[])org.storedText.clone();
+ storedDetailChunks = (PdfChunk[])org.storedDetailChunks.clone();
+ storedTotalTextLength = org.storedTotalTextLength;
+
+ storedOrderLevels = (byte[])org.storedOrderLevels.clone();
+ storedIndexChars = (int[])org.storedIndexChars.clone();
+
+ storedIndexChunk = org.storedIndexChunk;
+ storedIndexChunkChar = org.storedIndexChunkChar;
+ storedCurrentChar = org.storedCurrentChar;
+
+ shortStore = org.shortStore;
+ arabicOptions = org.arabicOptions;
+ }
+
+ public boolean isEmpty() {
+ return (currentChar >= totalTextLength && indexChunk >= chunks.size());
+ }
+
+ public void clearChunks() {
+ chunks.clear();
+ totalTextLength = 0;
+ currentChar = 0;
+ }
+
+ public boolean getParagraph(int runDirection) {
+ this.runDirection = runDirection;
+ currentChar = 0;
+ totalTextLength = 0;
+ boolean hasText = false;
+ char c;
+ char uniC;
+ BaseFont bf;
+ for (; indexChunk < chunks.size(); ++indexChunk) {
+ PdfChunk ck = (PdfChunk)chunks.get(indexChunk);
+ bf = ck.font().getFont();
+ String s = ck.toString();
+ int len = s.length();
+ for (; indexChunkChar < len; ++indexChunkChar) {
+ c = s.charAt(indexChunkChar);
+ uniC = bf.getUnicodeEquivalent(c);
+ if (uniC == '\r' || uniC == '\n') {
+ // next condition is never true for CID
+ if (uniC == '\r' && indexChunkChar + 1 < len && s.charAt(indexChunkChar + 1) == '\n')
+ ++indexChunkChar;
+ ++indexChunkChar;
+ if (indexChunkChar >= len) {
+ indexChunkChar = 0;
+ ++indexChunk;
+ }
+ hasText = true;
+ if (totalTextLength == 0)
+ detailChunks[0] = ck;
+ break;
+ }
+ addPiece(c, ck);
+ }
+ if (hasText)
+ break;
+ indexChunkChar = 0;
+ }
+ if (totalTextLength == 0)
+ return hasText;
+
+ // remove trailing WS
+ totalTextLength = trimRight(0, totalTextLength - 1) + 1;
+ if (totalTextLength == 0)
+ return true;
+
+ if (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL) {
+ if (orderLevels.length < totalTextLength) {
+ orderLevels = new byte[pieceSize];
+ indexChars = new int[pieceSize];
+ }
+ ArabicLigaturizer.processNumbers(text, 0, totalTextLength, arabicOptions);
+ BidiOrder order = new BidiOrder(text, 0, totalTextLength, (byte)(runDirection == PdfWriter.RUN_DIRECTION_RTL ? 1 : 0));
+ byte od[] = order.getLevels();
+ for (int k = 0; k < totalTextLength; ++k) {
+ orderLevels[k] = od[k];
+ indexChars[k] = k;
+ }
+ doArabicShapping();
+ mirrorGlyphs();
+ }
+ totalTextLength = trimRightEx(0, totalTextLength - 1) + 1;
+ return true;
+ }
+
+ public void addChunk(PdfChunk chunk) {
+ chunks.add(chunk);
+ }
+
+ public void addChunks(ArrayList chunks) {
+ this.chunks.addAll(chunks);
+ }
+
+ public void addPiece(char c, PdfChunk chunk) {
+ if (totalTextLength >= pieceSize) {
+ char tempText[] = text;
+ PdfChunk tempDetailChunks[] = detailChunks;
+ pieceSize *= 2;
+ text = new char[pieceSize];
+ detailChunks = new PdfChunk[pieceSize];
+ System.arraycopy(tempText, 0, text, 0, totalTextLength);
+ System.arraycopy(tempDetailChunks, 0, detailChunks, 0, totalTextLength);
+ }
+ text[totalTextLength] = c;
+ detailChunks[totalTextLength++] = chunk;
+ }
+
+ public void save() {
+ if (indexChunk > 0) {
+ if (indexChunk >= chunks.size())
+ chunks.clear();
+ else {
+ for (--indexChunk; indexChunk >= 0; --indexChunk)
+ chunks.remove(indexChunk);
+ }
+ indexChunk = 0;
+ }
+ storedRunDirection = runDirection;
+ storedTotalTextLength = totalTextLength;
+ storedIndexChunk = indexChunk;
+ storedIndexChunkChar = indexChunkChar;
+ storedCurrentChar = currentChar;
+ shortStore = (currentChar < totalTextLength);
+ if (!shortStore) {
+ // long save
+ if (storedText.length < totalTextLength) {
+ storedText = new char[totalTextLength];
+ storedDetailChunks = new PdfChunk[totalTextLength];
+ }
+ System.arraycopy(text, 0, storedText, 0, totalTextLength);
+ System.arraycopy(detailChunks, 0, storedDetailChunks, 0, totalTextLength);
+ }
+ if (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL) {
+ if (storedOrderLevels.length < totalTextLength) {
+ storedOrderLevels = new byte[totalTextLength];
+ storedIndexChars = new int[totalTextLength];
+ }
+ System.arraycopy(orderLevels, currentChar, storedOrderLevels, currentChar, totalTextLength - currentChar);
+ System.arraycopy(indexChars, currentChar, storedIndexChars, currentChar, totalTextLength - currentChar);
+ }
+ }
+
+ public void restore() {
+ runDirection = storedRunDirection;
+ totalTextLength = storedTotalTextLength;
+ indexChunk = storedIndexChunk;
+ indexChunkChar = storedIndexChunkChar;
+ currentChar = storedCurrentChar;
+ if (!shortStore) {
+ // long restore
+ System.arraycopy(storedText, 0, text, 0, totalTextLength);
+ System.arraycopy(storedDetailChunks, 0, detailChunks, 0, totalTextLength);
+ }
+ if (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL) {
+ System.arraycopy(storedOrderLevels, currentChar, orderLevels, currentChar, totalTextLength - currentChar);
+ System.arraycopy(storedIndexChars, currentChar, indexChars, currentChar, totalTextLength - currentChar);
+ }
+ }
+
+ public void mirrorGlyphs() {
+ for (int k = 0; k < totalTextLength; ++k) {
+ if ((orderLevels[k] & 1) == 1) {
+ int mirror = mirrorChars.get(text[k]);
+ if (mirror != 0)
+ text[k] = (char)mirror;
+ }
+ }
+ }
+
+ public void doArabicShapping() {
+ int src = 0;
+ int dest = 0;
+ for (;;) {
+ while (src < totalTextLength) {
+ char c = text[src];
+ if (c >= 0x0600 && c <= 0x06ff)
+ break;
+ if (src != dest) {
+ text[dest] = text[src];
+ detailChunks[dest] = detailChunks[src];
+ orderLevels[dest] = orderLevels[src];
+ }
+ ++src;
+ ++dest;
+ }
+ if (src >= totalTextLength) {
+ totalTextLength = dest;
+ return;
+ }
+ int startArabicIdx = src;
+ ++src;
+ while (src < totalTextLength) {
+ char c = text[src];
+ if (c < 0x0600 || c > 0x06ff)
+ break;
+ ++src;
+ }
+ int arabicWordSize = src - startArabicIdx;
+ int size = ArabicLigaturizer.arabic_shape(text, startArabicIdx, arabicWordSize, text, dest, arabicWordSize, arabicOptions /*PangoArabicShapping.ar_novowel PangoArabicShapping.ar_lig | PangoArabicShapping.ar_composedtashkeel*/);
+ if (startArabicIdx != dest) {
+ for (int k = 0; k < size; ++k) {
+ detailChunks[dest] = detailChunks[startArabicIdx];
+ orderLevels[dest++] = orderLevels[startArabicIdx++];
+ }
+ }
+ else
+ dest += size;
+ }
+ }
+
+ public PdfLine processLine(float width, int alignment, int runDirection, int arabicOptions) {
+ this.arabicOptions = arabicOptions;
+ save();
+ boolean isRTL = (runDirection == PdfWriter.RUN_DIRECTION_RTL);
+ if (currentChar >= totalTextLength) {
+ boolean hasText = getParagraph(runDirection);
+ if (!hasText)
+ return null;
+ if (totalTextLength == 0) {
+ ArrayList ar = new ArrayList();
+ PdfChunk ck = new PdfChunk("", detailChunks[0]);
+ ar.add(ck);
+ return new PdfLine(0, 0, alignment, true, ar, isRTL);
+ }
+ }
+ float originalWidth = width;
+ int lastSplit = -1;
+ if (currentChar != 0)
+ currentChar = trimLeftEx(currentChar, totalTextLength - 1);
+ int oldCurrentChar = currentChar;
+ char c = 0;
+ char uniC = 0;
+ PdfChunk ck = null;
+ float charWidth = 0;
+ PdfChunk lastValidChunk = null;
+ for (; currentChar < totalTextLength; ++currentChar) {
+ c = text[currentChar];
+ ck = detailChunks[currentChar];
+ uniC = ck.getUnicodeEquivalent(c);
+ if (PdfChunk.noPrint(uniC))
+ continue;
+ charWidth = ck.getCharWidth(c);
+ if (ck.isExtSplitCharacter(oldCurrentChar, currentChar, totalTextLength, text, detailChunks))
+ lastSplit = currentChar;
+ if (width - charWidth < 0)
+ break;
+ width -= charWidth;
+ lastValidChunk = ck;
+ }
+ if (lastValidChunk == null) {
+ // not even a single char fit; must output the first char
+ ++currentChar;
+ return new PdfLine(0, 0, alignment, false, createArrayOfPdfChunks(currentChar - 1, currentChar - 1), isRTL);
+ }
+ if (currentChar >= totalTextLength) {
+ // there was more line than text
+ return new PdfLine(0, width, alignment, true, createArrayOfPdfChunks(oldCurrentChar, totalTextLength - 1), isRTL);
+ }
+ int newCurrentChar = trimRightEx(oldCurrentChar, currentChar - 1);
+ if (newCurrentChar < oldCurrentChar) {
+ // only WS
+ return new PdfLine(0, width, alignment, false, createArrayOfPdfChunks(oldCurrentChar, currentChar - 1), isRTL);
+ }
+ if (newCurrentChar == currentChar - 1) { // middle of word
+ HyphenationEvent he = (HyphenationEvent)lastValidChunk.getAttribute(Chunk.HYPHENATION);
+ if (he != null) {
+ int word[] = getWord(oldCurrentChar, newCurrentChar);
+ if (word != null) {
+ float testWidth = width + getWidth(word[0], currentChar - 1);
+ String pre = he.getHyphenatedWordPre(new String(text, word[0], word[1] - word[0]), lastValidChunk.font().getFont(), lastValidChunk.font().size(), testWidth);
+ String post = he.getHyphenatedWordPost();
+ if (pre.length() > 0) {
+ PdfChunk extra = new PdfChunk(pre, lastValidChunk);
+ currentChar = word[1] - post.length();
+ return new PdfLine(0, testWidth - lastValidChunk.font().width(pre), alignment, false, createArrayOfPdfChunks(oldCurrentChar, word[0] - 1, extra), isRTL);
+ }
+ }
+ }
+ }
+ if (lastSplit == -1 || lastSplit >= newCurrentChar) {
+ // no split point or split point ahead of end
+ return new PdfLine(0, width + getWidth(newCurrentChar + 1, currentChar - 1), alignment, false, createArrayOfPdfChunks(oldCurrentChar, newCurrentChar), isRTL);
+ }
+ // standard split
+ currentChar = lastSplit + 1;
+ newCurrentChar = trimRightEx(oldCurrentChar, lastSplit);
+ if (newCurrentChar < oldCurrentChar) {
+ // only WS again
+ newCurrentChar = currentChar - 1;
+ }
+ return new PdfLine(0, originalWidth - getWidth(oldCurrentChar, newCurrentChar), alignment, false, createArrayOfPdfChunks(oldCurrentChar, newCurrentChar), isRTL);
+ }
+
+ /** Gets the width of a range of characters.
+ * @param startIdx the first index to calculate
+ * @param lastIdx the last inclusive index to calculate
+ * @return the sum of all widths
+ */
+ public float getWidth(int startIdx, int lastIdx) {
+ char c = 0;
+ char uniC;
+ PdfChunk ck = null;
+ float width = 0;
+ for (; startIdx <= lastIdx; ++startIdx) {
+ c = text[startIdx];
+ ck = detailChunks[startIdx];
+ uniC = ck.getUnicodeEquivalent(c);
+ if (PdfChunk.noPrint(uniC))
+ continue;
+ width += detailChunks[startIdx].getCharWidth(c);
+ }
+ return width;
+ }
+
+ public ArrayList createArrayOfPdfChunks(int startIdx, int endIdx) {
+ return createArrayOfPdfChunks(startIdx, endIdx, null);
+ }
+
+ public ArrayList createArrayOfPdfChunks(int startIdx, int endIdx, PdfChunk extraPdfChunk) {
+ boolean bidi = (runDirection == PdfWriter.RUN_DIRECTION_LTR || runDirection == PdfWriter.RUN_DIRECTION_RTL);
+ if (bidi)
+ reorder(startIdx, endIdx);
+ ArrayList ar = new ArrayList();
+ PdfChunk refCk = detailChunks[startIdx];
+ PdfChunk ck = null;
+ StringBuffer buf = new StringBuffer();
+ char c;
+ int idx = 0;
+ for (; startIdx <= endIdx; ++startIdx) {
+ idx = bidi ? indexChars[startIdx] : startIdx;
+ c = text[idx];
+ ck = detailChunks[idx];
+ if (PdfChunk.noPrint(ck.getUnicodeEquivalent(c)))
+ continue;
+ if (ck.isImage()) {
+ if (buf.length() > 0) {
+ ar.add(new PdfChunk(buf.toString(), refCk));
+ buf = new StringBuffer();
+ }
+ ar.add(ck);
+ }
+ else if (ck == refCk) {
+ buf.append(c);
+ }
+ else {
+ if (buf.length() > 0) {
+ ar.add(new PdfChunk(buf.toString(), refCk));
+ buf = new StringBuffer();
+ }
+ if (!ck.isImage())
+ buf.append(c);
+ refCk = ck;
+ }
+ }
+ if (buf.length() > 0) {
+ ar.add(new PdfChunk(buf.toString(), refCk));
+ }
+ if (extraPdfChunk != null)
+ ar.add(extraPdfChunk);
+ return ar;
+ }
+
+ public int[] getWord(int startIdx, int idx) {
+ int last = idx;
+ int first = idx;
+ // forward
+ for (; last < totalTextLength; ++last) {
+ if (!Character.isLetter(text[last]))
+ break;
+ }
+ if (last == idx)
+ return null;
+ // backward
+ for (; first >= startIdx; --first) {
+ if (!Character.isLetter(text[first]))
+ break;
+ }
+ ++first;
+ return new int[]{first, last};
+ }
+
+ public int trimRight(int startIdx, int endIdx) {
+ int idx = endIdx;
+ char c;
+ for (; idx >= startIdx; --idx) {
+ c = detailChunks[idx].getUnicodeEquivalent(text[idx]);
+ if (!isWS(c))
+ break;
+ }
+ return idx;
+ }
+
+ public int trimLeft(int startIdx, int endIdx) {
+ int idx = startIdx;
+ char c;
+ for (; idx <= endIdx; ++idx) {
+ c = detailChunks[idx].getUnicodeEquivalent(text[idx]);
+ if (!isWS(c))
+ break;
+ }
+ return idx;
+ }
+
+ public int trimRightEx(int startIdx, int endIdx) {
+ int idx = endIdx;
+ char c = 0;
+ for (; idx >= startIdx; --idx) {
+ c = detailChunks[idx].getUnicodeEquivalent(text[idx]);
+ if (!isWS(c) && !PdfChunk.noPrint(c))
+ break;
+ }
+ return idx;
+ }
+
+ public int trimLeftEx(int startIdx, int endIdx) {
+ int idx = startIdx;
+ char c = 0;
+ for (; idx <= endIdx; ++idx) {
+ c = detailChunks[idx].getUnicodeEquivalent(text[idx]);
+ if (!isWS(c) && !PdfChunk.noPrint(c))
+ break;
+ }
+ return idx;
+ }
+
+ public void reorder(int start, int end) {
+ byte maxLevel = orderLevels[start];
+ byte minLevel = maxLevel;
+ byte onlyOddLevels = maxLevel;
+ byte onlyEvenLevels = maxLevel;
+ for (int k = start + 1; k <= end; ++k) {
+ byte b = orderLevels[k];
+ if (b > maxLevel)
+ maxLevel = b;
+ else if (b < minLevel)
+ minLevel = b;
+ onlyOddLevels &= b;
+ onlyEvenLevels |= b;
+ }
+ if ((onlyEvenLevels & 1) == 0) // nothing to do
+ return;
+ if ((onlyOddLevels & 1) == 1) { // single inversion
+ flip(start, end + 1);
+ return;
+ }
+ minLevel |= 1;
+ for (; maxLevel >= minLevel; --maxLevel) {
+ int pstart = start;
+ for (;;) {
+ for (;pstart <= end; ++pstart) {
+ if (orderLevels[pstart] >= maxLevel)
+ break;
+ }
+ if (pstart > end)
+ break;
+ int pend = pstart + 1;
+ for (; pend <= end; ++pend) {
+ if (orderLevels[pend] < maxLevel)
+ break;
+ }
+ flip(pstart, pend);
+ pstart = pend + 1;
+ }
+ }
+ }
+
+ public void flip(int start, int end) {
+ int mid = (start + end) / 2;
+ --end;
+ for (; start < mid; ++start, --end) {
+ int temp = indexChars[start];
+ indexChars[start] = indexChars[end];
+ indexChars[end] = temp;
+ }
+ }
+
+ public static boolean isWS(char c) {
+ return (c <= ' ');
+ }
+
+ static {
+ mirrorChars.put(0x0028, 0x0029); // LEFT PARENTHESIS
+ mirrorChars.put(0x0029, 0x0028); // RIGHT PARENTHESIS
+ mirrorChars.put(0x003C, 0x003E); // LESS-THAN SIGN
+ mirrorChars.put(0x003E, 0x003C); // GREATER-THAN SIGN
+ mirrorChars.put(0x005B, 0x005D); // LEFT SQUARE BRACKET
+ mirrorChars.put(0x005D, 0x005B); // RIGHT SQUARE BRACKET
+ mirrorChars.put(0x007B, 0x007D); // LEFT CURLY BRACKET
+ mirrorChars.put(0x007D, 0x007B); // RIGHT CURLY BRACKET
+ mirrorChars.put(0x00AB, 0x00BB); // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ mirrorChars.put(0x00BB, 0x00AB); // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ mirrorChars.put(0x2039, 0x203A); // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ mirrorChars.put(0x203A, 0x2039); // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ mirrorChars.put(0x2045, 0x2046); // LEFT SQUARE BRACKET WITH QUILL
+ mirrorChars.put(0x2046, 0x2045); // RIGHT SQUARE BRACKET WITH QUILL
+ mirrorChars.put(0x207D, 0x207E); // SUPERSCRIPT LEFT PARENTHESIS
+ mirrorChars.put(0x207E, 0x207D); // SUPERSCRIPT RIGHT PARENTHESIS
+ mirrorChars.put(0x208D, 0x208E); // SUBSCRIPT LEFT PARENTHESIS
+ mirrorChars.put(0x208E, 0x208D); // SUBSCRIPT RIGHT PARENTHESIS
+ mirrorChars.put(0x2208, 0x220B); // ELEMENT OF
+ mirrorChars.put(0x2209, 0x220C); // NOT AN ELEMENT OF
+ mirrorChars.put(0x220A, 0x220D); // SMALL ELEMENT OF
+ mirrorChars.put(0x220B, 0x2208); // CONTAINS AS MEMBER
+ mirrorChars.put(0x220C, 0x2209); // DOES NOT CONTAIN AS MEMBER
+ mirrorChars.put(0x220D, 0x220A); // SMALL CONTAINS AS MEMBER
+ mirrorChars.put(0x2215, 0x29F5); // DIVISION SLASH
+ mirrorChars.put(0x223C, 0x223D); // TILDE OPERATOR
+ mirrorChars.put(0x223D, 0x223C); // REVERSED TILDE
+ mirrorChars.put(0x2243, 0x22CD); // ASYMPTOTICALLY EQUAL TO
+ mirrorChars.put(0x2252, 0x2253); // APPROXIMATELY EQUAL TO OR THE IMAGE OF
+ mirrorChars.put(0x2253, 0x2252); // IMAGE OF OR APPROXIMATELY EQUAL TO
+ mirrorChars.put(0x2254, 0x2255); // COLON EQUALS
+ mirrorChars.put(0x2255, 0x2254); // EQUALS COLON
+ mirrorChars.put(0x2264, 0x2265); // LESS-THAN OR EQUAL TO
+ mirrorChars.put(0x2265, 0x2264); // GREATER-THAN OR EQUAL TO
+ mirrorChars.put(0x2266, 0x2267); // LESS-THAN OVER EQUAL TO
+ mirrorChars.put(0x2267, 0x2266); // GREATER-THAN OVER EQUAL TO
+ mirrorChars.put(0x2268, 0x2269); // [BEST FIT] LESS-THAN BUT NOT EQUAL TO
+ mirrorChars.put(0x2269, 0x2268); // [BEST FIT] GREATER-THAN BUT NOT EQUAL TO
+ mirrorChars.put(0x226A, 0x226B); // MUCH LESS-THAN
+ mirrorChars.put(0x226B, 0x226A); // MUCH GREATER-THAN
+ mirrorChars.put(0x226E, 0x226F); // [BEST FIT] NOT LESS-THAN
+ mirrorChars.put(0x226F, 0x226E); // [BEST FIT] NOT GREATER-THAN
+ mirrorChars.put(0x2270, 0x2271); // [BEST FIT] NEITHER LESS-THAN NOR EQUAL TO
+ mirrorChars.put(0x2271, 0x2270); // [BEST FIT] NEITHER GREATER-THAN NOR EQUAL TO
+ mirrorChars.put(0x2272, 0x2273); // [BEST FIT] LESS-THAN OR EQUIVALENT TO
+ mirrorChars.put(0x2273, 0x2272); // [BEST FIT] GREATER-THAN OR EQUIVALENT TO
+ mirrorChars.put(0x2274, 0x2275); // [BEST FIT] NEITHER LESS-THAN NOR EQUIVALENT TO
+ mirrorChars.put(0x2275, 0x2274); // [BEST FIT] NEITHER GREATER-THAN NOR EQUIVALENT TO
+ mirrorChars.put(0x2276, 0x2277); // LESS-THAN OR GREATER-THAN
+ mirrorChars.put(0x2277, 0x2276); // GREATER-THAN OR LESS-THAN
+ mirrorChars.put(0x2278, 0x2279); // NEITHER LESS-THAN NOR GREATER-THAN
+ mirrorChars.put(0x2279, 0x2278); // NEITHER GREATER-THAN NOR LESS-THAN
+ mirrorChars.put(0x227A, 0x227B); // PRECEDES
+ mirrorChars.put(0x227B, 0x227A); // SUCCEEDS
+ mirrorChars.put(0x227C, 0x227D); // PRECEDES OR EQUAL TO
+ mirrorChars.put(0x227D, 0x227C); // SUCCEEDS OR EQUAL TO
+ mirrorChars.put(0x227E, 0x227F); // [BEST FIT] PRECEDES OR EQUIVALENT TO
+ mirrorChars.put(0x227F, 0x227E); // [BEST FIT] SUCCEEDS OR EQUIVALENT TO
+ mirrorChars.put(0x2280, 0x2281); // [BEST FIT] DOES NOT PRECEDE
+ mirrorChars.put(0x2281, 0x2280); // [BEST FIT] DOES NOT SUCCEED
+ mirrorChars.put(0x2282, 0x2283); // SUBSET OF
+ mirrorChars.put(0x2283, 0x2282); // SUPERSET OF
+ mirrorChars.put(0x2284, 0x2285); // [BEST FIT] NOT A SUBSET OF
+ mirrorChars.put(0x2285, 0x2284); // [BEST FIT] NOT A SUPERSET OF
+ mirrorChars.put(0x2286, 0x2287); // SUBSET OF OR EQUAL TO
+ mirrorChars.put(0x2287, 0x2286); // SUPERSET OF OR EQUAL TO
+ mirrorChars.put(0x2288, 0x2289); // [BEST FIT] NEITHER A SUBSET OF NOR EQUAL TO
+ mirrorChars.put(0x2289, 0x2288); // [BEST FIT] NEITHER A SUPERSET OF NOR EQUAL TO
+ mirrorChars.put(0x228A, 0x228B); // [BEST FIT] SUBSET OF WITH NOT EQUAL TO
+ mirrorChars.put(0x228B, 0x228A); // [BEST FIT] SUPERSET OF WITH NOT EQUAL TO
+ mirrorChars.put(0x228F, 0x2290); // SQUARE IMAGE OF
+ mirrorChars.put(0x2290, 0x228F); // SQUARE ORIGINAL OF
+ mirrorChars.put(0x2291, 0x2292); // SQUARE IMAGE OF OR EQUAL TO
+ mirrorChars.put(0x2292, 0x2291); // SQUARE ORIGINAL OF OR EQUAL TO
+ mirrorChars.put(0x2298, 0x29B8); // CIRCLED DIVISION SLASH
+ mirrorChars.put(0x22A2, 0x22A3); // RIGHT TACK
+ mirrorChars.put(0x22A3, 0x22A2); // LEFT TACK
+ mirrorChars.put(0x22A6, 0x2ADE); // ASSERTION
+ mirrorChars.put(0x22A8, 0x2AE4); // TRUE
+ mirrorChars.put(0x22A9, 0x2AE3); // FORCES
+ mirrorChars.put(0x22AB, 0x2AE5); // DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+ mirrorChars.put(0x22B0, 0x22B1); // PRECEDES UNDER RELATION
+ mirrorChars.put(0x22B1, 0x22B0); // SUCCEEDS UNDER RELATION
+ mirrorChars.put(0x22B2, 0x22B3); // NORMAL SUBGROUP OF
+ mirrorChars.put(0x22B3, 0x22B2); // CONTAINS AS NORMAL SUBGROUP
+ mirrorChars.put(0x22B4, 0x22B5); // NORMAL SUBGROUP OF OR EQUAL TO
+ mirrorChars.put(0x22B5, 0x22B4); // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
+ mirrorChars.put(0x22B6, 0x22B7); // ORIGINAL OF
+ mirrorChars.put(0x22B7, 0x22B6); // IMAGE OF
+ mirrorChars.put(0x22C9, 0x22CA); // LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
+ mirrorChars.put(0x22CA, 0x22C9); // RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
+ mirrorChars.put(0x22CB, 0x22CC); // LEFT SEMIDIRECT PRODUCT
+ mirrorChars.put(0x22CC, 0x22CB); // RIGHT SEMIDIRECT PRODUCT
+ mirrorChars.put(0x22CD, 0x2243); // REVERSED TILDE EQUALS
+ mirrorChars.put(0x22D0, 0x22D1); // DOUBLE SUBSET
+ mirrorChars.put(0x22D1, 0x22D0); // DOUBLE SUPERSET
+ mirrorChars.put(0x22D6, 0x22D7); // LESS-THAN WITH DOT
+ mirrorChars.put(0x22D7, 0x22D6); // GREATER-THAN WITH DOT
+ mirrorChars.put(0x22D8, 0x22D9); // VERY MUCH LESS-THAN
+ mirrorChars.put(0x22D9, 0x22D8); // VERY MUCH GREATER-THAN
+ mirrorChars.put(0x22DA, 0x22DB); // LESS-THAN EQUAL TO OR GREATER-THAN
+ mirrorChars.put(0x22DB, 0x22DA); // GREATER-THAN EQUAL TO OR LESS-THAN
+ mirrorChars.put(0x22DC, 0x22DD); // EQUAL TO OR LESS-THAN
+ mirrorChars.put(0x22DD, 0x22DC); // EQUAL TO OR GREATER-THAN
+ mirrorChars.put(0x22DE, 0x22DF); // EQUAL TO OR PRECEDES
+ mirrorChars.put(0x22DF, 0x22DE); // EQUAL TO OR SUCCEEDS
+ mirrorChars.put(0x22E0, 0x22E1); // [BEST FIT] DOES NOT PRECEDE OR EQUAL
+ mirrorChars.put(0x22E1, 0x22E0); // [BEST FIT] DOES NOT SUCCEED OR EQUAL
+ mirrorChars.put(0x22E2, 0x22E3); // [BEST FIT] NOT SQUARE IMAGE OF OR EQUAL TO
+ mirrorChars.put(0x22E3, 0x22E2); // [BEST FIT] NOT SQUARE ORIGINAL OF OR EQUAL TO
+ mirrorChars.put(0x22E4, 0x22E5); // [BEST FIT] SQUARE IMAGE OF OR NOT EQUAL TO
+ mirrorChars.put(0x22E5, 0x22E4); // [BEST FIT] SQUARE ORIGINAL OF OR NOT EQUAL TO
+ mirrorChars.put(0x22E6, 0x22E7); // [BEST FIT] LESS-THAN BUT NOT EQUIVALENT TO
+ mirrorChars.put(0x22E7, 0x22E6); // [BEST FIT] GREATER-THAN BUT NOT EQUIVALENT TO
+ mirrorChars.put(0x22E8, 0x22E9); // [BEST FIT] PRECEDES BUT NOT EQUIVALENT TO
+ mirrorChars.put(0x22E9, 0x22E8); // [BEST FIT] SUCCEEDS BUT NOT EQUIVALENT TO
+ mirrorChars.put(0x22EA, 0x22EB); // [BEST FIT] NOT NORMAL SUBGROUP OF
+ mirrorChars.put(0x22EB, 0x22EA); // [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP
+ mirrorChars.put(0x22EC, 0x22ED); // [BEST FIT] NOT NORMAL SUBGROUP OF OR EQUAL TO
+ mirrorChars.put(0x22ED, 0x22EC); // [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+ mirrorChars.put(0x22F0, 0x22F1); // UP RIGHT DIAGONAL ELLIPSIS
+ mirrorChars.put(0x22F1, 0x22F0); // DOWN RIGHT DIAGONAL ELLIPSIS
+ mirrorChars.put(0x22F2, 0x22FA); // ELEMENT OF WITH LONG HORIZONTAL STROKE
+ mirrorChars.put(0x22F3, 0x22FB); // ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ mirrorChars.put(0x22F4, 0x22FC); // SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ mirrorChars.put(0x22F6, 0x22FD); // ELEMENT OF WITH OVERBAR
+ mirrorChars.put(0x22F7, 0x22FE); // SMALL ELEMENT OF WITH OVERBAR
+ mirrorChars.put(0x22FA, 0x22F2); // CONTAINS WITH LONG HORIZONTAL STROKE
+ mirrorChars.put(0x22FB, 0x22F3); // CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ mirrorChars.put(0x22FC, 0x22F4); // SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ mirrorChars.put(0x22FD, 0x22F6); // CONTAINS WITH OVERBAR
+ mirrorChars.put(0x22FE, 0x22F7); // SMALL CONTAINS WITH OVERBAR
+ mirrorChars.put(0x2308, 0x2309); // LEFT CEILING
+ mirrorChars.put(0x2309, 0x2308); // RIGHT CEILING
+ mirrorChars.put(0x230A, 0x230B); // LEFT FLOOR
+ mirrorChars.put(0x230B, 0x230A); // RIGHT FLOOR
+ mirrorChars.put(0x2329, 0x232A); // LEFT-POINTING ANGLE BRACKET
+ mirrorChars.put(0x232A, 0x2329); // RIGHT-POINTING ANGLE BRACKET
+ mirrorChars.put(0x2768, 0x2769); // MEDIUM LEFT PARENTHESIS ORNAMENT
+ mirrorChars.put(0x2769, 0x2768); // MEDIUM RIGHT PARENTHESIS ORNAMENT
+ mirrorChars.put(0x276A, 0x276B); // MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT
+ mirrorChars.put(0x276B, 0x276A); // MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT
+ mirrorChars.put(0x276C, 0x276D); // MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT
+ mirrorChars.put(0x276D, 0x276C); // MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT
+ mirrorChars.put(0x276E, 0x276F); // HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT
+ mirrorChars.put(0x276F, 0x276E); // HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT
+ mirrorChars.put(0x2770, 0x2771); // HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT
+ mirrorChars.put(0x2771, 0x2770); // HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT
+ mirrorChars.put(0x2772, 0x2773); // LIGHT LEFT TORTOISE SHELL BRACKET
+ mirrorChars.put(0x2773, 0x2772); // LIGHT RIGHT TORTOISE SHELL BRACKET
+ mirrorChars.put(0x2774, 0x2775); // MEDIUM LEFT CURLY BRACKET ORNAMENT
+ mirrorChars.put(0x2775, 0x2774); // MEDIUM RIGHT CURLY BRACKET ORNAMENT
+ mirrorChars.put(0x27D5, 0x27D6); // LEFT OUTER JOIN
+ mirrorChars.put(0x27D6, 0x27D5); // RIGHT OUTER JOIN
+ mirrorChars.put(0x27DD, 0x27DE); // LONG RIGHT TACK
+ mirrorChars.put(0x27DE, 0x27DD); // LONG LEFT TACK
+ mirrorChars.put(0x27E2, 0x27E3); // WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK
+ mirrorChars.put(0x27E3, 0x27E2); // WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK
+ mirrorChars.put(0x27E4, 0x27E5); // WHITE SQUARE WITH LEFTWARDS TICK
+ mirrorChars.put(0x27E5, 0x27E4); // WHITE SQUARE WITH RIGHTWARDS TICK
+ mirrorChars.put(0x27E6, 0x27E7); // MATHEMATICAL LEFT WHITE SQUARE BRACKET
+ mirrorChars.put(0x27E7, 0x27E6); // MATHEMATICAL RIGHT WHITE SQUARE BRACKET
+ mirrorChars.put(0x27E8, 0x27E9); // MATHEMATICAL LEFT ANGLE BRACKET
+ mirrorChars.put(0x27E9, 0x27E8); // MATHEMATICAL RIGHT ANGLE BRACKET
+ mirrorChars.put(0x27EA, 0x27EB); // MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
+ mirrorChars.put(0x27EB, 0x27EA); // MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
+ mirrorChars.put(0x2983, 0x2984); // LEFT WHITE CURLY BRACKET
+ mirrorChars.put(0x2984, 0x2983); // RIGHT WHITE CURLY BRACKET
+ mirrorChars.put(0x2985, 0x2986); // LEFT WHITE PARENTHESIS
+ mirrorChars.put(0x2986, 0x2985); // RIGHT WHITE PARENTHESIS
+ mirrorChars.put(0x2987, 0x2988); // Z NOTATION LEFT IMAGE BRACKET
+ mirrorChars.put(0x2988, 0x2987); // Z NOTATION RIGHT IMAGE BRACKET
+ mirrorChars.put(0x2989, 0x298A); // Z NOTATION LEFT BINDING BRACKET
+ mirrorChars.put(0x298A, 0x2989); // Z NOTATION RIGHT BINDING BRACKET
+ mirrorChars.put(0x298B, 0x298C); // LEFT SQUARE BRACKET WITH UNDERBAR
+ mirrorChars.put(0x298C, 0x298B); // RIGHT SQUARE BRACKET WITH UNDERBAR
+ mirrorChars.put(0x298D, 0x2990); // LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
+ mirrorChars.put(0x298E, 0x298F); // RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+ mirrorChars.put(0x298F, 0x298E); // LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+ mirrorChars.put(0x2990, 0x298D); // RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
+ mirrorChars.put(0x2991, 0x2992); // LEFT ANGLE BRACKET WITH DOT
+ mirrorChars.put(0x2992, 0x2991); // RIGHT ANGLE BRACKET WITH DOT
+ mirrorChars.put(0x2993, 0x2994); // LEFT ARC LESS-THAN BRACKET
+ mirrorChars.put(0x2994, 0x2993); // RIGHT ARC GREATER-THAN BRACKET
+ mirrorChars.put(0x2995, 0x2996); // DOUBLE LEFT ARC GREATER-THAN BRACKET
+ mirrorChars.put(0x2996, 0x2995); // DOUBLE RIGHT ARC LESS-THAN BRACKET
+ mirrorChars.put(0x2997, 0x2998); // LEFT BLACK TORTOISE SHELL BRACKET
+ mirrorChars.put(0x2998, 0x2997); // RIGHT BLACK TORTOISE SHELL BRACKET
+ mirrorChars.put(0x29B8, 0x2298); // CIRCLED REVERSE SOLIDUS
+ mirrorChars.put(0x29C0, 0x29C1); // CIRCLED LESS-THAN
+ mirrorChars.put(0x29C1, 0x29C0); // CIRCLED GREATER-THAN
+ mirrorChars.put(0x29C4, 0x29C5); // SQUARED RISING DIAGONAL SLASH
+ mirrorChars.put(0x29C5, 0x29C4); // SQUARED FALLING DIAGONAL SLASH
+ mirrorChars.put(0x29CF, 0x29D0); // LEFT TRIANGLE BESIDE VERTICAL BAR
+ mirrorChars.put(0x29D0, 0x29CF); // VERTICAL BAR BESIDE RIGHT TRIANGLE
+ mirrorChars.put(0x29D1, 0x29D2); // BOWTIE WITH LEFT HALF BLACK
+ mirrorChars.put(0x29D2, 0x29D1); // BOWTIE WITH RIGHT HALF BLACK
+ mirrorChars.put(0x29D4, 0x29D5); // TIMES WITH LEFT HALF BLACK
+ mirrorChars.put(0x29D5, 0x29D4); // TIMES WITH RIGHT HALF BLACK
+ mirrorChars.put(0x29D8, 0x29D9); // LEFT WIGGLY FENCE
+ mirrorChars.put(0x29D9, 0x29D8); // RIGHT WIGGLY FENCE
+ mirrorChars.put(0x29DA, 0x29DB); // LEFT DOUBLE WIGGLY FENCE
+ mirrorChars.put(0x29DB, 0x29DA); // RIGHT DOUBLE WIGGLY FENCE
+ mirrorChars.put(0x29F5, 0x2215); // REVERSE SOLIDUS OPERATOR
+ mirrorChars.put(0x29F8, 0x29F9); // BIG SOLIDUS
+ mirrorChars.put(0x29F9, 0x29F8); // BIG REVERSE SOLIDUS
+ mirrorChars.put(0x29FC, 0x29FD); // LEFT-POINTING CURVED ANGLE BRACKET
+ mirrorChars.put(0x29FD, 0x29FC); // RIGHT-POINTING CURVED ANGLE BRACKET
+ mirrorChars.put(0x2A2B, 0x2A2C); // MINUS SIGN WITH FALLING DOTS
+ mirrorChars.put(0x2A2C, 0x2A2B); // MINUS SIGN WITH RISING DOTS
+ mirrorChars.put(0x2A2D, 0x2A2C); // PLUS SIGN IN LEFT HALF CIRCLE
+ mirrorChars.put(0x2A2E, 0x2A2D); // PLUS SIGN IN RIGHT HALF CIRCLE
+ mirrorChars.put(0x2A34, 0x2A35); // MULTIPLICATION SIGN IN LEFT HALF CIRCLE
+ mirrorChars.put(0x2A35, 0x2A34); // MULTIPLICATION SIGN IN RIGHT HALF CIRCLE
+ mirrorChars.put(0x2A3C, 0x2A3D); // INTERIOR PRODUCT
+ mirrorChars.put(0x2A3D, 0x2A3C); // RIGHTHAND INTERIOR PRODUCT
+ mirrorChars.put(0x2A64, 0x2A65); // Z NOTATION DOMAIN ANTIRESTRICTION
+ mirrorChars.put(0x2A65, 0x2A64); // Z NOTATION RANGE ANTIRESTRICTION
+ mirrorChars.put(0x2A79, 0x2A7A); // LESS-THAN WITH CIRCLE INSIDE
+ mirrorChars.put(0x2A7A, 0x2A79); // GREATER-THAN WITH CIRCLE INSIDE
+ mirrorChars.put(0x2A7D, 0x2A7E); // LESS-THAN OR SLANTED EQUAL TO
+ mirrorChars.put(0x2A7E, 0x2A7D); // GREATER-THAN OR SLANTED EQUAL TO
+ mirrorChars.put(0x2A7F, 0x2A80); // LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
+ mirrorChars.put(0x2A80, 0x2A7F); // GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
+ mirrorChars.put(0x2A81, 0x2A82); // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
+ mirrorChars.put(0x2A82, 0x2A81); // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
+ mirrorChars.put(0x2A83, 0x2A84); // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT
+ mirrorChars.put(0x2A84, 0x2A83); // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT
+ mirrorChars.put(0x2A8B, 0x2A8C); // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
+ mirrorChars.put(0x2A8C, 0x2A8B); // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
+ mirrorChars.put(0x2A91, 0x2A92); // LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL
+ mirrorChars.put(0x2A92, 0x2A91); // GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL
+ mirrorChars.put(0x2A93, 0x2A94); // LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL
+ mirrorChars.put(0x2A94, 0x2A93); // GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL
+ mirrorChars.put(0x2A95, 0x2A96); // SLANTED EQUAL TO OR LESS-THAN
+ mirrorChars.put(0x2A96, 0x2A95); // SLANTED EQUAL TO OR GREATER-THAN
+ mirrorChars.put(0x2A97, 0x2A98); // SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE
+ mirrorChars.put(0x2A98, 0x2A97); // SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE
+ mirrorChars.put(0x2A99, 0x2A9A); // DOUBLE-LINE EQUAL TO OR LESS-THAN
+ mirrorChars.put(0x2A9A, 0x2A99); // DOUBLE-LINE EQUAL TO OR GREATER-THAN
+ mirrorChars.put(0x2A9B, 0x2A9C); // DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN
+ mirrorChars.put(0x2A9C, 0x2A9B); // DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN
+ mirrorChars.put(0x2AA1, 0x2AA2); // DOUBLE NESTED LESS-THAN
+ mirrorChars.put(0x2AA2, 0x2AA1); // DOUBLE NESTED GREATER-THAN
+ mirrorChars.put(0x2AA6, 0x2AA7); // LESS-THAN CLOSED BY CURVE
+ mirrorChars.put(0x2AA7, 0x2AA6); // GREATER-THAN CLOSED BY CURVE
+ mirrorChars.put(0x2AA8, 0x2AA9); // LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
+ mirrorChars.put(0x2AA9, 0x2AA8); // GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
+ mirrorChars.put(0x2AAA, 0x2AAB); // SMALLER THAN
+ mirrorChars.put(0x2AAB, 0x2AAA); // LARGER THAN
+ mirrorChars.put(0x2AAC, 0x2AAD); // SMALLER THAN OR EQUAL TO
+ mirrorChars.put(0x2AAD, 0x2AAC); // LARGER THAN OR EQUAL TO
+ mirrorChars.put(0x2AAF, 0x2AB0); // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
+ mirrorChars.put(0x2AB0, 0x2AAF); // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
+ mirrorChars.put(0x2AB3, 0x2AB4); // PRECEDES ABOVE EQUALS SIGN
+ mirrorChars.put(0x2AB4, 0x2AB3); // SUCCEEDS ABOVE EQUALS SIGN
+ mirrorChars.put(0x2ABB, 0x2ABC); // DOUBLE PRECEDES
+ mirrorChars.put(0x2ABC, 0x2ABB); // DOUBLE SUCCEEDS
+ mirrorChars.put(0x2ABD, 0x2ABE); // SUBSET WITH DOT
+ mirrorChars.put(0x2ABE, 0x2ABD); // SUPERSET WITH DOT
+ mirrorChars.put(0x2ABF, 0x2AC0); // SUBSET WITH PLUS SIGN BELOW
+ mirrorChars.put(0x2AC0, 0x2ABF); // SUPERSET WITH PLUS SIGN BELOW
+ mirrorChars.put(0x2AC1, 0x2AC2); // SUBSET WITH MULTIPLICATION SIGN BELOW
+ mirrorChars.put(0x2AC2, 0x2AC1); // SUPERSET WITH MULTIPLICATION SIGN BELOW
+ mirrorChars.put(0x2AC3, 0x2AC4); // SUBSET OF OR EQUAL TO WITH DOT ABOVE
+ mirrorChars.put(0x2AC4, 0x2AC3); // SUPERSET OF OR EQUAL TO WITH DOT ABOVE
+ mirrorChars.put(0x2AC5, 0x2AC6); // SUBSET OF ABOVE EQUALS SIGN
+ mirrorChars.put(0x2AC6, 0x2AC5); // SUPERSET OF ABOVE EQUALS SIGN
+ mirrorChars.put(0x2ACD, 0x2ACE); // SQUARE LEFT OPEN BOX OPERATOR
+ mirrorChars.put(0x2ACE, 0x2ACD); // SQUARE RIGHT OPEN BOX OPERATOR
+ mirrorChars.put(0x2ACF, 0x2AD0); // CLOSED SUBSET
+ mirrorChars.put(0x2AD0, 0x2ACF); // CLOSED SUPERSET
+ mirrorChars.put(0x2AD1, 0x2AD2); // CLOSED SUBSET OR EQUAL TO
+ mirrorChars.put(0x2AD2, 0x2AD1); // CLOSED SUPERSET OR EQUAL TO
+ mirrorChars.put(0x2AD3, 0x2AD4); // SUBSET ABOVE SUPERSET
+ mirrorChars.put(0x2AD4, 0x2AD3); // SUPERSET ABOVE SUBSET
+ mirrorChars.put(0x2AD5, 0x2AD6); // SUBSET ABOVE SUBSET
+ mirrorChars.put(0x2AD6, 0x2AD5); // SUPERSET ABOVE SUPERSET
+ mirrorChars.put(0x2ADE, 0x22A6); // SHORT LEFT TACK
+ mirrorChars.put(0x2AE3, 0x22A9); // DOUBLE VERTICAL BAR LEFT TURNSTILE
+ mirrorChars.put(0x2AE4, 0x22A8); // VERTICAL BAR DOUBLE LEFT TURNSTILE
+ mirrorChars.put(0x2AE5, 0x22AB); // DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE
+ mirrorChars.put(0x2AEC, 0x2AED); // DOUBLE STROKE NOT SIGN
+ mirrorChars.put(0x2AED, 0x2AEC); // REVERSED DOUBLE STROKE NOT SIGN
+ mirrorChars.put(0x2AF7, 0x2AF8); // TRIPLE NESTED LESS-THAN
+ mirrorChars.put(0x2AF8, 0x2AF7); // TRIPLE NESTED GREATER-THAN
+ mirrorChars.put(0x2AF9, 0x2AFA); // DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO
+ mirrorChars.put(0x2AFA, 0x2AF9); // DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO
+ mirrorChars.put(0x3008, 0x3009); // LEFT ANGLE BRACKET
+ mirrorChars.put(0x3009, 0x3008); // RIGHT ANGLE BRACKET
+ mirrorChars.put(0x300A, 0x300B); // LEFT DOUBLE ANGLE BRACKET
+ mirrorChars.put(0x300B, 0x300A); // RIGHT DOUBLE ANGLE BRACKET
+ mirrorChars.put(0x300C, 0x300D); // [BEST FIT] LEFT CORNER BRACKET
+ mirrorChars.put(0x300D, 0x300C); // [BEST FIT] RIGHT CORNER BRACKET
+ mirrorChars.put(0x300E, 0x300F); // [BEST FIT] LEFT WHITE CORNER BRACKET
+ mirrorChars.put(0x300F, 0x300E); // [BEST FIT] RIGHT WHITE CORNER BRACKET
+ mirrorChars.put(0x3010, 0x3011); // LEFT BLACK LENTICULAR BRACKET
+ mirrorChars.put(0x3011, 0x3010); // RIGHT BLACK LENTICULAR BRACKET
+ mirrorChars.put(0x3014, 0x3015); // LEFT TORTOISE SHELL BRACKET
+ mirrorChars.put(0x3015, 0x3014); // RIGHT TORTOISE SHELL BRACKET
+ mirrorChars.put(0x3016, 0x3017); // LEFT WHITE LENTICULAR BRACKET
+ mirrorChars.put(0x3017, 0x3016); // RIGHT WHITE LENTICULAR BRACKET
+ mirrorChars.put(0x3018, 0x3019); // LEFT WHITE TORTOISE SHELL BRACKET
+ mirrorChars.put(0x3019, 0x3018); // RIGHT WHITE TORTOISE SHELL BRACKET
+ mirrorChars.put(0x301A, 0x301B); // LEFT WHITE SQUARE BRACKET
+ mirrorChars.put(0x301B, 0x301A); // RIGHT WHITE SQUARE BRACKET
+ mirrorChars.put(0xFF08, 0xFF09); // FULLWIDTH LEFT PARENTHESIS
+ mirrorChars.put(0xFF09, 0xFF08); // FULLWIDTH RIGHT PARENTHESIS
+ mirrorChars.put(0xFF1C, 0xFF1E); // FULLWIDTH LESS-THAN SIGN
+ mirrorChars.put(0xFF1E, 0xFF1C); // FULLWIDTH GREATER-THAN SIGN
+ mirrorChars.put(0xFF3B, 0xFF3D); // FULLWIDTH LEFT SQUARE BRACKET
+ mirrorChars.put(0xFF3D, 0xFF3B); // FULLWIDTH RIGHT SQUARE BRACKET
+ mirrorChars.put(0xFF5B, 0xFF5D); // FULLWIDTH LEFT CURLY BRACKET
+ mirrorChars.put(0xFF5D, 0xFF5B); // FULLWIDTH RIGHT CURLY BRACKET
+ mirrorChars.put(0xFF5F, 0xFF60); // FULLWIDTH LEFT WHITE PARENTHESIS
+ mirrorChars.put(0xFF60, 0xFF5F); // FULLWIDTH RIGHT WHITE PARENTHESIS
+ mirrorChars.put(0xFF62, 0xFF63); // [BEST FIT] HALFWIDTH LEFT CORNER BRACKET
+ mirrorChars.put(0xFF63, 0xFF62); // [BEST FIT] HALFWIDTH RIGHT CORNER BRACKET
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/BidiOrder.java b/src/main/java/com/lowagie/text/pdf/BidiOrder.java
new file mode 100644
index 0000000..97f9f74
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/BidiOrder.java
@@ -0,0 +1,1240 @@
+package com.lowagie.text.pdf;
+
+/*
+ *
+ * Copyright 2003 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+/*
+ * (C) Copyright IBM Corp. 1999, All Rights Reserved
+ *
+ * version 1.1
+ */
+
+/**
+ * Reference implementation of the Unicode 3.0 Bidi algorithm.
+ *
+ *
+ * There are two levels of input to the algorithm, since clients may prefer
+ * to supply some information from out-of-band sources rather than relying on
+ * the default behavior.
+ *
+ *
+ *
+ * Output is separated into several stages as well, to better enable clients
+ * to evaluate various aspects of implementation conformance.
+ *
+ *
+ * Note that for conformance, algorithms are only required to generate correct
+ * reordering and character directionality (odd or even levels) over a line.
+ * Generating identical level arrays over a line is not required. Bidi
+ * explicit format codes (LRE, RLE, LRO, RLO, PDF) and BN can be assigned
+ * arbitrary levels and positions as long as the other text matches.
+ *
+ * Rule L1.
+ * getReordering(linebreaks)[linebreaks[1] + 4]
+ * (linebreaks[1] is the position after the last character of the
+ * second line, which is also the index of the first character on the
+ * third line, and adding four gets the fifth character from the left).
+ * StringBuffer
but works with byte
arrays.
+ * Floating point is converted to a format suitable to the PDF.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+
+public class ByteBuffer extends OutputStream {
+ /** The count of bytes in the buffer. */
+ protected int count;
+
+ /** The buffer where the bytes are stored. */
+ protected byte buf[];
+
+ private static int byteCacheSize = 0;
+
+ private static byte[][] byteCache = new byte[byteCacheSize][];
+ public static byte ZERO = (byte)'0';
+ private static final char[] chars = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+ private static final byte[] bytes = new byte[] {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102};
+ /**
+ * If true
always output floating point numbers with 6 decimal digits.
+ * If false
uses the faster, although less precise, representation.
+ */
+ public static boolean HIGH_PRECISION = false;
+ private static final DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
+
+ /** Creates new ByteBuffer with capacity 128 */
+ public ByteBuffer() {
+ this(128);
+ }
+
+ /**
+ * Creates a byte buffer with a certain capacity.
+ * @param size the initial capacity
+ */
+ public ByteBuffer(int size) {
+ if (size < 1)
+ size = 128;
+ buf = new byte[size];
+ }
+
+ /**
+ * Sets the cache size.
+ * int
. The size of the array will grow by one.
+ * @param b the int to be appended
+ * @return a reference to this ByteBuffer
object
+ */
+ public ByteBuffer append_i(int b) {
+ int newcount = count + 1;
+ if (newcount > buf.length) {
+ byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
+ System.arraycopy(buf, 0, newbuf, 0, count);
+ buf = newbuf;
+ }
+ buf[count] = (byte)b;
+ count = newcount;
+ return this;
+ }
+
+ /**
+ * Appends the subarray of the byte
array. The buffer will grow by
+ * len
bytes.
+ * @param b the array to be appended
+ * @param off the offset to the start of the array
+ * @param len the length of bytes to append
+ * @return a reference to this ByteBuffer
object
+ */
+ public ByteBuffer append(byte b[], int off, int len) {
+ if ((off < 0) || (off > b.length) || (len < 0) ||
+ ((off + len) > b.length) || ((off + len) < 0) || len == 0)
+ return this;
+ int newcount = count + len;
+ if (newcount > buf.length) {
+ byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
+ System.arraycopy(buf, 0, newbuf, 0, count);
+ buf = newbuf;
+ }
+ System.arraycopy(b, off, buf, count, len);
+ count = newcount;
+ return this;
+ }
+
+ /**
+ * Appends an array of bytes.
+ * @param b the array to be appended
+ * @return a reference to this ByteBuffer
object
+ */
+ public ByteBuffer append(byte b[]) {
+ return append(b, 0, b.length);
+ }
+
+ /**
+ * Appends a String
to the buffer. The String
is
+ * converted according to the encoding ISO-8859-1.
+ * @param str the String
to be appended
+ * @return a reference to this ByteBuffer
object
+ */
+ public ByteBuffer append(String str) {
+ if (str != null)
+ return append(DocWriter.getISOBytes(str));
+ return this;
+ }
+
+ /**
+ * Appends a char
to the buffer. The char
is
+ * converted according to the encoding ISO-8859-1.
+ * @param c the char
to be appended
+ * @return a reference to this ByteBuffer
object
+ */
+ public ByteBuffer append(char c) {
+ return append_i(c);
+ }
+
+ /**
+ * Appends another ByteBuffer
to this buffer.
+ * @param buf the ByteBuffer
to be appended
+ * @return a reference to this ByteBuffer
object
+ */
+ public ByteBuffer append(ByteBuffer buf) {
+ return append(buf.buf, 0, buf.count);
+ }
+
+ /**
+ * Appends the string representation of an int
.
+ * @param i the int
to be appended
+ * @return a reference to this ByteBuffer
object
+ */
+ public ByteBuffer append(int i) {
+ return append((double)i);
+ }
+
+ public ByteBuffer append(byte b) {
+ return append_i(b);
+ }
+
+ public ByteBuffer appendHex(byte b) {
+ append(bytes[(b >> 4) & 0x0f]);
+ return append(bytes[b & 0x0f]);
+ }
+
+ /**
+ * Appends a string representation of a float
according
+ * to the Pdf conventions.
+ * @param i the float
to be appended
+ * @return a reference to this ByteBuffer
object
+ */
+ public ByteBuffer append(float i) {
+ return append((double)i);
+ }
+
+ /**
+ * Appends a string representation of a double
according
+ * to the Pdf conventions.
+ * @param d the double
to be appended
+ * @return a reference to this ByteBuffer
object
+ */
+ public ByteBuffer append(double d) {
+ append(formatDouble(d, this));
+ return this;
+ }
+
+ /**
+ * Outputs a double
into a format suitable for the PDF.
+ * @param d a double
+ * @return the String
representation of the double
+ */
+ public static String formatDouble(double d) {
+ return formatDouble(d, null);
+ }
+
+ /**
+ * Outputs a double
into a format suitable for the PDF.
+ * @param d a double
+ * @param buf a ByteBuffer
+ * @return the String
representation of the double
if
+ * buf
is null
. If buf
is not null
,
+ * then the double is appended directly to the buffer and this methods returns null
.
+ */
+ public static String formatDouble(double d, ByteBuffer buf) {
+ if (HIGH_PRECISION) {
+ DecimalFormat dn = new DecimalFormat("0.######", dfs);
+ String sform = dn.format(d);
+ if (buf == null)
+ return sform;
+ else {
+ buf.append(sform);
+ return null;
+ }
+ }
+ boolean negative = false;
+ if (Math.abs(d) < 0.000015) {
+ if (buf != null) {
+ buf.append(ZERO);
+ return null;
+ } else {
+ return "0";
+ }
+ }
+ if (d < 0) {
+ negative = true;
+ d = -d;
+ }
+ if (d < 1.0) {
+ d += 0.000005;
+ if (d >= 1) {
+ if (negative) {
+ if (buf != null) {
+ buf.append((byte)'-');
+ buf.append((byte)'1');
+ return null;
+ } else {
+ return "-1";
+ }
+ } else {
+ if (buf != null) {
+ buf.append((byte)'1');
+ return null;
+ } else {
+ return "1";
+ }
+ }
+ }
+ if (buf != null) {
+ int v = (int) (d * 100000);
+
+ if (negative) buf.append((byte)'-');
+ buf.append((byte)'0');
+ buf.append((byte)'.');
+
+ buf.append( (byte)(v / 10000 + ZERO) );
+ if (v % 10000 != 0) {
+ buf.append( (byte)((v / 1000) % 10 + ZERO) );
+ if (v % 1000 != 0) {
+ buf.append( (byte)((v / 100) % 10 + ZERO) );
+ if (v % 100 != 0) {
+ buf.append((byte)((v / 10) % 10 + ZERO) );
+ if (v % 10 != 0) {
+ buf.append((byte)((v) % 10 + ZERO) );
+ }
+ }
+ }
+ }
+ return null;
+ } else {
+ int x = 100000;
+ int v = (int) (d * x);
+
+ StringBuffer res = new StringBuffer();
+ if (negative) res.append('-');
+ res.append("0.");
+
+ while( v < x/10 ) {
+ res.append('0');
+ x /= 10;
+ }
+ res.append(v);
+ int cut = res.length() - 1;
+ while (res.charAt(cut) == '0') {
+ --cut;
+ }
+ res.setLength(cut + 1);
+ return res.toString();
+ }
+ } else if (d <= 32767) {
+ d += 0.005;
+ int v = (int) (d * 100);
+
+ if (v < byteCacheSize && byteCache[v] != null) {
+ if (buf != null) {
+ if (negative) buf.append((byte)'-');
+ buf.append(byteCache[v]);
+ return null;
+ } else {
+ String tmp = PdfEncodings.convertToString(byteCache[v], null);
+ if (negative) tmp = "-" + tmp;
+ return tmp;
+ }
+ }
+ if (buf != null) {
+ if (v < byteCacheSize) {
+ //create the cachebyte[]
+ byte[] cache;
+ int size = 0;
+ if (v >= 1000000) {
+ //the original number is >=10000, we need 5 more bytes
+ size += 5;
+ } else if (v >= 100000) {
+ //the original number is >=1000, we need 4 more bytes
+ size += 4;
+ } else if (v >= 10000) {
+ //the original number is >=100, we need 3 more bytes
+ size += 3;
+ } else if (v >= 1000) {
+ //the original number is >=10, we need 2 more bytes
+ size += 2;
+ } else if (v >= 100) {
+ //the original number is >=1, we need 1 more bytes
+ size += 1;
+ }
+
+ //now we must check if we have a decimal number
+ if (v % 100 != 0) {
+ //yes, do not forget the "."
+ size += 2;
+ }
+ if (v % 10 != 0) {
+ size++;
+ }
+ cache = new byte[size];
+ int add = 0;
+ if (v >= 1000000) {
+ cache[add++] = bytes[(v / 1000000)];
+ }
+ if (v >= 100000) {
+ cache[add++] = bytes[(v / 100000) % 10];
+ }
+ if (v >= 10000) {
+ cache[add++] = bytes[(v / 10000) % 10];
+ }
+ if (v >= 1000) {
+ cache[add++] = bytes[(v / 1000) % 10];
+ }
+ if (v >= 100) {
+ cache[add++] = bytes[(v / 100) % 10];
+ }
+
+ if (v % 100 != 0) {
+ cache[add++] = (byte)'.';
+ cache[add++] = bytes[(v / 10) % 10];
+ if (v % 10 != 0) {
+ cache[add++] = bytes[v % 10];
+ }
+ }
+ byteCache[v] = cache;
+ }
+
+ if (negative) buf.append((byte)'-');
+ if (v >= 1000000) {
+ buf.append( bytes[(v / 1000000)] );
+ }
+ if (v >= 100000) {
+ buf.append( bytes[(v / 100000) % 10] );
+ }
+ if (v >= 10000) {
+ buf.append( bytes[(v / 10000) % 10] );
+ }
+ if (v >= 1000) {
+ buf.append( bytes[(v / 1000) % 10] );
+ }
+ if (v >= 100) {
+ buf.append( bytes[(v / 100) % 10] );
+ }
+
+ if (v % 100 != 0) {
+ buf.append((byte)'.');
+ buf.append( bytes[(v / 10) % 10] );
+ if (v % 10 != 0) {
+ buf.append( bytes[v % 10] );
+ }
+ }
+ return null;
+ } else {
+ StringBuffer res = new StringBuffer();
+ if (negative) res.append('-');
+ if (v >= 1000000) {
+ res.append( chars[(v / 1000000)] );
+ }
+ if (v >= 100000) {
+ res.append( chars[(v / 100000) % 10] );
+ }
+ if (v >= 10000) {
+ res.append( chars[(v / 10000) % 10] );
+ }
+ if (v >= 1000) {
+ res.append( chars[(v / 1000) % 10] );
+ }
+ if (v >= 100) {
+ res.append( chars[(v / 100) % 10] );
+ }
+
+ if (v % 100 != 0) {
+ res.append('.');
+ res.append( chars[(v / 10) % 10] );
+ if (v % 10 != 0) {
+ res.append( chars[v % 10] );
+ }
+ }
+ return res.toString();
+ }
+ } else {
+ StringBuffer res = new StringBuffer();
+ if (negative) res.append('-');
+ d += 0.5;
+ long v = (long) d;
+ return res.append(v).toString();
+ }
+ }
+
+ /**
+ * Sets the size to zero.
+ */
+ public void reset() {
+ count = 0;
+ }
+
+ /**
+ * Creates a newly allocated byte array. Its size is the current
+ * size of this output stream and the valid contents of the buffer
+ * have been copied into it.
+ *
+ * @return the current contents of this output stream, as a byte array.
+ */
+ public byte[] toByteArray() {
+ byte newbuf[] = new byte[count];
+ System.arraycopy(buf, 0, newbuf, 0, count);
+ return newbuf;
+ }
+
+ /**
+ * Returns the current size of the buffer.
+ *
+ * @return the value of the count
field, which is the number of valid bytes in this byte buffer.
+ */
+ public int size() {
+ return count;
+ }
+
+ public void setSize(int size) {
+ if (size > count || size < 0)
+ throw new IndexOutOfBoundsException("The new size must be positive and <= of the current size");
+ count = size;
+ }
+
+ /**
+ * Converts the buffer's contents into a string, translating bytes into
+ * characters according to the platform's default character encoding.
+ *
+ * @return String translated from the buffer's contents.
+ */
+ public String toString() {
+ return new String(buf, 0, count);
+ }
+
+ /**
+ * Converts the buffer's contents into a string, translating bytes into
+ * characters according to the specified character encoding.
+ *
+ * @param enc a character-encoding name.
+ * @return String translated from the buffer's contents.
+ * @throws UnsupportedEncodingException
+ * If the named encoding is not supported.
+ */
+ public String toString(String enc) throws UnsupportedEncodingException {
+ return new String(buf, 0, count, enc);
+ }
+
+ /**
+ * Writes the complete contents of this byte buffer output to
+ * the specified output stream argument, as if by calling the output
+ * stream's write method using out.write(buf, 0, count)
.
+ *
+ * @param out the output stream to which to write the data.
+ * @exception IOException if an I/O error occurs.
+ */
+ public void writeTo(OutputStream out) throws IOException {
+ out.write(buf, 0, count);
+ }
+
+ public void write(int b) throws IOException {
+ append((byte)b);
+ }
+
+ public void write(byte[] b, int off, int len) {
+ append(b, off, len);
+ }
+
+ public byte[] getBuffer() {
+ return buf;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/CFFFont.java b/src/main/java/com/lowagie/text/pdf/CFFFont.java
new file mode 100644
index 0000000..2d15f96
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/CFFFont.java
@@ -0,0 +1,1184 @@
+/*
+ *
+ * Copyright 2003 Sivan Toledo
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ */
+
+/*
+ * Comments by Sivan Toledo:
+ * I created this class in order to add to iText the ability to utilize
+ * OpenType fonts with CFF glyphs (these usually have an .otf extension).
+ * The CFF font within the CFF table of the OT font might be either a CID
+ * or a Type1 font. (CFF fonts may also contain multiple fonts; I do not
+ * know if this is allowed in an OT table). The PDF spec, however, only
+ * allow a CID font with an Identity-H or Identity-V encoding. Otherwise,
+ * you are limited to an 8-bit encoding.
+ * Adobe fonts come in both flavors. That is, the OTFs sometimes have
+ * a CID CFF inside (for Japanese fonts), and sometimes a Type1 CFF
+ * (virtually all the others, Latin/Greek/Cyrillic). So to easily use
+ * all the glyphs in the latter, without creating multiple 8-bit encoding,
+ * I wrote this class, whose main purpose is to convert a Type1 font inside
+ * a CFF container (which might include other fonts) into a CID CFF font
+ * that can be directly embeded in the PDF.
+ *
+ * Limitations of the current version:
+ * 1. It does not extract a single CID font from a CFF that contains that
+ * particular CID along with other fonts. The Adobe Japanese OTF's that
+ * I have only have one font in the CFF table, so these can be
+ * embeded in the PDF as is.
+ * 2. It does not yet subset fonts.
+ * 3. It may or may not work on CFF fonts that are not within OTF's.
+ * I didn't try that. In any case, that would probably only be
+ * useful for subsetting CID fonts, not for CFF Type1 fonts (I don't
+ * think there are any available.
+ * I plan to extend the class to support these three features at some
+ * future time.
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * @author stoledo
+ */
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import com.lowagie.text.ExceptionConverter;
+
+public class CFFFont {
+
+ static final String operatorNames[] = {
+ "version", "Notice", "FullName", "FamilyName",
+ "Weight", "FontBBox", "BlueValues", "OtherBlues",
+ "FamilyBlues", "FamilyOtherBlues", "StdHW", "StdVW",
+ "UNKNOWN_12", "UniqueID", "XUID", "charset",
+ "Encoding", "CharStrings", "Private", "Subrs",
+ "defaultWidthX", "nominalWidthX", "UNKNOWN_22", "UNKNOWN_23",
+ "UNKNOWN_24", "UNKNOWN_25", "UNKNOWN_26", "UNKNOWN_27",
+ "UNKNOWN_28", "UNKNOWN_29", "UNKNOWN_30", "UNKNOWN_31",
+ "Copyright", "isFixedPitch", "ItalicAngle", "UnderlinePosition",
+ "UnderlineThickness", "PaintType", "CharstringType", "FontMatrix",
+ "StrokeWidth", "BlueScale", "BlueShift", "BlueFuzz",
+ "StemSnapH", "StemSnapV", "ForceBold", "UNKNOWN_12_15",
+ "UNKNOWN_12_16", "LanguageGroup", "ExpansionFactor", "initialRandomSeed",
+ "SyntheticBase", "PostScript", "BaseFontName", "BaseFontBlend",
+ "UNKNOWN_12_24", "UNKNOWN_12_25", "UNKNOWN_12_26", "UNKNOWN_12_27",
+ "UNKNOWN_12_28", "UNKNOWN_12_29", "ROS", "CIDFontVersion",
+ "CIDFontRevision", "CIDFontType", "CIDCount", "UIDBase",
+ "FDArray", "FDSelect", "FontName"
+ };
+
+ static final String standardStrings[] = {
+ // Automatically generated from Appendix A of the CFF specification; do
+ // not edit. Size should be 391.
+ ".notdef", "space", "exclam", "quotedbl", "numbersign", "dollar",
+ "percent", "ampersand", "quoteright", "parenleft", "parenright",
+ "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one",
+ "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon",
+ "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C",
+ "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
+ "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash",
+ "bracketright", "asciicircum", "underscore", "quoteleft", "a", "b", "c",
+ "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
+ "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright",
+ "asciitilde", "exclamdown", "cent", "sterling", "fraction", "yen",
+ "florin", "section", "currency", "quotesingle", "quotedblleft",
+ "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", "endash",
+ "dagger", "daggerdbl", "periodcentered", "paragraph", "bullet",
+ "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright",
+ "ellipsis", "perthousand", "questiondown", "grave", "acute", "circumflex",
+ "tilde", "macron", "breve", "dotaccent", "dieresis", "ring", "cedilla",
+ "hungarumlaut", "ogonek", "caron", "emdash", "AE", "ordfeminine", "Lslash",
+ "Oslash", "OE", "ordmasculine", "ae", "dotlessi", "lslash", "oslash", "oe",
+ "germandbls", "onesuperior", "logicalnot", "mu", "trademark", "Eth",
+ "onehalf", "plusminus", "Thorn", "onequarter", "divide", "brokenbar",
+ "degree", "thorn", "threequarters", "twosuperior", "registered", "minus",
+ "eth", "multiply", "threesuperior", "copyright", "Aacute", "Acircumflex",
+ "Adieresis", "Agrave", "Aring", "Atilde", "Ccedilla", "Eacute",
+ "Ecircumflex", "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis",
+ "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis", "Ograve",
+ "Otilde", "Scaron", "Uacute", "Ucircumflex", "Udieresis", "Ugrave",
+ "Yacute", "Ydieresis", "Zcaron", "aacute", "acircumflex", "adieresis",
+ "agrave", "aring", "atilde", "ccedilla", "eacute", "ecircumflex",
+ "edieresis", "egrave", "iacute", "icircumflex", "idieresis", "igrave",
+ "ntilde", "oacute", "ocircumflex", "odieresis", "ograve", "otilde",
+ "scaron", "uacute", "ucircumflex", "udieresis", "ugrave", "yacute",
+ "ydieresis", "zcaron", "exclamsmall", "Hungarumlautsmall",
+ "dollaroldstyle", "dollarsuperior", "ampersandsmall", "Acutesmall",
+ "parenleftsuperior", "parenrightsuperior", "twodotenleader",
+ "onedotenleader", "zerooldstyle", "oneoldstyle", "twooldstyle",
+ "threeoldstyle", "fouroldstyle", "fiveoldstyle", "sixoldstyle",
+ "sevenoldstyle", "eightoldstyle", "nineoldstyle", "commasuperior",
+ "threequartersemdash", "periodsuperior", "questionsmall", "asuperior",
+ "bsuperior", "centsuperior", "dsuperior", "esuperior", "isuperior",
+ "lsuperior", "msuperior", "nsuperior", "osuperior", "rsuperior",
+ "ssuperior", "tsuperior", "ff", "ffi", "ffl", "parenleftinferior",
+ "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
+ "Asmall", "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall", "Gsmall",
+ "Hsmall", "Ismall", "Jsmall", "Ksmall", "Lsmall", "Msmall", "Nsmall",
+ "Osmall", "Psmall", "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall",
+ "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall", "colonmonetary",
+ "onefitted", "rupiah", "Tildesmall", "exclamdownsmall", "centoldstyle",
+ "Lslashsmall", "Scaronsmall", "Zcaronsmall", "Dieresissmall", "Brevesmall",
+ "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash",
+ "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
+ "questiondownsmall", "oneeighth", "threeeighths", "fiveeighths",
+ "seveneighths", "onethird", "twothirds", "zerosuperior", "foursuperior",
+ "fivesuperior", "sixsuperior", "sevensuperior", "eightsuperior",
+ "ninesuperior", "zeroinferior", "oneinferior", "twoinferior",
+ "threeinferior", "fourinferior", "fiveinferior", "sixinferior",
+ "seveninferior", "eightinferior", "nineinferior", "centinferior",
+ "dollarinferior", "periodinferior", "commainferior", "Agravesmall",
+ "Aacutesmall", "Acircumflexsmall", "Atildesmall", "Adieresissmall",
+ "Aringsmall", "AEsmall", "Ccedillasmall", "Egravesmall", "Eacutesmall",
+ "Ecircumflexsmall", "Edieresissmall", "Igravesmall", "Iacutesmall",
+ "Icircumflexsmall", "Idieresissmall", "Ethsmall", "Ntildesmall",
+ "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
+ "Odieresissmall", "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
+ "Ucircumflexsmall", "Udieresissmall", "Yacutesmall", "Thornsmall",
+ "Ydieresissmall", "001.000", "001.001", "001.002", "001.003", "Black",
+ "Bold", "Book", "Light", "Medium", "Regular", "Roman", "Semibold"
+ };
+
+ //private String[] strings;
+ public String getString(char sid) {
+ if (sid < standardStrings.length) return standardStrings[sid];
+ if (sid >= standardStrings.length+(stringOffsets.length-1)) return null;
+ int j = sid - standardStrings.length;
+ //java.lang.System.err.println("going for "+j);
+ int p = getPosition();
+ seek(stringOffsets[j]);
+ StringBuffer s = new StringBuffer();
+ for (int k=stringOffsets[j]; kfalse
. CJK font and not embedded
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ CJKFont(String fontName, String enc, boolean emb) throws DocumentException, IOException {
+ loadProperties();
+ fontType = FONT_TYPE_CJK;
+ String nameBase = getBaseName(fontName);
+ if (!isCJKFont(nameBase, enc))
+ throw new DocumentException("Font '" + fontName + "' with '" + enc + "' encoding is not a CJK font.");
+ if (nameBase.length() < fontName.length()) {
+ style = fontName.substring(nameBase.length());
+ fontName = nameBase;
+ }
+ this.fontName = fontName;
+ encoding = CJK_ENCODING;
+ vertical = enc.endsWith("V");
+ CMap = enc;
+ if (enc.startsWith("Identity-")) {
+ cidDirect = true;
+ String s = cjkFonts.getProperty(fontName);
+ s = s.substring(0, s.indexOf('_'));
+ char c[] = (char[])allCMaps.get(s);
+ if (c == null) {
+ c = readCMap(s);
+ if (c == null)
+ throw new DocumentException("The cmap " + s + " does not exist as a resource.");
+ c[CID_NEWLINE] = '\n';
+ allCMaps.put(s, c);
+ }
+ translationMap = c;
+ }
+ else {
+ char c[] = (char[])allCMaps.get(enc);
+ if (c == null) {
+ String s = cjkEncodings.getProperty(enc);
+ if (s == null)
+ throw new DocumentException("The resource cjkencodings.properties does not contain the encoding " + enc);
+ StringTokenizer tk = new StringTokenizer(s);
+ String nt = tk.nextToken();
+ c = (char[])allCMaps.get(nt);
+ if (c == null) {
+ c = readCMap(nt);
+ allCMaps.put(nt, c);
+ }
+ if (tk.hasMoreTokens()) {
+ String nt2 = tk.nextToken();
+ char m2[] = readCMap(nt2);
+ for (int k = 0; k < 0x10000; ++k) {
+ if (m2[k] == 0)
+ m2[k] = c[k];
+ }
+ allCMaps.put(enc, m2);
+ c = m2;
+ }
+ }
+ translationMap = c;
+ }
+ fontDesc = (HashMap)allFonts.get(fontName);
+ if (fontDesc == null) {
+ fontDesc = readFontProperties(fontName);
+ allFonts.put(fontName, fontDesc);
+ }
+ hMetrics = (IntHashtable)fontDesc.get("W");
+ vMetrics = (IntHashtable)fontDesc.get("W2");
+ }
+
+ /** Checks if its a valid CJK font.
+ * @param fontName the font name
+ * @param enc the encoding
+ * @return true
if it is CJK font
+ */
+ public static boolean isCJKFont(String fontName, String enc) {
+ loadProperties();
+ String encodings = cjkFonts.getProperty(fontName);
+ return (encodings != null && (enc.equals("Identity-H") || enc.equals("Identity-V") || encodings.indexOf("_" + enc + "_") >= 0));
+ }
+
+ public int getWidth(String text) {
+ int total = 0;
+ for (int k = 0; k < text.length(); ++k) {
+ int c = text.charAt(k);
+ if (!cidDirect)
+ c = translationMap[c];
+ int v;
+ if (vertical)
+ v = vMetrics.get(c);
+ else
+ v = hMetrics.get(c);
+ if (v > 0)
+ total += v;
+ else
+ total += 1000;
+ }
+ return total;
+ }
+
+ int getRawWidth(int c, String name) {
+ return 0;
+ }
+
+ public int getKerning(char char1, char char2) {
+ return 0;
+ }
+
+ private PdfDictionary getFontDescriptor() {
+ PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR);
+ dic.put(PdfName.ASCENT, new PdfLiteral((String)fontDesc.get("Ascent")));
+ dic.put(PdfName.CAPHEIGHT, new PdfLiteral((String)fontDesc.get("CapHeight")));
+ dic.put(PdfName.DESCENT, new PdfLiteral((String)fontDesc.get("Descent")));
+ dic.put(PdfName.FLAGS, new PdfLiteral((String)fontDesc.get("Flags")));
+ dic.put(PdfName.FONTBBOX, new PdfLiteral((String)fontDesc.get("FontBBox")));
+ dic.put(PdfName.FONTNAME, new PdfName(fontName + style));
+ dic.put(PdfName.ITALICANGLE, new PdfLiteral((String)fontDesc.get("ItalicAngle")));
+ dic.put(PdfName.STEMV, new PdfLiteral((String)fontDesc.get("StemV")));
+ PdfDictionary pdic = new PdfDictionary();
+ pdic.put(PdfName.PANOSE, new PdfString((String)fontDesc.get("Panose"), null));
+ dic.put(PdfName.STYLE, pdic);
+ return dic;
+ }
+
+ private PdfDictionary getCIDFont(PdfIndirectReference fontDescriptor, IntHashtable cjkTag) {
+ PdfDictionary dic = new PdfDictionary(PdfName.FONT);
+ dic.put(PdfName.SUBTYPE, PdfName.CIDFONTTYPE0);
+ dic.put(PdfName.BASEFONT, new PdfName(fontName + style));
+ dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor);
+ int keys[] = cjkTag.toOrderedKeys();
+ String w = convertToHCIDMetrics(keys, hMetrics);
+ if (w != null)
+ dic.put(PdfName.W, new PdfLiteral(w));
+ if (vertical) {
+ w = convertToVCIDMetrics(keys, vMetrics, hMetrics);;
+ if (w != null)
+ dic.put(PdfName.W2, new PdfLiteral(w));
+ }
+ else
+ dic.put(PdfName.DW, new PdfNumber(1000));
+ PdfDictionary cdic = new PdfDictionary();
+ cdic.put(PdfName.REGISTRY, new PdfString((String)fontDesc.get("Registry"), null));
+ cdic.put(PdfName.ORDERING, new PdfString((String)fontDesc.get("Ordering"), null));
+ cdic.put(PdfName.SUPPLEMENT, new PdfLiteral((String)fontDesc.get("Supplement")));
+ dic.put(PdfName.CIDSYSTEMINFO, cdic);
+ return dic;
+ }
+
+ private PdfDictionary getFontBaseType(PdfIndirectReference CIDFont) {
+ PdfDictionary dic = new PdfDictionary(PdfName.FONT);
+ dic.put(PdfName.SUBTYPE, PdfName.TYPE0);
+ String name = fontName;
+ if (style.length() > 0)
+ name += "-" + style.substring(1);
+ name += "-" + CMap;
+ dic.put(PdfName.BASEFONT, new PdfName(name));
+ dic.put(PdfName.ENCODING, new PdfName(CMap));
+ dic.put(PdfName.DESCENDANTFONTS, new PdfArray(CIDFont));
+ return dic;
+ }
+
+ void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException {
+ IntHashtable cjkTag = (IntHashtable)params[0];
+ PdfIndirectReference ind_font = null;
+ PdfObject pobj = null;
+ PdfIndirectObject obj = null;
+ pobj = getFontDescriptor();
+ if (pobj != null){
+ obj = writer.addToBody(pobj);
+ ind_font = obj.getIndirectReference();
+ }
+ pobj = getCIDFont(ind_font, cjkTag);
+ if (pobj != null){
+ obj = writer.addToBody(pobj);
+ ind_font = obj.getIndirectReference();
+ }
+ pobj = getFontBaseType(ind_font);
+ writer.addToBody(pobj, ref);
+ }
+
+ private float getDescNumber(String name) {
+ return Integer.parseInt((String)fontDesc.get(name));
+ }
+
+ private float getBBox(int idx) {
+ String s = (String)fontDesc.get("FontBBox");
+ StringTokenizer tk = new StringTokenizer(s, " []\r\n\t\f");
+ String ret = tk.nextToken();
+ for (int k = 0; k < idx; ++k)
+ ret = tk.nextToken();
+ return Integer.parseInt(ret);
+ }
+
+ /** Gets the font parameter identified by key
. Valid values
+ * for key
are ASCENT
, CAPHEIGHT
, DESCENT
+ * and ITALICANGLE
.
+ * @param key the parameter to be extracted
+ * @param fontSize the font size in points
+ * @return the parameter in points
+ */
+ public float getFontDescriptor(int key, float fontSize) {
+ switch (key) {
+ case AWT_ASCENT:
+ case ASCENT:
+ return getDescNumber("Ascent") * fontSize / 1000;
+ case CAPHEIGHT:
+ return getDescNumber("CapHeight") * fontSize / 1000;
+ case AWT_DESCENT:
+ case DESCENT:
+ return getDescNumber("Descent") * fontSize / 1000;
+ case ITALICANGLE:
+ return getDescNumber("ItalicAngle");
+ case BBOXLLX:
+ return fontSize * getBBox(0) / 1000;
+ case BBOXLLY:
+ return fontSize * getBBox(1) / 1000;
+ case BBOXURX:
+ return fontSize * getBBox(2) / 1000;
+ case BBOXURY:
+ return fontSize * getBBox(3) / 1000;
+ case AWT_LEADING:
+ return 0;
+ case AWT_MAXADVANCE:
+ return fontSize * (getBBox(2) - getBBox(0)) / 1000;
+ }
+ return 0;
+ }
+
+ public String getPostscriptFontName() {
+ return fontName;
+ }
+
+ /** Gets the full name of the font. If it is a True Type font
+ * each array element will have {Platform ID, Platform Encoding ID,
+ * Language ID, font name}. The interpretation of this values can be
+ * found in the Open Type specification, chapter 2, in the 'name' table.
+ * For the other fonts the array has a single element with {"", "", "",
+ * font name}.
+ * @return the full name of the font
+ */
+ public String[][] getFullFontName() {
+ return new String[][]{{"", "", "", fontName}};
+ }
+
+ /** Gets the family name of the font. If it is a True Type font
+ * each array element will have {Platform ID, Platform Encoding ID,
+ * Language ID, font name}. The interpretation of this values can be
+ * found in the Open Type specification, chapter 2, in the 'name' table.
+ * For the other fonts the array has a single element with {"", "", "",
+ * font name}.
+ * @return the family name of the font
+ */
+ public String[][] getFamilyFontName() {
+ return getFullFontName();
+ }
+
+ static char[] readCMap(String name) {
+ try {
+ name = name + ".cmap";
+ InputStream is = getResourceStream(RESOURCE_PATH + name);
+ char c[] = new char[0x10000];
+ for (int k = 0; k < 0x10000; ++k)
+ c[k] = (char)((is.read() << 8) + is.read());
+ return c;
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ return null;
+ }
+
+ static IntHashtable createMetric(String s) {
+ IntHashtable h = new IntHashtable();
+ StringTokenizer tk = new StringTokenizer(s);
+ while (tk.hasMoreTokens()) {
+ int n1 = Integer.parseInt(tk.nextToken());
+ h.put(n1, Integer.parseInt(tk.nextToken()));
+ }
+ return h;
+ }
+
+ static String convertToHCIDMetrics(int keys[], IntHashtable h) {
+ if (keys.length == 0)
+ return null;
+ int lastCid = 0;
+ int lastValue = 0;
+ int start;
+ for (start = 0; start < keys.length; ++start) {
+ lastCid = keys[start];
+ lastValue = h.get(lastCid);
+ if (lastValue != 0) {
+ ++start;
+ break;
+ }
+ }
+ if (lastValue == 0)
+ return null;
+ StringBuffer buf = new StringBuffer();
+ buf.append('[');
+ buf.append(lastCid);
+ int state = FIRST;
+ for (int k = start; k < keys.length; ++k) {
+ int cid = keys[k];
+ int value = h.get(cid);
+ if (value == 0)
+ continue;
+ switch (state) {
+ case FIRST: {
+ if (cid == lastCid + 1 && value == lastValue) {
+ state = SERIAL;
+ }
+ else if (cid == lastCid + 1) {
+ state = BRACKET;
+ buf.append('[').append(lastValue);
+ }
+ else {
+ buf.append('[').append(lastValue).append(']').append(cid);
+ }
+ break;
+ }
+ case BRACKET: {
+ if (cid == lastCid + 1 && value == lastValue) {
+ state = SERIAL;
+ buf.append(']').append(lastCid);
+ }
+ else if (cid == lastCid + 1) {
+ buf.append(' ').append(lastValue);
+ }
+ else {
+ state = FIRST;
+ buf.append(' ').append(lastValue).append(']').append(cid);
+ }
+ break;
+ }
+ case SERIAL: {
+ if (cid != lastCid + 1 || value != lastValue) {
+ buf.append(' ').append(lastCid).append(' ').append(lastValue).append(' ').append(cid);
+ state = FIRST;
+ }
+ break;
+ }
+ }
+ lastValue = value;
+ lastCid = cid;
+ }
+ switch (state) {
+ case FIRST: {
+ buf.append('[').append(lastValue).append("]]");
+ break;
+ }
+ case BRACKET: {
+ buf.append(' ').append(lastValue).append("]]");
+ break;
+ }
+ case SERIAL: {
+ buf.append(' ').append(lastCid).append(' ').append(lastValue).append(']');
+ break;
+ }
+ }
+ return buf.toString();
+ }
+
+ static String convertToVCIDMetrics(int keys[], IntHashtable v, IntHashtable h) {
+ if (keys.length == 0)
+ return null;
+ int lastCid = 0;
+ int lastValue = 0;
+ int lastHValue = 0;
+ int start;
+ for (start = 0; start < keys.length; ++start) {
+ lastCid = keys[start];
+ lastValue = v.get(lastCid);
+ if (lastValue != 0) {
+ ++start;
+ break;
+ }
+ else
+ lastHValue = h.get(lastCid);
+ }
+ if (lastValue == 0)
+ return null;
+ if (lastHValue == 0)
+ lastHValue = 1000;
+ StringBuffer buf = new StringBuffer();
+ buf.append('[');
+ buf.append(lastCid);
+ int state = FIRST;
+ for (int k = start; k < keys.length; ++k) {
+ int cid = keys[k];
+ int value = v.get(cid);
+ if (value == 0)
+ continue;
+ int hValue = h.get(lastCid);
+ if (hValue == 0)
+ hValue = 1000;
+ switch (state) {
+ case FIRST: {
+ if (cid == lastCid + 1 && value == lastValue && hValue == lastHValue) {
+ state = SERIAL;
+ }
+ else {
+ buf.append(' ').append(lastCid).append(' ').append(-lastValue).append(' ').append(lastHValue / 2).append(' ').append(V1Y).append(' ').append(cid);
+ }
+ break;
+ }
+ case SERIAL: {
+ if (cid != lastCid + 1 || value != lastValue || hValue != lastHValue) {
+ buf.append(' ').append(lastCid).append(' ').append(-lastValue).append(' ').append(lastHValue / 2).append(' ').append(V1Y).append(' ').append(cid);
+ state = FIRST;
+ }
+ break;
+ }
+ }
+ lastValue = value;
+ lastCid = cid;
+ lastHValue = hValue;
+ }
+ buf.append(' ').append(lastCid).append(' ').append(-lastValue).append(' ').append(lastHValue / 2).append(' ').append(V1Y).append(" ]");
+ return buf.toString();
+ }
+
+ static HashMap readFontProperties(String name) {
+ try {
+ name += ".properties";
+ InputStream is = getResourceStream(RESOURCE_PATH + name);
+ Properties p = new Properties();
+ p.load(is);
+ is.close();
+ IntHashtable W = createMetric(p.getProperty("W"));
+ p.remove("W");
+ IntHashtable W2 = createMetric(p.getProperty("W2"));
+ p.remove("W2");
+ HashMap map = new HashMap();
+ for (Enumeration e = p.keys(); e.hasMoreElements();) {
+ Object obj = e.nextElement();
+ map.put(obj, p.getProperty((String)obj));
+ }
+ map.put("W", W);
+ map.put("W2", W2);
+ return map;
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ return null;
+ }
+
+ public char getUnicodeEquivalent(char c) {
+ if (cidDirect)
+ return translationMap[c];
+ return c;
+ }
+
+ public char getCidCode(char c) {
+ if (cidDirect)
+ return c;
+ return translationMap[c];
+ }
+
+ /** Checks if the font has any kerning pairs.
+ * @return always false
+ */
+ public boolean hasKernPairs() {
+ return false;
+ }
+
+ /**
+ * Checks if a character exists in this font.
+ * @param c the character to check
+ * @return true
if the character has a glyph,
+ * false
otherwise
+ */
+ public boolean charExists(char c) {
+ return translationMap[c] != 0;
+ }
+
+ /**
+ * Sets the character advance.
+ * @param c the character
+ * @param advance the character advance normalized to 1000 units
+ * @return true
if the advance was set,
+ * false
otherwise. Will always return false
+ */
+ public boolean setCharAdvance(char c, int advance) {
+ return false;
+ }
+
+ /**
+ * Sets the font name that will appear in the pdf font dictionary.
+ * Use with care as it can easily make a font unreadable if not embedded.
+ * @param name the new font name
+ */
+ public void setPostscriptFontName(String name) {
+ fontName = name;
+ }
+
+ public boolean setKerning(char char1, char char2, int kern) {
+ return false;
+ }
+
+ public int[] getCharBBox(char c) {
+ return null;
+ }
+
+ protected int[] getRawCharBBox(int c, String name) {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/CMYKColor.java b/src/main/java/com/lowagie/text/pdf/CMYKColor.java
new file mode 100644
index 0000000..4acee83
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/CMYKColor.java
@@ -0,0 +1,129 @@
+/*
+ * $Id: CMYKColor.java,v 1.44 2005/12/11 15:31:04 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class CMYKColor extends ExtendedColor {
+
+ float cyan;
+ float magenta;
+ float yellow;
+ float black;
+
+ /**
+ * Constructs a CMYK Color beased on 4 colorvalues (values are integers from 0 to 255).
+ * @param intCyan
+ * @param intMagenta
+ * @param intYellow
+ * @param intBlack
+ */
+ public CMYKColor(int intCyan, int intMagenta, int intYellow, int intBlack) {
+ this((float)intCyan / 255f, (float)intMagenta / 255f, (float)intYellow / 255f, (float)intBlack / 255f);
+ }
+
+ /**
+ * Construct a CMYK Color.
+ * @param floatCyan
+ * @param floatMagenta
+ * @param floatYellow
+ * @param floatBlack
+ */
+ public CMYKColor(float floatCyan, float floatMagenta, float floatYellow, float floatBlack) {
+ super(TYPE_CMYK, 1f - floatCyan - floatBlack, 1f - floatMagenta - floatBlack, 1f - floatYellow - floatBlack);
+ cyan = normalize(floatCyan);
+ magenta = normalize(floatMagenta);
+ yellow = normalize(floatYellow);
+ black = normalize(floatBlack);
+ }
+
+ /**
+ * @return the cyan value
+ */
+ public float getCyan() {
+ return cyan;
+ }
+
+ /**
+ * @return the magenta value
+ */
+ public float getMagenta() {
+ return magenta;
+ }
+
+ /**
+ * @return the yellow value
+ */
+ public float getYellow() {
+ return yellow;
+ }
+
+ /**
+ * @return the black value
+ */
+ public float getBlack() {
+ return black;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CMYKColor))
+ return false;
+ CMYKColor c2 = (CMYKColor)obj;
+ return (cyan == c2.cyan && magenta == c2.magenta && yellow == c2.yellow && black == c2.black);
+ }
+
+ public int hashCode() {
+ return Float.floatToIntBits(cyan) ^ Float.floatToIntBits(magenta) ^ Float.floatToIntBits(yellow) ^ Float.floatToIntBits(black);
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/ColorDetails.java b/src/main/java/com/lowagie/text/pdf/ColorDetails.java
new file mode 100644
index 0000000..b945c06
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/ColorDetails.java
@@ -0,0 +1,101 @@
+/*
+ * $Id: ColorDetails.java,v 1.43 2005/05/04 14:31:49 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+/** Each spotcolor in the document will have an instance of this class
+ *
+ * @author Phillip Pan (phillip@formstar.com)
+ */
+class ColorDetails {
+
+ /** The indirect reference to this color
+ */
+ PdfIndirectReference indirectReference;
+ /** The color name that appears in the document body stream
+ */
+ PdfName colorName;
+ /** The color
+ */
+ PdfSpotColor spotcolor;
+
+ /** Each spot color used in a document has an instance of this class.
+ * @param colorName the color name
+ * @param indirectReference the indirect reference to the font
+ * @param scolor the PDfSpotColor
+ */
+ ColorDetails(PdfName colorName, PdfIndirectReference indirectReference, PdfSpotColor scolor) {
+ this.colorName = colorName;
+ this.indirectReference = indirectReference;
+ this.spotcolor = scolor;
+ }
+
+ /** Gets the indirect reference to this color.
+ * @return the indirect reference to this color
+ */
+ PdfIndirectReference getIndirectReference() {
+ return indirectReference;
+ }
+
+ /** Gets the color name as it appears in the document body.
+ * @return the color name
+ */
+ PdfName getColorName() {
+ return colorName;
+ }
+
+ /** Gets the SpotColor
object.
+ * @return the PdfSpotColor
+ */
+ PdfObject getSpotColor(PdfWriter writer) throws IOException {
+ return spotcolor.getSpotObject(writer);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/ColumnText.java b/src/main/java/com/lowagie/text/pdf/ColumnText.java
new file mode 100644
index 0000000..3197290
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/ColumnText.java
@@ -0,0 +1,1520 @@
+/*
+ * $Id: ColumnText.java,v 1.68 2006/01/29 20:28:23 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.Stack;
+import java.util.Iterator;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Chunk;
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.Graphic;
+import com.lowagie.text.ListItem;
+import com.lowagie.text.Element;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Image;
+import com.lowagie.text.SimpleTable;
+
+/**
+ * Formats text in a columnwise form. The text is bound
+ * on the left and on the right by a sequence of lines. This allows the column
+ * to have any shape, not only rectangular.
+ * go
will return one of the following
+ * situations: the column ended or the text ended.
+ * setColumns
and the method go
can be called again.
+ * addText
+ * and the method go
can be called again.
+ * The only limitation is that one or more complete paragraphs must be loaded
+ * each time.
+ * PdfWriter.RUN_DIRECTION_RTL
the meaning of the horizontal
+ * alignments and margins is mirrored.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+
+public class ColumnText {
+ /** Eliminate the arabic vowels */
+ public static final int AR_NOVOWEL = ArabicLigaturizer.ar_novowel;
+ /** Compose the tashkeel in the ligatures. */
+ public static final int AR_COMPOSEDTASHKEEL = ArabicLigaturizer.ar_composedtashkeel;
+ /** Do some extra double ligatures. */
+ public static final int AR_LIG = ArabicLigaturizer.ar_lig;
+ /**
+ * Digit shaping option: Replace European digits (U+0030...U+0039) by Arabic-Indic digits.
+ */
+ public static final int DIGITS_EN2AN = ArabicLigaturizer.DIGITS_EN2AN;
+
+ /**
+ * Digit shaping option: Replace Arabic-Indic digits by European digits (U+0030...U+0039).
+ */
+ public static final int DIGITS_AN2EN = ArabicLigaturizer.DIGITS_AN2EN;
+
+ /**
+ * Digit shaping option:
+ * Replace European digits (U+0030...U+0039) by Arabic-Indic digits
+ * if the most recent strongly directional character
+ * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC).
+ * The initial state at the start of the text is assumed to be not an Arabic,
+ * letter, so European digits at the start of the text will not change.
+ * Compare to DIGITS_ALEN2AN_INIT_AL.
+ */
+ public static final int DIGITS_EN2AN_INIT_LR = ArabicLigaturizer.DIGITS_EN2AN_INIT_LR;
+
+ /**
+ * Digit shaping option:
+ * Replace European digits (U+0030...U+0039) by Arabic-Indic digits
+ * if the most recent strongly directional character
+ * is an Arabic letter (its Bidi direction value is RIGHT_TO_LEFT_ARABIC).
+ * The initial state at the start of the text is assumed to be an Arabic,
+ * letter, so European digits at the start of the text will change.
+ * Compare to DIGITS_ALEN2AN_INT_LR.
+ */
+ public static final int DIGITS_EN2AN_INIT_AL = ArabicLigaturizer.DIGITS_EN2AN_INIT_AL;
+
+ /**
+ * Digit type option: Use Arabic-Indic digits (U+0660...U+0669).
+ */
+ public static final int DIGIT_TYPE_AN = ArabicLigaturizer.DIGIT_TYPE_AN;
+
+ /**
+ * Digit type option: Use Eastern (Extended) Arabic-Indic digits (U+06f0...U+06f9).
+ */
+ public static final int DIGIT_TYPE_AN_EXTENDED = ArabicLigaturizer.DIGIT_TYPE_AN_EXTENDED;
+
+ protected int runDirection = PdfWriter.RUN_DIRECTION_DEFAULT;
+
+ /** the space char ratio */
+ public static final float GLOBAL_SPACE_CHAR_RATIO = 0;
+
+ /** Initial value of the status. */
+ public static final int START_COLUMN = 0;
+
+ /** Signals that there is no more text available. */
+ public static final int NO_MORE_TEXT = 1;
+
+ /** Signals that there is no more column. */
+ public static final int NO_MORE_COLUMN = 2;
+
+ /** The column is valid. */
+ protected static final int LINE_STATUS_OK = 0;
+
+ /** The line is out the column limits. */
+ protected static final int LINE_STATUS_OFFLIMITS = 1;
+
+ /** The line cannot fit this column position. */
+ protected static final int LINE_STATUS_NOLINE = 2;
+
+ /** Upper bound of the column. */
+ protected float maxY;
+
+ /** Lower bound of the column. */
+ protected float minY;
+
+ protected float leftX;
+
+ protected float rightX;
+
+ /** The column alignment. Default is left alignment. */
+ protected int alignment = Element.ALIGN_LEFT;
+
+ /** The left column bound. */
+ protected ArrayList leftWall;
+
+ /** The right column bound. */
+ protected ArrayList rightWall;
+
+ /** The chunks that form the text. */
+// protected ArrayList chunks = new ArrayList();
+ protected BidiLine bidiLine;
+
+ /** The current y line location. Text will be written at this line minus the leading. */
+ protected float yLine;
+
+ /** The leading for the current line. */
+ protected float currentLeading = 16;
+
+ /** The fixed text leading. */
+ protected float fixedLeading = 16;
+
+ /** The text leading that is multiplied by the biggest font size in the line. */
+ protected float multipliedLeading = 0;
+
+ /** The PdfContent
where the text will be written to. */
+ protected PdfContentByte canvas;
+
+ protected PdfContentByte[] canvases;
+
+ /** The line status when trying to fit a line to a column. */
+ protected int lineStatus;
+
+ /** The first paragraph line indent. */
+ protected float indent = 0;
+
+ /** The following paragraph lines indent. */
+ protected float followingIndent = 0;
+
+ /** The right paragraph lines indent. */
+ protected float rightIndent = 0;
+
+ /** The extra space between paragraphs. */
+ protected float extraParagraphSpace = 0;
+
+ /** The width of the line when the column is defined as a simple rectangle. */
+ protected float rectangularWidth = -1;
+
+ protected boolean rectangularMode = false;
+ /** Holds value of property spaceCharRatio. */
+ private float spaceCharRatio = GLOBAL_SPACE_CHAR_RATIO;
+
+ private boolean lastWasNewline = true;
+
+ /** Holds value of property linesWritten. */
+ private int linesWritten;
+
+ private float firstLineY;
+ private boolean firstLineYDone = false;
+
+ /** Holds value of property arabicOptions. */
+ private int arabicOptions = 0;
+
+ protected float descender;
+
+ protected boolean composite = false;
+
+ protected ColumnText compositeColumn;
+
+ protected LinkedList compositeElements;
+
+ protected int listIdx = 0;
+
+ private boolean splittedRow;
+
+ protected Phrase waitPhrase;
+
+ /** if true, first line height is adjusted so that the max ascender touches the top */
+ private boolean useAscender = false;
+
+ /**
+ * Creates a ColumnText
.
+ * @param canvas the place where the text will be written to. Can
+ * be a template.
+ */
+ public ColumnText(PdfContentByte canvas) {
+ this.canvas = canvas;
+ }
+
+ /** Creates an independent duplicated of the instance org
.
+ * @param org the original ColumnText
+ * @return the duplicated
+ */
+ public static ColumnText duplicate(ColumnText org) {
+ ColumnText ct = new ColumnText(null);
+ ct.setACopy(org);
+ return ct;
+ }
+
+ /** Makes this instance an independent copy of org
.
+ * @param org the original ColumnText
+ * @return itself
+ */
+ public ColumnText setACopy(ColumnText org) {
+ setSimpleVars(org);
+ if (org.bidiLine != null)
+ bidiLine = new BidiLine(org.bidiLine);
+ return this;
+ }
+
+ protected void setSimpleVars(ColumnText org) {
+ maxY = org.maxY;
+ minY = org.minY;
+ alignment = org.alignment;
+ leftWall = null;
+ if (org.leftWall != null)
+ leftWall = new ArrayList(org.leftWall);
+ rightWall = null;
+ if (org.rightWall != null)
+ rightWall = new ArrayList(org.rightWall);
+ yLine = org.yLine;
+ currentLeading = org.currentLeading;
+ fixedLeading = org.fixedLeading;
+ multipliedLeading = org.multipliedLeading;
+ canvas = org.canvas;
+ canvases = org.canvases;
+ lineStatus = org.lineStatus;
+ indent = org.indent;
+ followingIndent = org.followingIndent;
+ rightIndent = org.rightIndent;
+ extraParagraphSpace = org.extraParagraphSpace;
+ rectangularWidth = org.rectangularWidth;
+ rectangularMode = org.rectangularMode;
+ spaceCharRatio = org.spaceCharRatio;
+ lastWasNewline = org.lastWasNewline;
+ linesWritten = org.linesWritten;
+ arabicOptions = org.arabicOptions;
+ runDirection = org.runDirection;
+ descender = org.descender;
+ composite = org.composite;
+ splittedRow = org.splittedRow;
+ if (org.composite) {
+ compositeElements = new LinkedList(org.compositeElements);
+ if (splittedRow) {
+ PdfPTable table = (PdfPTable)compositeElements.getFirst();
+ compositeElements.set(0, new PdfPTable(table));
+ }
+ if (org.compositeColumn != null)
+ compositeColumn = duplicate(org.compositeColumn);
+ }
+ listIdx = org.listIdx;
+ firstLineY = org.firstLineY;
+ leftX = org.leftX;
+ rightX = org.rightX;
+ firstLineYDone = org.firstLineYDone;
+ waitPhrase = org.waitPhrase;
+ useAscender = org.useAscender;
+ filledWidth = org.filledWidth;
+ }
+
+ private void addWaitingPhrase() {
+ if (bidiLine == null && waitPhrase != null) {
+ bidiLine = new BidiLine();
+ for (Iterator j = waitPhrase.getChunks().iterator(); j.hasNext();) {
+ bidiLine.addChunk(new PdfChunk((Chunk)j.next(), null));
+ }
+ waitPhrase = null;
+ }
+ }
+
+ /**
+ * Adds a Phrase
to the current text array.
+ * Will not have any effect if addElement() was called before.
+ * @param phrase the text
+ */
+ public void addText(Phrase phrase) {
+ if (phrase == null || composite)
+ return;
+ addWaitingPhrase();
+ if (bidiLine == null) {
+ waitPhrase = phrase;
+ return;
+ }
+ for (Iterator j = phrase.getChunks().iterator(); j.hasNext();) {
+ bidiLine.addChunk(new PdfChunk((Chunk)j.next(), null));
+ }
+ }
+
+ /**
+ * Replaces the current text array with this Phrase
.
+ * Anything added previously with addElement() is lost.
+ * @param phrase the text
+ */
+ public void setText(Phrase phrase) {
+ bidiLine = null;
+ composite = false;
+ compositeColumn = null;
+ compositeElements = null;
+ listIdx = 0;
+ splittedRow = false;
+ waitPhrase = phrase;
+ }
+
+ /**
+ * Adds a Chunk
to the current text array.
+ * Will not have any effect if addElement() was called before.
+ * @param chunk the text
+ */
+ public void addText(Chunk chunk) {
+ if (chunk == null || composite)
+ return;
+ addText(new Phrase(chunk));
+ }
+
+ /**
+ * Adds an element. Elements supported are Paragraph
,
+ * List
, PdfPTable
, Image
and
+ * Graphic
.
+ * addText()
.
+ * @param element the Element
+ */
+ public void addElement(Element element) {
+ if (element == null)
+ return;
+ if (element instanceof Image) {
+ Image img = (Image)element;
+ PdfPTable t = new PdfPTable(1);
+ float w = img.getWidthPercentage();
+ if (w == 0) {
+ t.setTotalWidth(img.scaledWidth());
+ t.setLockedWidth(true);
+ }
+ else
+ t.setWidthPercentage(w);
+ t.setSpacingAfter(img.spacingAfter());
+ t.setSpacingBefore(img.spacingBefore());
+ switch (img.alignment()) {
+ case Image.LEFT:
+ t.setHorizontalAlignment(Element.ALIGN_LEFT);
+ break;
+ case Image.RIGHT:
+ t.setHorizontalAlignment(Element.ALIGN_RIGHT);
+ break;
+ default:
+ t.setHorizontalAlignment(Element.ALIGN_CENTER);
+ break;
+ }
+ PdfPCell c = new PdfPCell(img, true);
+ c.setPadding(0);
+ c.setBorder(img.border());
+ c.setBorderColor(img.borderColor());
+ c.setBorderWidth(img.borderWidth());
+ c.setBackgroundColor(img.backgroundColor());
+ t.addCell(c);
+ element = t;
+ }
+ if (element.type() == Element.CHUNK) {
+ element = new Paragraph((Chunk)element);
+ }
+ else if (element.type() == Element.PHRASE) {
+ element = new Paragraph((Phrase)element);
+ }
+ if (element instanceof SimpleTable) {
+ try {
+ element = ((SimpleTable)element).createPdfPTable();
+ } catch (DocumentException e) {
+ throw new IllegalArgumentException("Element not allowed.");
+ }
+ }
+ else if (element.type() != Element.PARAGRAPH && element.type() != Element.LIST && element.type() != Element.PTABLE && element.type() != Element.GRAPHIC)
+ throw new IllegalArgumentException("Element not allowed.");
+ if (!composite) {
+ composite = true;
+ compositeElements = new LinkedList();
+ bidiLine = null;
+ waitPhrase = null;
+ }
+ compositeElements.add(element);
+ }
+
+ /**
+ * Converts a sequence of lines representing one of the column bounds into
+ * an internal format.
+ * float[4]
representing
+ * the line x = ax + b.
+ * @param cLine the column array
+ * @return the converted array
+ */
+ protected ArrayList convertColumn(float cLine[]) {
+ if (cLine.length < 4)
+ throw new RuntimeException("No valid column line found.");
+ ArrayList cc = new ArrayList();
+ for (int k = 0; k < cLine.length - 2; k += 2) {
+ float x1 = cLine[k];
+ float y1 = cLine[k + 1];
+ float x2 = cLine[k + 2];
+ float y2 = cLine[k + 3];
+ if (y1 == y2)
+ continue;
+ // x = ay + b
+ float a = (x1 - x2) / (y1 - y2);
+ float b = x1 - a * y1;
+ float r[] = new float[4];
+ r[0] = Math.min(y1, y2);
+ r[1] = Math.max(y1, y2);
+ r[2] = a;
+ r[3] = b;
+ cc.add(r);
+ maxY = Math.max(maxY, r[1]);
+ minY = Math.min(minY, r[0]);
+ }
+ if (cc.size() == 0)
+ throw new RuntimeException("No valid column line found.");
+ return cc;
+ }
+
+ /**
+ * Finds the intersection between the yLine
and the column. It will
+ * set the lineStatus
apropriatly.
+ * @param wall the column to intersect
+ * @return the x coordinate of the intersection
+ */
+ protected float findLimitsPoint(ArrayList wall) {
+ lineStatus = LINE_STATUS_OK;
+ if (yLine < minY || yLine > maxY) {
+ lineStatus = LINE_STATUS_OFFLIMITS;
+ return 0;
+ }
+ for (int k = 0; k < wall.size(); ++k) {
+ float r[] = (float[])wall.get(k);
+ if (yLine < r[0] || yLine > r[1])
+ continue;
+ return r[2] * yLine + r[3];
+ }
+ lineStatus = LINE_STATUS_NOLINE;
+ return 0;
+ }
+
+ /**
+ * Finds the intersection between the yLine
and the two
+ * column bounds. It will set the lineStatus
apropriatly.
+ * @return a float[2]
with the x coordinates of the intersection
+ */
+ protected float[] findLimitsOneLine() {
+ float x1 = findLimitsPoint(leftWall);
+ if (lineStatus == LINE_STATUS_OFFLIMITS || lineStatus == LINE_STATUS_NOLINE)
+ return null;
+ float x2 = findLimitsPoint(rightWall);
+ if (lineStatus == LINE_STATUS_NOLINE)
+ return null;
+ return new float[]{x1, x2};
+ }
+
+ /**
+ * Finds the intersection between the yLine
,
+ * the yLine-leading
and the two
+ * column bounds. It will set the lineStatus
apropriatly.
+ * @return a float[4]
with the x coordinates of the intersection
+ */
+ protected float[] findLimitsTwoLines() {
+ boolean repeat = false;
+ for (;;) {
+ if (repeat && currentLeading == 0)
+ return null;
+ repeat = true;
+ float x1[] = findLimitsOneLine();
+ if (lineStatus == LINE_STATUS_OFFLIMITS)
+ return null;
+ yLine -= currentLeading;
+ if (lineStatus == LINE_STATUS_NOLINE) {
+ continue;
+ }
+ float x2[] = findLimitsOneLine();
+ if (lineStatus == LINE_STATUS_OFFLIMITS)
+ return null;
+ if (lineStatus == LINE_STATUS_NOLINE) {
+ yLine -= currentLeading;
+ continue;
+ }
+ if (x1[0] >= x2[1] || x2[0] >= x1[1])
+ continue;
+ return new float[]{x1[0], x1[1], x2[0], x2[1]};
+ }
+ }
+
+ /**
+ * Sets the columns bounds. Each column bound is described by a
+ * float[]
with the line points [x1,y1,x2,y2,...].
+ * The array must have at least 4 elements.
+ * @param leftLine the left column bound
+ * @param rightLine the right column bound
+ */
+ public void setColumns(float leftLine[], float rightLine[]) {
+ maxY = -10e20f;
+ minY = 10e20f;
+ rightWall = convertColumn(rightLine);
+ leftWall = convertColumn(leftLine);
+ rectangularWidth = -1;
+ rectangularMode = false;
+ }
+
+ /**
+ * Simplified method for rectangular columns.
+ * @param phrase a Phrase
+ * @param llx the lower left x corner
+ * @param lly the lower left y corner
+ * @param urx the upper right x corner
+ * @param ury the upper right y corner
+ * @param leading the leading
+ * @param alignment the column alignment
+ */
+ public void setSimpleColumn(Phrase phrase, float llx, float lly, float urx, float ury, float leading, int alignment) {
+ addText(phrase);
+ setSimpleColumn(llx, lly, urx, ury, leading, alignment);
+ }
+
+ /**
+ * Simplified method for rectangular columns.
+ * @param llx the lower left x corner
+ * @param lly the lower left y corner
+ * @param urx the upper right x corner
+ * @param ury the upper right y corner
+ * @param leading the leading
+ * @param alignment the column alignment
+ */
+ public void setSimpleColumn(float llx, float lly, float urx, float ury, float leading, int alignment) {
+ setLeading(leading);
+ this.alignment = alignment;
+ setSimpleColumn(llx, lly, urx, ury);
+ }
+
+ /**
+ * Simplified method for rectangular columns.
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void setSimpleColumn(float llx, float lly, float urx, float ury) {
+ leftX = Math.min(llx, urx);
+ maxY = Math.max(lly, ury);
+ minY = Math.min(lly, ury);
+ rightX = Math.max(llx, urx);
+ yLine = maxY;
+ rectangularWidth = rightX - leftX;
+ if (rectangularWidth < 0)
+ rectangularWidth = 0;
+ rectangularMode = true;
+ }
+ /**
+ * Sets the leading to fixed
+ * @param leading the leading
+ */
+ public void setLeading(float leading) {
+ fixedLeading = leading;
+ multipliedLeading = 0;
+ }
+
+ /**
+ * Sets the leading fixed and variable. The resultant leading will be
+ * fixedLeading+multipliedLeading*maxFontSize where maxFontSize is the
+ * size of the bigest font in the line.
+ * @param fixedLeading the fixed leading
+ * @param multipliedLeading the variable leading
+ */
+ public void setLeading(float fixedLeading, float multipliedLeading) {
+ this.fixedLeading = fixedLeading;
+ this.multipliedLeading = multipliedLeading;
+ }
+
+ /**
+ * Gets the fixed leading
+ * @return the leading
+ */
+ public float getLeading() {
+ return fixedLeading;
+ }
+
+ /**
+ * Gets the variable leading
+ * @return the leading
+ */
+ public float getMultipliedLeading() {
+ return multipliedLeading;
+ }
+
+ /**
+ * Sets the yLine. The line will be written to yLine-leading.
+ * @param yLine the yLine
+ */
+ public void setYLine(float yLine) {
+ this.yLine = yLine;
+ }
+
+ /**
+ * Gets the yLine.
+ * @return the yLine
+ */
+ public float getYLine() {
+ return yLine;
+ }
+
+ /**
+ * Sets the alignment.
+ * @param alignment the alignment
+ */
+ public void setAlignment(int alignment) {
+ this.alignment = alignment;
+ }
+
+ /**
+ * Gets the alignment.
+ * @return the alignment
+ */
+ public int getAlignment() {
+ return alignment;
+ }
+
+ /**
+ * Sets the first paragraph line indent.
+ * @param indent the indent
+ */
+ public void setIndent(float indent) {
+ this.indent = indent;
+ lastWasNewline = true;
+ }
+
+ /**
+ * Gets the first paragraph line indent.
+ * @return the indent
+ */
+ public float getIndent() {
+ return indent;
+ }
+
+ /**
+ * Sets the following paragraph lines indent.
+ * @param indent the indent
+ */
+ public void setFollowingIndent(float indent) {
+ this.followingIndent = indent;
+ lastWasNewline = true;
+ }
+
+ /**
+ * Gets the following paragraph lines indent.
+ * @return the indent
+ */
+ public float getFollowingIndent() {
+ return followingIndent;
+ }
+
+ /**
+ * Sets the right paragraph lines indent.
+ * @param indent the indent
+ */
+ public void setRightIndent(float indent) {
+ this.rightIndent = indent;
+ lastWasNewline = true;
+ }
+
+ /**
+ * Gets the right paragraph lines indent.
+ * @return the indent
+ */
+ public float getRightIndent() {
+ return rightIndent;
+ }
+
+ /**
+ * Outputs the lines to the document. It is equivalent to go(false)
.
+ * @return returns the result of the operation. It can be NO_MORE_TEXT
+ * and/or NO_MORE_COLUMN
+ * @throws DocumentException on error
+ */
+ public int go() throws DocumentException {
+ return go(false);
+ }
+
+ /**
+ * Outputs the lines to the document. The output can be simulated.
+ * @param simulate true
to simulate the writting to the document
+ * @return returns the result of the operation. It can be NO_MORE_TEXT
+ * and/or NO_MORE_COLUMN
+ * @throws DocumentException on error
+ */
+ public int go(boolean simulate) throws DocumentException {
+ if (composite)
+ return goComposite(simulate);
+ addWaitingPhrase();
+ if (bidiLine == null)
+ return NO_MORE_TEXT;
+ descender = 0;
+ linesWritten = 0;
+ boolean dirty = false;
+ float ratio = spaceCharRatio;
+ Object currentValues[] = new Object[2];
+ PdfFont currentFont = null;
+ Float lastBaseFactor = new Float(0);
+ currentValues[1] = lastBaseFactor;
+ PdfDocument pdf = null;
+ PdfContentByte graphics = null;
+ PdfContentByte text = null;
+ firstLineY = Float.NaN;
+ int localRunDirection = PdfWriter.RUN_DIRECTION_NO_BIDI;
+ if (runDirection != PdfWriter.RUN_DIRECTION_DEFAULT)
+ localRunDirection = runDirection;
+ if (canvas != null) {
+ graphics = canvas;
+ pdf = canvas.getPdfDocument();
+ text = canvas.getDuplicate();
+ }
+ else if (!simulate)
+ throw new NullPointerException("ColumnText.go with simulate==false and text==null.");
+ if (!simulate) {
+ if (ratio == GLOBAL_SPACE_CHAR_RATIO)
+ ratio = text.getPdfWriter().getSpaceCharRatio();
+ else if (ratio < 0.001f)
+ ratio = 0.001f;
+ }
+ float firstIndent = 0;
+
+ int status = 0;
+ if (rectangularMode) {
+ for (;;) {
+ firstIndent = (lastWasNewline ? indent : followingIndent);
+ if (rectangularWidth <= firstIndent + rightIndent) {
+ status = NO_MORE_COLUMN;
+ if (bidiLine.isEmpty())
+ status |= NO_MORE_TEXT;
+ break;
+ }
+ if (bidiLine.isEmpty()) {
+ status = NO_MORE_TEXT;
+ break;
+ }
+ PdfLine line = bidiLine.processLine(rectangularWidth - firstIndent - rightIndent, alignment, localRunDirection, arabicOptions);
+ if (line == null) {
+ status = NO_MORE_TEXT;
+ break;
+ }
+ float maxSize = line.getMaxSizeSimple();
+ if (isUseAscender() && Float.isNaN(firstLineY)) {
+ currentLeading = line.getAscender();
+ }
+ else {
+ currentLeading = fixedLeading + maxSize * multipliedLeading;
+ }
+ if (yLine > maxY || yLine - currentLeading < minY ) {
+ status = NO_MORE_COLUMN;
+ bidiLine.restore();
+ break;
+ }
+ yLine -= currentLeading;
+ if (!simulate && !dirty) {
+ text.beginText();
+ dirty = true;
+ }
+ if (Float.isNaN(firstLineY)) {
+ firstLineY = yLine;
+ }
+ updateFilledWidth(rectangularWidth - line.widthLeft());
+ if (!simulate) {
+ currentValues[0] = currentFont;
+ text.setTextMatrix(leftX + (line.isRTL() ? rightIndent : firstIndent) + line.indentLeft(), yLine);
+ pdf.writeLineToContent(line, text, graphics, currentValues, ratio);
+ currentFont = (PdfFont)currentValues[0];
+ }
+ lastWasNewline = line.isNewlineSplit();
+ yLine -= line.isNewlineSplit() ? extraParagraphSpace : 0;
+ ++linesWritten;
+ descender = line.getDescender();
+ }
+ }
+ else {
+ currentLeading = fixedLeading;
+ for (;;) {
+ firstIndent = (lastWasNewline ? indent : followingIndent);
+ float yTemp = yLine;
+ float xx[] = findLimitsTwoLines();
+ if (xx == null) {
+ status = NO_MORE_COLUMN;
+ if (bidiLine.isEmpty())
+ status |= NO_MORE_TEXT;
+ yLine = yTemp;
+ break;
+ }
+ if (bidiLine.isEmpty()) {
+ status = NO_MORE_TEXT;
+ yLine = yTemp;
+ break;
+ }
+ float x1 = Math.max(xx[0], xx[2]);
+ float x2 = Math.min(xx[1], xx[3]);
+ if (x2 - x1 <= firstIndent + rightIndent)
+ continue;
+ if (!simulate && !dirty) {
+ text.beginText();
+ dirty = true;
+ }
+ PdfLine line = bidiLine.processLine(x2 - x1 - firstIndent - rightIndent, alignment, localRunDirection, arabicOptions);
+ if (line == null) {
+ status = NO_MORE_TEXT;
+ yLine = yTemp;
+ break;
+ }
+ if (!simulate) {
+ currentValues[0] = currentFont;
+ text.setTextMatrix(x1 + (line.isRTL() ? rightIndent : firstIndent) + line.indentLeft(), yLine);
+ pdf.writeLineToContent(line, text, graphics, currentValues, ratio);
+ currentFont = (PdfFont)currentValues[0];
+ }
+ lastWasNewline = line.isNewlineSplit();
+ yLine -= line.isNewlineSplit() ? extraParagraphSpace : 0;
+ ++linesWritten;
+ descender = line.getDescender();
+ }
+ }
+ if (dirty) {
+ text.endText();
+ canvas.add(text);
+ }
+ return status;
+ }
+
+ /**
+ * Sets the extra space between paragraphs.
+ * @return the extra space between paragraphs
+ */
+ public float getExtraParagraphSpace() {
+ return extraParagraphSpace;
+ }
+
+ /**
+ * Sets the extra space between paragraphs.
+ * @param extraParagraphSpace the extra space between paragraphs
+ */
+ public void setExtraParagraphSpace(float extraParagraphSpace) {
+ this.extraParagraphSpace = extraParagraphSpace;
+ }
+
+ /**
+ * Clears the chunk array. A call to go()
will always return
+ * NO_MORE_TEXT.
+ */
+ public void clearChunks() {
+ if (bidiLine != null)
+ bidiLine.clearChunks();
+ }
+
+ /** Gets the space/character extra spacing ratio for
+ * fully justified text.
+ * @return the space/character extra spacing ratio
+ */
+ public float getSpaceCharRatio() {
+ return spaceCharRatio;
+ }
+
+ /** Sets the ratio between the extra word spacing and the extra character spacing
+ * when the text is fully justified.
+ * Extra word spacing will grow spaceCharRatio
times more than extra character spacing.
+ * If the ratio is PdfWriter.NO_SPACE_CHAR_RATIO
then the extra character spacing
+ * will be zero.
+ * @param spaceCharRatio the ratio between the extra word spacing and the extra character spacing
+ */
+ public void setSpaceCharRatio(float spaceCharRatio) {
+ this.spaceCharRatio = spaceCharRatio;
+ }
+
+ /** Sets the run direction.
+ * @param runDirection the run direction
+ */
+ public void setRunDirection(int runDirection) {
+ if (runDirection < PdfWriter.RUN_DIRECTION_DEFAULT || runDirection > PdfWriter.RUN_DIRECTION_RTL)
+ throw new RuntimeException("Invalid run direction: " + runDirection);
+ this.runDirection = runDirection;
+ }
+
+ /** Gets the run direction.
+ * @return the run direction
+ */
+ public int getRunDirection() {
+ return runDirection;
+ }
+
+ /** Gets the number of lines written.
+ * @return the number of lines written
+ */
+ public int getLinesWritten() {
+ return this.linesWritten;
+ }
+
+ /** Gets the arabic shaping options.
+ * @return the arabic shaping options
+ */
+ public int getArabicOptions() {
+ return this.arabicOptions;
+ }
+
+ /** Sets the arabic shaping options. The option can be AR_NOVOWEL,
+ * AR_COMPOSEDTASHKEEL and AR_LIG.
+ * @param arabicOptions the arabic shaping options
+ */
+ public void setArabicOptions(int arabicOptions) {
+ this.arabicOptions = arabicOptions;
+ }
+
+ /** Gets the biggest descender value of the last line written.
+ * @return the biggest descender value of the last line written
+ */
+ public float getDescender() {
+ return descender;
+ }
+
+ /** Gets the width that the line will occupy after writing.
+ * Only the width of the first line is returned.
+ * @param phrase the Phrase
containing the line
+ * @param runDirection the run direction
+ * @param arabicOptions the options for the arabic shaping
+ * @return the width of the line
+ */
+ public static float getWidth(Phrase phrase, int runDirection, int arabicOptions) {
+ ColumnText ct = new ColumnText(null);
+ ct.addText(phrase);
+ ct.addWaitingPhrase();
+ PdfLine line = ct.bidiLine.processLine(20000, Element.ALIGN_LEFT, runDirection, arabicOptions);
+ if (line == null)
+ return 0;
+ else
+ return 20000 - line.widthLeft();
+ }
+
+ /** Gets the width that the line will occupy after writing.
+ * Only the width of the first line is returned.
+ * @param phrase the Phrase
containing the line
+ * @return the width of the line
+ */
+ public static float getWidth(Phrase phrase) {
+ return getWidth(phrase, PdfWriter.RUN_DIRECTION_NO_BIDI, 0);
+ }
+
+ /** Shows a line of text. Only the first line is written.
+ * @param canvas where the text is to be written to
+ * @param alignment the alignment. It is not influenced by the run direction
+ * @param phrase the Phrase
with the text
+ * @param x the x reference position
+ * @param y the y reference position
+ * @param rotation the rotation to be applied in degrees counterclockwise
+ * @param runDirection the run direction
+ * @param arabicOptions the options for the arabic shaping
+ */
+ public static void showTextAligned(PdfContentByte canvas, int alignment, Phrase phrase, float x, float y, float rotation, int runDirection, int arabicOptions) {
+ if (alignment != Element.ALIGN_LEFT && alignment != Element.ALIGN_CENTER
+ && alignment != Element.ALIGN_RIGHT)
+ alignment = Element.ALIGN_LEFT;
+ canvas.saveState();
+ ColumnText ct = new ColumnText(canvas);
+ if (rotation == 0) {
+ if (alignment == Element.ALIGN_LEFT)
+ ct.setSimpleColumn(phrase, x, y - 1, 20000 + x, y + 2, 2, alignment);
+ else if (alignment == Element.ALIGN_RIGHT)
+ ct.setSimpleColumn(phrase, x-20000, y-1, x, y+2, 2, alignment);
+ else
+ ct.setSimpleColumn(phrase, x-20000, y-1, x+20000, y+2, 2, alignment);
+ }
+ else {
+ double alpha = rotation * Math.PI / 180.0;
+ float cos = (float)Math.cos(alpha);
+ float sin = (float)Math.sin(alpha);
+ canvas.concatCTM(cos, sin, -sin, cos, x, y);
+ if (alignment == Element.ALIGN_LEFT)
+ ct.setSimpleColumn(phrase, 0, -1, 20000, 2, 2, alignment);
+ else if (alignment == Element.ALIGN_RIGHT)
+ ct.setSimpleColumn(phrase, -20000, -1, 0, 2, 2, alignment);
+ else
+ ct.setSimpleColumn(phrase, -20000, -1, 20000, 2, 2, alignment);
+ }
+ if (runDirection == PdfWriter.RUN_DIRECTION_RTL) {
+ if (alignment == Element.ALIGN_LEFT)
+ alignment = Element.ALIGN_RIGHT;
+ else if (alignment == Element.ALIGN_RIGHT)
+ alignment = Element.ALIGN_LEFT;
+ }
+ ct.setAlignment(alignment);
+ ct.setArabicOptions(arabicOptions);
+ ct.setRunDirection(runDirection);
+ try {
+ ct.go();
+ }
+ catch (DocumentException e) {
+ throw new ExceptionConverter(e);
+ }
+ canvas.restoreState();
+ }
+
+ /** Shows a line of text. Only the first line is written.
+ * @param canvas where the text is to be written to
+ * @param alignment the alignment
+ * @param phrase the Phrase
with the text
+ * @param x the x reference position
+ * @param y the y reference position
+ * @param rotation the rotation to be applied in degrees counterclockwise
+ */
+ public static void showTextAligned(PdfContentByte canvas, int alignment, Phrase phrase, float x, float y, float rotation) {
+ showTextAligned(canvas, alignment, phrase, x, y, rotation, PdfWriter.RUN_DIRECTION_NO_BIDI, 0);
+ }
+
+ protected int goComposite(boolean simulate) throws DocumentException {
+ if (!rectangularMode)
+ throw new DocumentException("Irregular columns are not supported in composite mode.");
+ linesWritten = 0;
+ descender = 0;
+ boolean firstPass = true;
+ main_loop:
+ while (true) {
+ if (compositeElements.isEmpty())
+ return NO_MORE_TEXT;
+ Element element = (Element)compositeElements.getFirst();
+ if (element.type() == Element.PARAGRAPH) {
+ Paragraph para = (Paragraph)element;
+ int status = 0;
+ for (int keep = 0; keep < 2; ++keep) {
+ float lastY = yLine;
+ boolean createHere = false;
+ if (compositeColumn == null) {
+ compositeColumn = new ColumnText(canvas);
+ compositeColumn.setUseAscender(firstPass ? useAscender : false);
+ compositeColumn.setAlignment(para.alignment());
+ compositeColumn.setIndent(para.indentationLeft() + para.getFirstLineIndent());
+ compositeColumn.setExtraParagraphSpace(para.getExtraParagraphSpace());
+ compositeColumn.setFollowingIndent(para.indentationLeft());
+ compositeColumn.setRightIndent(para.indentationRight());
+ compositeColumn.setLeading(para.leading(), para.getMultipliedLeading());
+ compositeColumn.setRunDirection(runDirection);
+ compositeColumn.setArabicOptions(arabicOptions);
+ compositeColumn.setSpaceCharRatio(spaceCharRatio);
+ compositeColumn.addText(para);
+ if (!firstPass) {
+ yLine -= para.spacingBefore();
+ }
+ createHere = true;
+ }
+ compositeColumn.leftX = leftX;
+ compositeColumn.rightX = rightX;
+ compositeColumn.yLine = yLine;
+ compositeColumn.rectangularWidth = rectangularWidth;
+ compositeColumn.rectangularMode = rectangularMode;
+ compositeColumn.minY = minY;
+ compositeColumn.maxY = maxY;
+ boolean keepCandidate = (para.getKeepTogether() && createHere && !firstPass);
+ status = compositeColumn.go(simulate || (keepCandidate && keep == 0));
+ updateFilledWidth(compositeColumn.filledWidth);
+ if ((status & NO_MORE_TEXT) == 0 && keepCandidate) {
+ compositeColumn = null;
+ yLine = lastY;
+ return NO_MORE_COLUMN;
+ }
+ if (simulate || !keepCandidate)
+ break;
+ if (keep == 0) {
+ compositeColumn = null;
+ yLine = lastY;
+ }
+ }
+ firstPass = false;
+ yLine = compositeColumn.yLine;
+ linesWritten += compositeColumn.linesWritten;
+ descender = compositeColumn.descender;
+ if ((status & NO_MORE_TEXT) != 0) {
+ compositeColumn = null;
+ compositeElements.removeFirst();
+ yLine -= para.spacingAfter();
+ }
+ if ((status & NO_MORE_COLUMN) != 0) {
+ return NO_MORE_COLUMN;
+ }
+ }
+ else if (element.type() == Element.LIST) {
+ com.lowagie.text.List list = (com.lowagie.text.List)element;
+ ArrayList items = list.getItems();
+ ListItem item = null;
+ float listIndentation = list.indentationLeft();
+ int count = 0;
+ Stack stack = new Stack();
+ for (int k = 0; k < items.size(); ++k) {
+ Object obj = items.get(k);
+ if (obj instanceof ListItem) {
+ if (count == listIdx) {
+ item = (ListItem)obj;
+ break;
+ }
+ else ++count;
+ }
+ else if (obj instanceof com.lowagie.text.List) {
+ stack.push(new Object[]{list, new Integer(k), new Float(listIndentation)});
+ list = (com.lowagie.text.List)obj;
+ items = list.getItems();
+ listIndentation += list.indentationLeft();
+ k = -1;
+ continue;
+ }
+ if (k == items.size() - 1) {
+ if (!stack.isEmpty()) {
+ Object objs[] = (Object[])stack.pop();
+ list = (com.lowagie.text.List)objs[0];
+ items = list.getItems();
+ k = ((Integer)objs[1]).intValue();
+ listIndentation = ((Float)objs[2]).floatValue();
+ }
+ }
+ }
+ int status = 0;
+ for (int keep = 0; keep < 2; ++keep) {
+ float lastY = yLine;
+ boolean createHere = false;
+ if (compositeColumn == null) {
+ if (item == null) {
+ listIdx = 0;
+ compositeElements.removeFirst();
+ continue main_loop;
+ }
+ compositeColumn = new ColumnText(canvas);
+ compositeColumn.setUseAscender(firstPass ? useAscender : false);
+ compositeColumn.setAlignment(item.alignment());
+ compositeColumn.setIndent(item.indentationLeft() + listIndentation + item.getFirstLineIndent());
+ compositeColumn.setExtraParagraphSpace(item.getExtraParagraphSpace());
+ compositeColumn.setFollowingIndent(compositeColumn.getIndent());
+ compositeColumn.setRightIndent(item.indentationRight() + list.indentationRight());
+ compositeColumn.setLeading(item.leading(), item.getMultipliedLeading());
+ compositeColumn.setRunDirection(runDirection);
+ compositeColumn.setArabicOptions(arabicOptions);
+ compositeColumn.setSpaceCharRatio(spaceCharRatio);
+ compositeColumn.addText(item);
+ if (!firstPass) {
+ yLine -= item.spacingBefore();
+ }
+ createHere = true;
+ }
+ compositeColumn.leftX = leftX;
+ compositeColumn.rightX = rightX;
+ compositeColumn.yLine = yLine;
+ compositeColumn.rectangularWidth = rectangularWidth;
+ compositeColumn.rectangularMode = rectangularMode;
+ compositeColumn.minY = minY;
+ compositeColumn.maxY = maxY;
+ boolean keepCandidate = (item.getKeepTogether() && createHere && !firstPass);
+ status = compositeColumn.go(simulate || (keepCandidate && keep == 0));
+ updateFilledWidth(compositeColumn.filledWidth);
+ if ((status & NO_MORE_TEXT) == 0 && keepCandidate) {
+ compositeColumn = null;
+ yLine = lastY;
+ return NO_MORE_COLUMN;
+ }
+ if (simulate || !keepCandidate)
+ break;
+ if (keep == 0) {
+ compositeColumn = null;
+ yLine = lastY;
+ }
+ }
+ firstPass = false;
+ yLine = compositeColumn.yLine;
+ linesWritten += compositeColumn.linesWritten;
+ descender = compositeColumn.descender;
+ if (!Float.isNaN(compositeColumn.firstLineY) && !compositeColumn.firstLineYDone) {
+ if (!simulate)
+ showTextAligned(canvas, Element.ALIGN_LEFT, new Phrase(item.listSymbol()), compositeColumn.leftX + listIndentation, compositeColumn.firstLineY, 0);
+ compositeColumn.firstLineYDone = true;
+ }
+ if ((status & NO_MORE_TEXT) != 0) {
+ compositeColumn = null;
+ ++listIdx;
+ yLine -= item.spacingAfter();
+ }
+ if ((status & NO_MORE_COLUMN) != 0) {
+ return NO_MORE_COLUMN;
+ }
+ }
+ else if (element.type() == Element.PTABLE) {
+ if (yLine < minY || yLine > maxY)
+ return NO_MORE_COLUMN;
+ PdfPTable table = (PdfPTable)element;
+ if (table.size() <= table.getHeaderRows()) {
+ compositeElements.removeFirst();
+ continue;
+ }
+ float yTemp = yLine;
+ float yLineWrite = yLine;
+ if (!firstPass && listIdx == 0) {
+ yTemp -= table.spacingBefore();
+ yLineWrite = yTemp;
+ }
+ currentLeading = 0;
+ if (yTemp < minY || yTemp > maxY)
+ return NO_MORE_COLUMN;
+ float x1 = leftX;
+ float tableWidth;
+ if (table.isLockedWidth()) {
+ tableWidth = table.getTotalWidth();
+ updateFilledWidth(tableWidth);
+ }
+ else {
+ tableWidth = rectangularWidth * table.getWidthPercentage() / 100f;
+ table.setTotalWidth(tableWidth);
+ }
+ int k;
+ boolean skipHeader = (!firstPass && table.isSkipFirstHeader() && listIdx <= table.getHeaderRows());
+ if (!skipHeader) {
+ yTemp -= table.getHeaderHeight();
+ if (yTemp < minY || yTemp > maxY) {
+ if (firstPass) {
+ compositeElements.removeFirst();
+ continue;
+ }
+ return NO_MORE_COLUMN;
+ }
+ }
+ if (listIdx < table.getHeaderRows())
+ listIdx = table.getHeaderRows();
+ for (k = listIdx; k < table.size(); ++k) {
+ float rowHeight = table.getRowHeight(k);
+ if (yTemp - rowHeight < minY)
+ break;
+ yTemp -= rowHeight;
+ }
+ if (k < table.size()) {
+ if (table.isSplitRows() && (!table.isSplitLate() || (k == listIdx && firstPass))) {
+ if (!splittedRow) {
+ splittedRow = true;
+ table = new PdfPTable(table);
+ compositeElements.set(0, table);
+ ArrayList rows = table.getRows();
+ for (int i = table.getHeaderRows(); i < listIdx; ++i)
+ rows.set(i, null);
+ }
+ float h = yTemp - minY;
+ PdfPRow newRow = table.getRow(k).splitRow(h);
+ if (newRow == null) {
+ if (k == listIdx)
+ return NO_MORE_COLUMN;
+ }
+ else {
+ yTemp = minY;
+ table.getRows().add(++k, newRow);
+ }
+ }
+ else if (!table.isSplitRows() && k == listIdx && firstPass) {
+ compositeElements.removeFirst();
+ splittedRow = false;
+ continue;
+ }
+ else if (k == listIdx && !firstPass && (!table.isSplitRows() || table.isSplitLate())) {
+ return NO_MORE_COLUMN;
+ }
+ }
+ firstPass = false;
+ if (!simulate) {
+ switch (table.getHorizontalAlignment()) {
+ case Element.ALIGN_LEFT:
+ break;
+ case Element.ALIGN_RIGHT:
+ x1 += rectangularWidth - tableWidth;
+ break;
+ default:
+ x1 += (rectangularWidth - tableWidth) / 2f;
+ }
+ int realHeaderRows = table.getHeaderRows();
+ int footerRows = table.getFooterRows();
+ if (footerRows > realHeaderRows)
+ footerRows = realHeaderRows;
+ realHeaderRows -= footerRows;
+ PdfPTable nt = PdfPTable.shallowCopy(table);
+ ArrayList rows = table.getRows();
+ ArrayList sub = nt.getRows();
+ if (!skipHeader) {
+ for (int j = 0; j < realHeaderRows; ++j)
+ sub.add(rows.get(j));
+ }
+ else
+ nt.setHeaderRows(footerRows);
+ for (int j = listIdx; j < k; ++j)
+ sub.add(rows.get(j));
+ for (int j = 0; j < footerRows; ++j)
+ sub.add(rows.get(j + realHeaderRows));
+ float rowHeight = 0;
+ if (table.isExtendLastRow()) {
+ PdfPRow last = (PdfPRow)sub.get(sub.size() - 1 - footerRows);
+ rowHeight = last.getMaxHeights();
+ last.setMaxHeights(yTemp - minY + rowHeight);
+ yTemp = minY;
+ }
+ if (canvases != null)
+ nt.writeSelectedRows(0, -1, x1, yLineWrite, canvases);
+ else
+ nt.writeSelectedRows(0, -1, x1, yLineWrite, canvas);
+ if (table.isExtendLastRow()) {
+ PdfPRow last = (PdfPRow)sub.get(sub.size() - 1 - footerRows);
+ last.setMaxHeights(rowHeight);
+ }
+ }
+ else if (table.isExtendLastRow() && minY > PdfPRow.BOTTOM_LIMIT)
+ yTemp = minY;
+ yLine = yTemp;
+ if (k >= table.size()) {
+ yLine -= table.spacingAfter();
+ compositeElements.removeFirst();
+ splittedRow = false;
+ listIdx = 0;
+ }
+ else {
+ if (splittedRow) {
+ ArrayList rows = table.getRows();
+ for (int i = listIdx; i < k; ++i)
+ rows.set(i, null);
+ }
+ listIdx = k;
+ return NO_MORE_COLUMN;
+ }
+ }
+ else if (element.type() == Element.GRAPHIC) {
+ if (!simulate) {
+ Graphic gr = (Graphic)element;
+ ByteBuffer bf = gr.getInternalBuffer();
+ ByteBuffer store = null;
+ if (bf.size() > 0) {
+ store = new ByteBuffer();
+ store.append(bf);
+ bf.reset();
+ }
+ gr.processAttributes(leftX, minY, rightX, maxY, yLine);
+ canvas.add(gr);
+ bf.reset();
+ if (store != null) {
+ bf.append(store);
+ }
+ }
+ compositeElements.removeFirst();
+ }
+ else
+ compositeElements.removeFirst();
+ }
+ }
+
+ /**
+ * Gets the canvas.
+ * @return a PdfContentByte.
+ */
+ public PdfContentByte getCanvas() {
+ return canvas;
+ }
+
+ /**
+ * Sets the canvas.
+ * @param canvas
+ */
+ public void setCanvas(PdfContentByte canvas) {
+ this.canvas = canvas;
+ this.canvases = null;
+ if (compositeColumn != null)
+ compositeColumn.setCanvas(canvas);
+ }
+
+ /**
+ * Sets the canvases.
+ * @param canvases
+ */
+ public void setCanvases(PdfContentByte[] canvases) {
+ this.canvases = canvases;
+ this.canvas = canvases[PdfPTable.TEXTCANVAS];
+ if (compositeColumn != null)
+ compositeColumn.setCanvases(canvases);
+ }
+
+ /**
+ * Gets the canvases.
+ * @return an array of PdfContentByte
+ */
+ public PdfContentByte[] getCanvases() {
+ return canvases;
+ }
+
+ /**
+ * Checks if the element has a height of 0.
+ * @return true or false
+ */
+ public boolean zeroHeightElement() {
+ return composite == true && compositeElements.size() > 0 && ((Element)compositeElements.getFirst()).type() == Element.GRAPHIC;
+ }
+
+ /**
+ * Checks if UseAscender is enabled/disabled.
+ * @return true is the adjustment of the first line height is based on max ascender.
+ */
+ public boolean isUseAscender() {
+ return useAscender;
+ }
+
+ /**
+ * Enables/Disables adjustment of first line height based on max ascender.
+ * @param use enable adjustment if true
+ */
+ public void setUseAscender(boolean use) {
+ useAscender = use;
+ }
+
+ /**
+ * Checks the status variable and looks if there's still some text.
+ */
+ public static boolean hasMoreText(int status) {
+ return (status & ColumnText.NO_MORE_TEXT) == 0;
+ }
+
+ /**
+ * Holds value of property filledWidth.
+ */
+ private float filledWidth;
+
+ /**
+ * Gets the real width used by the largest line.
+ * @return the real width used by the largest line
+ */
+ public float getFilledWidth() {
+
+ return this.filledWidth;
+ }
+
+ /**
+ * Sets the real width used by the largest line. Only used to set it
+ * to zero to start another measurement.
+ * @param filledWidth the real width used by the largest line
+ */
+ public void setFilledWidth(float filledWidth) {
+
+ this.filledWidth = filledWidth;
+ }
+
+ /**
+ * Replaces the filledWidth
if greater than the existing one.
+ * @param w the new filledWidth
if greater than the existing one
+ */
+ public void updateFilledWidth(float w) {
+ if (w > filledWidth)
+ filledWidth = w;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/DefaultFontMapper.java b/src/main/java/com/lowagie/text/pdf/DefaultFontMapper.java
new file mode 100644
index 0000000..bf3b645
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/DefaultFontMapper.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.awt.Font;
+import com.lowagie.text.ExceptionConverter;
+import java.util.HashMap;
+import java.io.File;
+/** Default class to map awt fonts to BaseFont.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+
+public class DefaultFontMapper implements FontMapper {
+
+ /** A representation of BaseFont parameters.
+ */
+ public static class BaseFontParameters {
+ /** The font name.
+ */
+ public String fontName;
+ /** The encoding for that font.
+ */
+ public String encoding;
+ /** The embedding for that font.
+ */
+ public boolean embedded;
+ /** Whether the font is cached of not.
+ */
+ public boolean cached;
+ /** The font bytes for ttf and afm.
+ */
+ public byte ttfAfm[];
+ /** The font bytes for pfb.
+ */
+ public byte pfb[];
+
+ /** Constructs default BaseFont parameters.
+ * @param fontName the font name or location
+ */
+ public BaseFontParameters(String fontName) {
+ this.fontName = fontName;
+ encoding = BaseFont.CP1252;
+ embedded = BaseFont.EMBEDDED;
+ cached = BaseFont.CACHED;
+ }
+ }
+
+ /** Maps aliases to names.
+ */
+ private HashMap aliases = new HashMap();
+ /** Maps names to BaseFont parameters.
+ */
+ private HashMap mapper = new HashMap();
+ /**
+ * Returns a BaseFont which can be used to represent the given AWT Font
+ *
+ * @param font the font to be converted
+ * @return a BaseFont which has similar properties to the provided Font
+ */
+
+ public BaseFont awtToPdf(Font font) {
+ try {
+ BaseFontParameters p = getBaseFontParameters(font.getFontName());
+ if (p != null)
+ return BaseFont.createFont(p.fontName, p.encoding, p.embedded, p.cached, p.ttfAfm, p.pfb);
+ String fontKey = null;
+ String logicalName = font.getName();
+
+ if (logicalName.equalsIgnoreCase("DialogInput") || logicalName.equalsIgnoreCase("Monospaced") || logicalName.equalsIgnoreCase("Courier")) {
+
+ if (font.isItalic()) {
+ if (font.isBold()) {
+ fontKey = BaseFont.COURIER_BOLDOBLIQUE;
+
+ } else {
+ fontKey = BaseFont.COURIER_OBLIQUE;
+ }
+
+ } else {
+ if (font.isBold()) {
+ fontKey = BaseFont.COURIER_BOLD;
+
+ } else {
+ fontKey = BaseFont.COURIER;
+ }
+ }
+
+ } else if (logicalName.equalsIgnoreCase("Serif") || logicalName.equalsIgnoreCase("TimesRoman")) {
+
+ if (font.isItalic()) {
+ if (font.isBold()) {
+ fontKey = BaseFont.TIMES_BOLDITALIC;
+
+ } else {
+ fontKey = BaseFont.TIMES_ITALIC;
+ }
+
+ } else {
+ if (font.isBold()) {
+ fontKey = BaseFont.TIMES_BOLD;
+
+ } else {
+ fontKey = BaseFont.TIMES_ROMAN;
+ }
+ }
+
+ } else { // default, this catches Dialog and SansSerif
+
+ if (font.isItalic()) {
+ if (font.isBold()) {
+ fontKey = BaseFont.HELVETICA_BOLDOBLIQUE;
+
+ } else {
+ fontKey = BaseFont.HELVETICA_OBLIQUE;
+ }
+
+ } else {
+ if (font.isBold()) {
+ fontKey = BaseFont.HELVETICA_BOLD;
+ } else {
+ fontKey = BaseFont.HELVETICA;
+ }
+ }
+ }
+ return BaseFont.createFont(fontKey, BaseFont.CP1252, false);
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Returns an AWT Font which can be used to represent the given BaseFont
+ *
+ * @param font the font to be converted
+ * @param size the desired point size of the resulting font
+ * @return a Font which has similar properties to the provided BaseFont
+ */
+
+ public Font pdfToAwt(BaseFont font, int size) {
+ String names[][] = font.getFullFontName();
+ if (names.length == 1)
+ return new Font(names[0][3], 0, size);
+ String name10 = null;
+ String name3x = null;
+ for (int k = 0; k < names.length; ++k) {
+ String name[] = names[k];
+ if (name[0].equals("1") && name[1].equals("0"))
+ name10 = name[3];
+ else if (name[2].equals("1033")) {
+ name3x = name[3];
+ break;
+ }
+ }
+ String finalName = name3x;
+ if (finalName == null)
+ finalName = name10;
+ if (finalName == null)
+ finalName = names[0][3];
+ return new Font(finalName, 0, size);
+ }
+
+ /** Maps a name to a BaseFont parameter.
+ * @param awtName the name
+ * @param parameters the BaseFont parameter
+ */
+ public void putName(String awtName, BaseFontParameters parameters) {
+ mapper.put(awtName, parameters);
+ }
+
+ /** Maps an alias to a name.
+ * @param alias the alias
+ * @param awtName the name
+ */
+ public void putAlias(String alias, String awtName) {
+ aliases.put(alias, awtName);
+ }
+
+ /** Looks for a BaseFont parameter associated with a name.
+ * @param name the name
+ * @return the BaseFont parameter or null
if not found.
+ */
+ public BaseFontParameters getBaseFontParameters(String name) {
+ String alias = (String)aliases.get(name);
+ if (alias == null)
+ return (BaseFontParameters)mapper.get(name);
+ BaseFontParameters p = (BaseFontParameters)mapper.get(alias);
+ if (p == null)
+ return (BaseFontParameters)mapper.get(name);
+ else
+ return p;
+ }
+
+ /**
+ * Inserts the names in this map.
+ * @param allNames the returned value of calling {@link BaseFont#getAllFontNames(String, String, byte[])}
+ * @param path the full path to the font
+ */
+ public void insertNames(Object allNames[], String path) {
+ String names[][] = (String[][])allNames[2];
+ String main = null;
+ for (int k = 0; k < names.length; ++k) {
+ String name[] = names[k];
+ if (name[2].equals("1033")) {
+ main = name[3];
+ break;
+ }
+ }
+ if (main == null)
+ main = names[0][3];
+ BaseFontParameters p = new BaseFontParameters(path);
+ mapper.put(main, p);
+ for (int k = 0; k < names.length; ++k) {
+ aliases.put(names[k][3], main);
+ }
+ aliases.put(allNames[0], main);
+ }
+
+ /** Inserts all the fonts recognized by iText in the
+ * directory
into the map. The encoding
+ * will be BaseFont.CP1252
but can be
+ * changed later.
+ * @param dir the directory to scan
+ * @return the number of files processed
+ */
+ public int insertDirectory(String dir) {
+ File file = new File(dir);
+ if (!file.exists() || !file.isDirectory())
+ return 0;
+ File files[] = file.listFiles();
+ int count = 0;
+ for (int k = 0; k < files.length; ++k) {
+ file = files[k];
+ String name = file.getPath().toLowerCase();
+ try {
+ if (name.endsWith(".ttf") || name.endsWith(".otf") || name.endsWith(".afm")) {
+ Object allNames[] = BaseFont.getAllFontNames(file.getPath(), BaseFont.CP1252, null);
+ insertNames(allNames, file.getPath());
+ ++count;
+ }
+ else if (name.endsWith(".ttc")) {
+ String ttcs[] = BaseFont.enumerateTTCNames(file.getPath());
+ for (int j = 0; j < ttcs.length; ++j) {
+ String nt = file.getPath() + "," + j;
+ Object allNames[] = BaseFont.getAllFontNames(nt, BaseFont.CP1252, null);
+ insertNames(allNames, nt);
+ }
+ ++count;
+ }
+ }
+ catch (Exception e) {
+ }
+ }
+ return count;
+ }
+
+ public HashMap getMapper() {
+ return mapper;
+ }
+
+ public HashMap getAliases() {
+ return aliases;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/DocumentFont.java b/src/main/java/com/lowagie/text/pdf/DocumentFont.java
new file mode 100644
index 0000000..b3c4942
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/DocumentFont.java
@@ -0,0 +1,599 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.ExceptionConverter;
+import java.util.HashMap;
+
+/**
+ *
+ * @author psoares
+ */
+public class DocumentFont extends BaseFont {
+ // code, [glyph, width]
+ private HashMap metrics = new HashMap();
+ private String fontName;
+ private PRIndirectReference refFont;
+ private PdfDictionary font;
+ private IntHashtable uni2byte = new IntHashtable();
+ private float Ascender = 800;
+ private float CapHeight = 700;
+ private float Descender = -200;
+ private float ItalicAngle = 0;
+ private float llx = -50;
+ private float lly = -200;
+ private float urx = 100;
+ private float ury = 900;
+ private boolean isType0 = false;
+
+ private BaseFont cjkMirror;
+
+ private static String cjkNames[] = {"HeiseiMin-W3", "HeiseiKakuGo-W5", "STSong-Light", "MHei-Medium",
+ "MSung-Light", "HYGoThic-Medium", "HYSMyeongJo-Medium", "MSungStd-Light", "STSongStd-Light",
+ "HYSMyeongJoStd-Medium", "KozMinPro-Regular"};
+
+ private static String cjkEncs[] = {"UniJIS-UCS2-H", "UniJIS-UCS2-H", "UniGB-UCS2-H", "UniCNS-UCS2-H",
+ "UniCNS-UCS2-H", "UniKS-UCS2-H", "UniKS-UCS2-H", "UniCNS-UCS2-H", "UniGB-UCS2-H",
+ "UniKS-UCS2-H", "UniJIS-UCS2-H"};
+
+ private static String cjkNames2[] = {"MSungStd-Light", "STSongStd-Light", "HYSMyeongJoStd-Medium", "KozMinPro-Regular"};
+
+ private static String cjkEncs2[] = {"UniCNS-UCS2-H", "UniGB-UCS2-H", "UniKS-UCS2-H", "UniJIS-UCS2-H",
+ "UniCNS-UTF16-H", "UniGB-UTF16-H", "UniKS-UTF16-H", "UniJIS-UTF16-H"};
+
+ private static final int stdEnc[] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 32,33,34,35,36,37,38,8217,40,41,42,43,44,45,46,47,
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
+ 8216,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,161,162,163,8260,165,402,167,164,39,8220,171,8249,8250,64257,64258,
+ 0,8211,8224,8225,183,0,182,8226,8218,8222,8221,187,8230,8240,0,191,
+ 0,96,180,710,732,175,728,729,168,0,730,184,0,733,731,711,
+ 8212,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,198,0,170,0,0,0,0,321,216,338,186,0,0,0,0,
+ 0,230,0,0,0,305,0,0,322,248,339,223,0,0,0,0};
+
+ /** Creates a new instance of DocumentFont */
+ DocumentFont(PRIndirectReference refFont) {
+ encoding = "";
+ fontSpecific = false;
+ this.refFont = refFont;
+ fontType = FONT_TYPE_DOCUMENT;
+ font = (PdfDictionary)PdfReader.getPdfObject(refFont);
+ fontName = PdfName.decodeName(((PdfName)PdfReader.getPdfObject(font.get(PdfName.BASEFONT))).toString());
+ PdfName subType = (PdfName)PdfReader.getPdfObject(font.get(PdfName.SUBTYPE));
+ if (PdfName.TYPE1.equals(subType) || PdfName.TRUETYPE.equals(subType))
+ doType1TT();
+ else {
+ for (int k = 0; k < cjkNames.length; ++k) {
+ if (fontName.startsWith(cjkNames[k])) {
+ fontName = cjkNames[k];
+ try {
+ cjkMirror = BaseFont.createFont(fontName, cjkEncs[k], false);
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ return;
+ }
+ }
+ String enc = PdfName.decodeName(((PdfName)PdfReader.getPdfObject(font.get(PdfName.ENCODING))).toString());
+ for (int k = 0; k < cjkEncs2.length; ++k) {
+ if (enc.startsWith(cjkEncs2[k])) {
+ try {
+ if (k > 3)
+ k -= 4;
+ cjkMirror = BaseFont.createFont(cjkNames2[k], cjkEncs2[k], false);
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ return;
+ }
+ }
+ if (PdfName.TYPE0.equals(subType) && enc.equals("Identity-H")) {
+ processType0(font);
+ isType0 = true;
+ }
+ }
+ }
+
+ private void processType0(PdfDictionary font) {
+ try {
+ byte[] touni = PdfReader.getStreamBytes((PRStream)PdfReader.getPdfObjectRelease(font.get(PdfName.TOUNICODE)));
+ PdfArray df = (PdfArray)PdfReader.getPdfObjectRelease(font.get(PdfName.DESCENDANTFONTS));
+ PdfDictionary cidft = (PdfDictionary)PdfReader.getPdfObjectRelease((PdfObject)df.getArrayList().get(0));
+ PdfNumber dwo = (PdfNumber)PdfReader.getPdfObjectRelease(cidft.get(PdfName.DW));
+ int dw = 1000;
+ if (dwo != null)
+ dw = dwo.intValue();
+ IntHashtable widths = readWidths((PdfArray)PdfReader.getPdfObjectRelease(cidft.get(PdfName.W)));
+ PdfDictionary fontDesc = (PdfDictionary)PdfReader.getPdfObjectRelease(cidft.get(PdfName.FONTDESCRIPTOR));
+ fillFontDesc(fontDesc);
+ fillMetrics(touni, widths, dw);
+ } catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ private IntHashtable readWidths(PdfArray ws) {
+ IntHashtable hh = new IntHashtable();
+ if (ws == null)
+ return hh;
+ ArrayList ar = ws.getArrayList();
+ for (int k = 0; k < ar.size(); ++k) {
+ int c1 = ((PdfNumber)PdfReader.getPdfObjectRelease((PdfObject)ar.get(k))).intValue();
+ PdfObject obj = PdfReader.getPdfObjectRelease((PdfObject)ar.get(++k));
+ if (obj.isArray()) {
+ ArrayList ar2 = ((PdfArray)obj).getArrayList();
+ for (int j = 0; j < ar2.size(); ++j) {
+ int c2 = ((PdfNumber)PdfReader.getPdfObjectRelease((PdfObject)ar2.get(j))).intValue();
+ hh.put(c1++, c2);
+ }
+ }
+ else {
+ int c2 = ((PdfNumber)obj).intValue();
+ int w = ((PdfNumber)PdfReader.getPdfObjectRelease((PdfObject)ar.get(++k))).intValue();
+ for (; c1 <= c2; ++c1)
+ hh.put(c1, w);
+ }
+ }
+ return hh;
+ }
+
+ private String decodeString(PdfString ps) {
+ if (ps.isHexWriting())
+ return PdfEncodings.convertToString(ps.getBytes(), "UnicodeBigUnmarked");
+ else
+ return ps.toUnicodeString();
+ }
+
+ private void fillMetrics(byte[] touni, IntHashtable widths, int dw) {
+ try {
+ PdfContentParser ps = new PdfContentParser(new PRTokeniser(touni));
+ PdfObject ob = null;
+ PdfObject last = null;
+ while ((ob = ps.readPRObject()) != null) {
+ if (ob.type() == PdfContentParser.COMMAND_TYPE) {
+ if (ob.toString().equals("beginbfchar")) {
+ int n = ((PdfNumber)last).intValue();
+ for (int k = 0; k < n; ++k) {
+ String cid = decodeString((PdfString)ps.readPRObject());
+ String uni = decodeString((PdfString)ps.readPRObject());
+ if (uni.length() == 1) {
+ int cidc = (int)cid.charAt(0);
+ int unic = (int)uni.charAt(uni.length() - 1);
+ int w = dw;
+ if (widths.containsKey(cidc))
+ w = widths.get(cidc);
+ metrics.put(new Integer(unic), new int[]{cidc, w});
+ }
+ }
+ }
+ else if (ob.toString().equals("beginbfrange")) {
+ int n = ((PdfNumber)last).intValue();
+ for (int k = 0; k < n; ++k) {
+ String cid1 = decodeString((PdfString)ps.readPRObject());
+ String cid2 = decodeString((PdfString)ps.readPRObject());
+ int cid1c = (int)cid1.charAt(0);
+ int cid2c = (int)cid2.charAt(0);
+ PdfObject ob2 = ps.readPRObject();
+ if (ob2.isString()) {
+ String uni = decodeString((PdfString)ob2);
+ if (uni.length() == 1) {
+ int unic = (int)uni.charAt(uni.length() - 1);
+ for (; cid1c <= cid2c; cid1c++, unic++) {
+ int w = dw;
+ if (widths.containsKey(cid1c))
+ w = widths.get(cid1c);
+ metrics.put(new Integer(unic), new int[]{cid1c, w});
+ }
+ }
+ }
+ else {
+ ArrayList ar = ((PdfArray)ob2).getArrayList();
+ for (int j = 0; j < ar.size(); ++j, ++cid1c) {
+ String uni = decodeString((PdfString)ar.get(j));
+ if (uni.length() == 1) {
+ int unic = (int)uni.charAt(uni.length() - 1);
+ int w = dw;
+ if (widths.containsKey(cid1c))
+ w = widths.get(cid1c);
+ metrics.put(new Integer(unic), new int[]{cid1c, w});
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ last = ob;
+ }
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ private void doType1TT() {
+ PdfObject enc = PdfReader.getPdfObject(font.get(PdfName.ENCODING));
+ if (enc == null)
+ fillEncoding(null);
+ else {
+ if (enc.isName())
+ fillEncoding((PdfName)enc);
+ else {
+ PdfDictionary encDic = (PdfDictionary)enc;
+ enc = PdfReader.getPdfObject(encDic.get(PdfName.BASEENCODING));
+ if (enc == null)
+ fillEncoding(null);
+ else
+ fillEncoding((PdfName)enc);
+ PdfArray diffs = (PdfArray)PdfReader.getPdfObject(encDic.get(PdfName.DIFFERENCES));
+ if (diffs != null) {
+ ArrayList dif = diffs.getArrayList();
+ int currentNumber = 0;
+ for (int k = 0; k < dif.size(); ++k) {
+ PdfObject obj = (PdfObject)dif.get(k);
+ if (obj.isNumber())
+ currentNumber = ((PdfNumber)obj).intValue();
+ else {
+ int c[] = GlyphList.nameToUnicode(PdfName.decodeName(((PdfName)obj).toString()));
+ if (c != null && c.length > 0)
+ uni2byte.put(c[0], currentNumber);
+ ++currentNumber;
+ }
+ }
+ }
+ }
+ }
+ PdfArray newWidths = (PdfArray)PdfReader.getPdfObject(font.get(PdfName.WIDTHS));
+ PdfNumber first = (PdfNumber)PdfReader.getPdfObject(font.get(PdfName.FIRSTCHAR));
+ PdfNumber last = (PdfNumber)PdfReader.getPdfObject(font.get(PdfName.LASTCHAR));
+ if (BuiltinFonts14.containsKey(fontName)) {
+ BaseFont bf;
+ try {
+ bf = BaseFont.createFont(fontName, WINANSI, false);
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ int e[] = uni2byte.toOrderedKeys();
+ for (int k = 0; k < e.length; ++k) {
+ int n = uni2byte.get(e[k]);
+ widths[n] = bf.getRawWidth(n, GlyphList.unicodeToName(e[k]));
+ }
+ Ascender = bf.getFontDescriptor(ASCENT, 1000);
+ CapHeight = bf.getFontDescriptor(CAPHEIGHT, 1000);
+ Descender = bf.getFontDescriptor(DESCENT, 1000);
+ ItalicAngle = bf.getFontDescriptor(ITALICANGLE, 1000);
+ llx = bf.getFontDescriptor(BBOXLLX, 1000);
+ lly = bf.getFontDescriptor(BBOXLLY, 1000);
+ urx = bf.getFontDescriptor(BBOXURX, 1000);
+ ury = bf.getFontDescriptor(BBOXURY, 1000);
+ }
+ if (first != null && last != null && newWidths != null) {
+ int f = first.intValue();
+ ArrayList ar = newWidths.getArrayList();
+ for (int k = 0; k < ar.size(); ++k) {
+ widths[f + k] = ((PdfNumber)ar.get(k)).intValue();
+ }
+ }
+ fillFontDesc((PdfDictionary)PdfReader.getPdfObject(font.get(PdfName.FONTDESCRIPTOR)));
+ }
+
+ private void fillFontDesc(PdfDictionary fontDesc) {
+ if (fontDesc == null)
+ return;
+ PdfNumber v = (PdfNumber)PdfReader.getPdfObject(fontDesc.get(PdfName.ASCENT));
+ if (v != null)
+ Ascender = v.floatValue();
+ v = (PdfNumber)PdfReader.getPdfObject(fontDesc.get(PdfName.CAPHEIGHT));
+ if (v != null)
+ CapHeight = v.floatValue();
+ v = (PdfNumber)PdfReader.getPdfObject(fontDesc.get(PdfName.DESCENT));
+ if (v != null)
+ Descender = v.floatValue();
+ v = (PdfNumber)PdfReader.getPdfObject(fontDesc.get(PdfName.ITALICANGLE));
+ if (v != null)
+ ItalicAngle = v.floatValue();
+ PdfArray bbox = (PdfArray)PdfReader.getPdfObject(fontDesc.get(PdfName.FONTBBOX));
+ if (bbox != null) {
+ ArrayList ar = bbox.getArrayList();
+ llx = ((PdfNumber)ar.get(0)).floatValue();
+ lly = ((PdfNumber)ar.get(1)).floatValue();
+ urx = ((PdfNumber)ar.get(2)).floatValue();
+ ury = ((PdfNumber)ar.get(3)).floatValue();
+ if (llx > urx) {
+ float t = llx;
+ llx = urx;
+ urx = t;
+ }
+ if (lly > ury) {
+ float t = lly;
+ lly = ury;
+ ury = t;
+ }
+ }
+ }
+
+ private void fillEncoding(PdfName encoding) {
+ if (PdfName.MAC_ROMAN_ENCODING.equals(encoding) || PdfName.WIN_ANSI_ENCODING.equals(encoding)) {
+ byte b[] = new byte[256];
+ for (int k = 0; k < 256; ++k)
+ b[k] = (byte)k;
+ String enc = WINANSI;
+ if (PdfName.MAC_ROMAN_ENCODING.equals(encoding))
+ enc = MACROMAN;
+ String cv = PdfEncodings.convertToString(b, enc);
+ char arr[] = cv.toCharArray();
+ for (int k = 0; k < 256; ++k)
+ uni2byte.put(arr[k], k);
+ }
+ else {
+ for (int k = 0; k < 256; ++k)
+ uni2byte.put(stdEnc[k], k);
+ }
+ }
+
+ /** Gets the family name of the font. If it is a True Type font
+ * each array element will have {Platform ID, Platform Encoding ID,
+ * Language ID, font name}. The interpretation of this values can be
+ * found in the Open Type specification, chapter 2, in the 'name' table.
+ * For the other fonts the array has a single element with {"", "", "",
+ * font name}.
+ * @return the family name of the font
+ *
+ */
+ public String[][] getFamilyFontName() {
+ return null;
+ }
+
+ /** Gets the font parameter identified by key
. Valid values
+ * for key
are ASCENT
, CAPHEIGHT
, DESCENT
,
+ * ITALICANGLE
, BBOXLLX
, BBOXLLY
, BBOXURX
+ * and BBOXURY
.
+ * @param key the parameter to be extracted
+ * @param fontSize the font size in points
+ * @return the parameter in points
+ *
+ */
+ public float getFontDescriptor(int key, float fontSize) {
+ if (cjkMirror != null)
+ return cjkMirror.getFontDescriptor(key, fontSize);
+ switch (key) {
+ case AWT_ASCENT:
+ case ASCENT:
+ return Ascender * fontSize / 1000;
+ case CAPHEIGHT:
+ return CapHeight * fontSize / 1000;
+ case AWT_DESCENT:
+ case DESCENT:
+ return Descender * fontSize / 1000;
+ case ITALICANGLE:
+ return ItalicAngle;
+ case BBOXLLX:
+ return llx * fontSize / 1000;
+ case BBOXLLY:
+ return lly * fontSize / 1000;
+ case BBOXURX:
+ return urx * fontSize / 1000;
+ case BBOXURY:
+ return ury * fontSize / 1000;
+ case AWT_LEADING:
+ return 0;
+ case AWT_MAXADVANCE:
+ return (urx - llx) * fontSize / 1000;
+ }
+ return 0;
+ }
+
+ /** Gets the full name of the font. If it is a True Type font
+ * each array element will have {Platform ID, Platform Encoding ID,
+ * Language ID, font name}. The interpretation of this values can be
+ * found in the Open Type specification, chapter 2, in the 'name' table.
+ * For the other fonts the array has a single element with {"", "", "",
+ * font name}.
+ * @return the full name of the font
+ *
+ */
+ public String[][] getFullFontName() {
+ return null;
+ }
+
+ /** Gets the kerning between two Unicode chars.
+ * @param char1 the first char
+ * @param char2 the second char
+ * @return the kerning to be applied
+ *
+ */
+ public int getKerning(char char1, char char2) {
+ return 0;
+ }
+
+ /** Gets the postscript font name.
+ * @return the postscript font name
+ *
+ */
+ public String getPostscriptFontName() {
+ return fontName;
+ }
+
+ /** Gets the width from the font according to the Unicode char c
+ * or the name
. If the name
is null it's a symbolic font.
+ * @param c the unicode char
+ * @param name the glyph name
+ * @return the width of the char
+ *
+ */
+ int getRawWidth(int c, String name) {
+ return 0;
+ }
+
+ /** Checks if the font has any kerning pairs.
+ * @return true
if the font has any kerning pairs
+ *
+ */
+ public boolean hasKernPairs() {
+ return false;
+ }
+
+ /** Outputs to the writer the font dictionaries and streams.
+ * @param writer the writer for this document
+ * @param ref the font indirect reference
+ * @param params several parameters that depend on the font type
+ * @throws IOException on error
+ * @throws DocumentException error in generating the object
+ *
+ */
+ void writeFont(PdfWriter writer, PdfIndirectReference ref, Object[] params) throws DocumentException, IOException {
+ }
+
+ public int getWidth(String text) {
+ if (cjkMirror != null)
+ return cjkMirror.getWidth(text);
+ else if (isType0) {
+ char[] chars = text.toCharArray();
+ int len = chars.length;
+ int total = 0;
+ for (int k = 0; k < len; ++k) {
+ int[] ws = (int[])metrics.get(new Integer((int)chars[k]));
+ if (ws != null)
+ total += ws[1];
+ }
+ return total;
+ }
+ else
+ return super.getWidth(text);
+ }
+
+ byte[] convertToBytes(String text) {
+ if (cjkMirror != null)
+ return PdfEncodings.convertToBytes(text, CJKFont.CJK_ENCODING);
+ else if (isType0) {
+ char[] chars = text.toCharArray();
+ int len = chars.length;
+ byte[] b = new byte[len * 2];
+ int bptr = 0;
+ for (int k = 0; k < len; ++k) {
+ int[] ws = (int[])metrics.get(new Integer((int)chars[k]));
+ if (ws != null) {
+ int g = ws[0];
+ b[bptr++] = (byte)(g / 256);
+ b[bptr++] = (byte)(g);
+ }
+ }
+ if (bptr == b.length)
+ return b;
+ else {
+ byte[] nb = new byte[bptr];
+ System.arraycopy(b, 0, nb, 0, bptr);
+ return nb;
+ }
+ }
+ else {
+ char cc[] = text.toCharArray();
+ byte b[] = new byte[cc.length];
+ int ptr = 0;
+ for (int k = 0; k < cc.length; ++k) {
+ if (uni2byte.containsKey(cc[k]))
+ b[ptr++] = (byte)uni2byte.get(cc[k]);
+ }
+ if (ptr == b.length)
+ return b;
+ else {
+ byte[] b2 = new byte[ptr];
+ System.arraycopy(b, 0, b2, 0, ptr);
+ return b2;
+ }
+ }
+ }
+
+ PdfIndirectReference getIndirectReference() {
+ return refFont;
+ }
+
+ public boolean charExists(char c) {
+ if (cjkMirror != null)
+ return cjkMirror.charExists(c);
+ else if (isType0) {
+ return metrics.containsKey(new Integer((int)c));
+ }
+ else
+ return super.charExists(c);
+ }
+
+ /**
+ * Sets the font name that will appear in the pdf font dictionary.
+ * It does nothing in this case as the font is already in the document.
+ * @param name the new font name
+ */
+ public void setPostscriptFontName(String name) {
+ }
+
+ public boolean setKerning(char char1, char char2, int kern) {
+ return false;
+ }
+
+ public int[] getCharBBox(char c) {
+ return null;
+ }
+
+ protected int[] getRawCharBBox(int c, String name) {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/EnumerateTTC.java b/src/main/java/com/lowagie/text/pdf/EnumerateTTC.java
new file mode 100644
index 0000000..d4110a3
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/EnumerateTTC.java
@@ -0,0 +1,118 @@
+/*
+ * $Id: EnumerateTTC.java,v 1.45 2005/05/04 14:31:46 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.*;
+import java.util.HashMap;
+import com.lowagie.text.DocumentException;
+/** Enumerates all the fonts inside a True Type Collection.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+class EnumerateTTC extends TrueTypeFont{
+
+ protected String[] names;
+
+ EnumerateTTC(String ttcFile) throws DocumentException, IOException {
+ fileName = ttcFile;
+ rf = new RandomAccessFileOrArray(ttcFile);
+ findNames();
+ }
+
+ EnumerateTTC(byte ttcArray[]) throws DocumentException, IOException {
+ fileName = "Byte array TTC";
+ rf = new RandomAccessFileOrArray(ttcArray);
+ findNames();
+ }
+
+ void findNames() throws DocumentException, IOException {
+ tables = new HashMap();
+
+ try {
+ String mainTag = readStandardString(4);
+ if (!mainTag.equals("ttcf"))
+ throw new DocumentException(fileName + " is not a valid TTC file.");
+ rf.skipBytes(4);
+ int dirCount = rf.readInt();
+ names = new String[dirCount];
+ int dirPos = rf.getFilePointer();
+ for (int dirIdx = 0; dirIdx < dirCount; ++dirIdx) {
+ tables.clear();
+ rf.seek(dirPos);
+ rf.skipBytes(dirIdx * 4);
+ directoryOffset = rf.readInt();
+ rf.seek(directoryOffset);
+ if (rf.readInt() != 0x00010000)
+ throw new DocumentException(fileName + " is not a valid TTF file.");
+ int num_tables = rf.readUnsignedShort();
+ rf.skipBytes(6);
+ for (int k = 0; k < num_tables; ++k) {
+ String tag = readStandardString(4);
+ rf.skipBytes(4);
+ int table_location[] = new int[2];
+ table_location[0] = rf.readInt();
+ table_location[1] = rf.readInt();
+ tables.put(tag, table_location);
+ }
+ names[dirIdx] = getBaseFont();
+ }
+ }
+ finally {
+ if (rf != null)
+ rf.close();
+ }
+ }
+
+ String[] getNames() {
+ return names;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/ExtendedColor.java b/src/main/java/com/lowagie/text/pdf/ExtendedColor.java
new file mode 100644
index 0000000..c0dfc66
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/ExtendedColor.java
@@ -0,0 +1,122 @@
+/*
+ * $Id: ExtendedColor.java,v 1.43 2005/03/29 14:08:15 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.awt.Color;
+/**
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class ExtendedColor extends Color{
+
+ /** a type of extended color. */
+ public static final int TYPE_RGB = 0;
+ /** a type of extended color. */
+ public static final int TYPE_GRAY = 1;
+ /** a type of extended color. */
+ public static final int TYPE_CMYK = 2;
+ /** a type of extended color. */
+ public static final int TYPE_SEPARATION = 3;
+ /** a type of extended color. */
+ public static final int TYPE_PATTERN = 4;
+ /** a type of extended color. */
+ public static final int TYPE_SHADING = 5;
+
+ protected int type;
+
+ /**
+ * Constructs an extended color of a certain type.
+ * @param type
+ */
+ public ExtendedColor(int type) {
+ super(0, 0, 0);
+ this.type = type;
+ }
+
+ /**
+ * Constructs an extended color of a certain type and a certain color.
+ * @param type
+ * @param red
+ * @param green
+ * @param blue
+ */
+ public ExtendedColor(int type, float red, float green, float blue) {
+ super(normalize(red), normalize(green), normalize(blue));
+ this.type = type;
+ }
+
+ /**
+ * Gets the type of this color.
+ * @return one of the types (see constants)
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Gets the type of a given color.
+ * @param color
+ * @return one of the types (see constants)
+ */
+ public static int getType(Color color) {
+ if (color instanceof ExtendedColor)
+ return ((ExtendedColor)color).getType();
+ return TYPE_RGB;
+ }
+
+ static final float normalize(float value) {
+ if (value < 0)
+ return 0;
+ if (value > 1)
+ return 1;
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/ExtraEncoding.java b/src/main/java/com/lowagie/text/pdf/ExtraEncoding.java
new file mode 100644
index 0000000..5862022
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/ExtraEncoding.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2003 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * Classes implementing this interface can create custom encodings or
+ * replace existing ones. It is used in the context of PdfEncoding
.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public interface ExtraEncoding {
+
+ /**
+ * Converts an Unicode string to a byte array according to some encoding.
+ * @param text the Unicode string
+ * @param encoding the requested encoding. It's mainly of use if the same class
+ * supports more than one encoding.
+ * @return the conversion or null
if no conversion is supported
+ */
+ public byte[] charToByte(String text, String encoding);
+
+ /**
+ * Converts a byte array to an Unicode string according to some encoding.
+ * @param b the input byte array
+ * @param encoding the requested encoding. It's mainly of use if the same class
+ * supports more than one encoding.
+ * @return the conversion or null
if no conversion is supported
+ */
+ public String byteToChar(byte b[], String encoding);
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/FdfReader.java b/src/main/java/com/lowagie/text/pdf/FdfReader.java
new file mode 100644
index 0000000..7a846fe
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/FdfReader.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2003 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.net.URL;
+/** Reads an FDF form and makes the fields available
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class FdfReader extends PdfReader {
+
+ HashMap fields;
+ String fileSpec;
+ PdfName encoding;
+
+ /** Reads an FDF form.
+ * @param filename the file name of the form
+ * @throws IOException on error
+ */
+ public FdfReader(String filename) throws IOException {
+ super(filename);
+ }
+
+ /** Reads an FDF form.
+ * @param pdfIn the byte array with the form
+ * @throws IOException on error
+ */
+ public FdfReader(byte pdfIn[]) throws IOException {
+ super(pdfIn);
+ }
+
+ /** Reads an FDF form.
+ * @param url the URL of the document
+ * @throws IOException on error
+ */
+ public FdfReader(URL url) throws IOException {
+ super(url);
+ }
+
+ /** Reads an FDF form.
+ * @param is the InputStream
containing the document. The stream is read to the
+ * end but is not closed
+ * @throws IOException on error
+ */
+ public FdfReader(InputStream is) throws IOException {
+ super(is);
+ }
+
+ protected void readPdf() throws IOException {
+ fields = new HashMap();
+ try {
+ tokens.checkFdfHeader();
+ rebuildXref();
+ readDocObj();
+ }
+ finally {
+ try {
+ tokens.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ readFields();
+ }
+
+ protected void kidNode(PdfDictionary merged, String name) {
+ PdfArray kids = (PdfArray)getPdfObject(merged.get(PdfName.KIDS));
+ if (kids == null || kids.getArrayList().size() == 0) {
+ if (name.length() > 0)
+ name = name.substring(1);
+ fields.put(name, merged);
+ }
+ else {
+ merged.remove(PdfName.KIDS);
+ ArrayList ar = kids.getArrayList();
+ for (int k = 0; k < ar.size(); ++k) {
+ PdfDictionary dic = new PdfDictionary();
+ dic.merge(merged);
+ PdfDictionary newDic = (PdfDictionary)getPdfObject((PdfObject)ar.get(k));
+ PdfString t = (PdfString)getPdfObject(newDic.get(PdfName.T));
+ String newName = name;
+ if (t != null)
+ newName += "." + t.toUnicodeString();
+ dic.merge(newDic);
+ dic.remove(PdfName.T);
+ kidNode(dic, newName);
+ }
+ }
+ }
+
+ protected void readFields() throws IOException {
+ catalog = (PdfDictionary)getPdfObject(trailer.get(PdfName.ROOT));
+ PdfDictionary fdf = (PdfDictionary)getPdfObject(catalog.get(PdfName.FDF));
+ PdfString fs = (PdfString)getPdfObject(fdf.get(PdfName.F));
+ if (fs != null)
+ fileSpec = fs.toUnicodeString();
+ PdfArray fld = (PdfArray)getPdfObject(fdf.get(PdfName.FIELDS));
+ if (fld == null)
+ return;
+ encoding = (PdfName)getPdfObject(fdf.get(PdfName.ENCODING));
+ PdfDictionary merged = new PdfDictionary();
+ merged.put(PdfName.KIDS, fld);
+ kidNode(merged, "");
+ }
+
+ /** Gets all the fields. The map is keyed by the fully qualified
+ * field name and the value is a merged PdfDictionary
+ * with the field content.
+ * @return all the fields
+ */
+ public HashMap getFields() {
+ return fields;
+ }
+
+ /** Gets the field dictionary.
+ * @param name the fully qualified field name
+ * @return the field dictionary
+ */
+ public PdfDictionary getField(String name) {
+ return (PdfDictionary)fields.get(name);
+ }
+
+ /** Gets the field value or null
if the field does not
+ * exist or has no value defined.
+ * @param name the fully qualified field name
+ * @return the field value or null
+ */
+ public String getFieldValue(String name) {
+ PdfDictionary field = (PdfDictionary)fields.get(name);
+ if (field == null)
+ return null;
+ PdfObject v = getPdfObject(field.get(PdfName.V));
+ if (v == null)
+ return null;
+ if (v.isName())
+ return PdfName.decodeName(((PdfName)v).toString());
+ else if (v.isString()) {
+ PdfString vs = (PdfString)v;
+ if (encoding == null || vs.getEncoding() != null)
+ return vs.toUnicodeString();
+ byte b[] = vs.getBytes();
+ if (b.length >= 2 && b[0] == (byte)254 && b[1] == (byte)255)
+ return vs.toUnicodeString();
+ try {
+ if (encoding.equals(PdfName.SHIFT_JIS))
+ return new String(b, "SJIS");
+ else if (encoding.equals(PdfName.UHC))
+ return new String(b, "MS949");
+ else if (encoding.equals(PdfName.GBK))
+ return new String(b, "GBK");
+ else if (encoding.equals(PdfName.BIGFIVE))
+ return new String(b, "Big5");
+ }
+ catch (Exception e) {
+ }
+ return vs.toUnicodeString();
+ }
+ return null;
+ }
+
+ /** Gets the PDF file specification contained in the FDF.
+ * @return the PDF file specification contained in the FDF
+ */
+ public String getFileSpec() {
+ return fileSpec;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/FdfWriter.java b/src/main/java/com/lowagie/text/pdf/FdfWriter.java
new file mode 100644
index 0000000..1174e8f
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/FdfWriter.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2003 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+import com.lowagie.text.DocWriter;
+import com.lowagie.text.DocumentException;
+
+/** Writes an FDF form.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class FdfWriter {
+ static byte[] HEADER_FDF = DocWriter.getISOBytes("%FDF-1.2\n%\u00e2\u00e3\u00cf\u00d3\n");
+ HashMap fields = new HashMap();
+
+ /** The PDF file associated with the FDF. */
+ private String file;
+
+ /** Creates a new FdfWriter. */
+ public FdfWriter() {
+ }
+
+ /** Writes the content to a stream.
+ * @param os the stream
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public void writeTo(OutputStream os) throws DocumentException, IOException {
+ Wrt wrt = new Wrt(os, this);
+ wrt.writeTo();
+ }
+
+ boolean setField(String field, PdfObject value) {
+ HashMap map = fields;
+ StringTokenizer tk = new StringTokenizer(field, ".");
+ if (!tk.hasMoreTokens())
+ return false;
+ while (true) {
+ String s = tk.nextToken();
+ Object obj = map.get(s);
+ if (tk.hasMoreTokens()) {
+ if (obj == null) {
+ obj = new HashMap();
+ map.put(s, obj);
+ map = (HashMap)obj;
+ continue;
+ }
+ else if (obj instanceof HashMap)
+ map = (HashMap)obj;
+ else
+ return false;
+ }
+ else {
+ if (obj == null || !(obj instanceof HashMap)) {
+ map.put(s, value);
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+ }
+
+ void iterateFields(HashMap values, HashMap map, String name) {
+ for (Iterator it = map.keySet().iterator(); it.hasNext();) {
+ String s = (String)it.next();
+ Object obj = map.get(s);
+ if (obj instanceof HashMap)
+ iterateFields(values, (HashMap)obj, name + "." + s);
+ else
+ values.put((name + "." + s).substring(1), obj);
+ }
+ }
+
+ /** Removes the field value.
+ * @param field the field name
+ * @return true
if the field was found and removed,
+ * false
otherwise
+ */
+ public boolean removeField(String field) {
+ HashMap map = fields;
+ StringTokenizer tk = new StringTokenizer(field, ".");
+ if (!tk.hasMoreTokens())
+ return false;
+ ArrayList hist = new ArrayList();
+ while (true) {
+ String s = tk.nextToken();
+ Object obj = map.get(s);
+ if (obj == null)
+ return false;
+ hist.add(map);
+ hist.add(s);
+ if (tk.hasMoreTokens()) {
+ if (obj instanceof HashMap)
+ map = (HashMap)obj;
+ else
+ return false;
+ }
+ else {
+ if (obj instanceof HashMap)
+ return false;
+ else
+ break;
+ }
+ }
+ for (int k = hist.size() - 2; k >= 0; k -= 2) {
+ map = (HashMap)hist.get(k);
+ String s = (String)hist.get(k + 1);
+ map.remove(s);
+ if (map.size() > 0)
+ break;
+ }
+ return true;
+ }
+
+ /** Gets all the fields. The map is keyed by the fully qualified
+ * field name and the values are PdfObject
.
+ * @return a map with all the fields
+ */
+ public HashMap getFields() {
+ HashMap values = new HashMap();
+ iterateFields(values, fields, "");
+ return values;
+ }
+
+ /** Gets the field value.
+ * @param field the field name
+ * @return the field value or null
if not found
+ */
+ public String getField(String field) {
+ HashMap map = fields;
+ StringTokenizer tk = new StringTokenizer(field, ".");
+ if (!tk.hasMoreTokens())
+ return null;
+ while (true) {
+ String s = tk.nextToken();
+ Object obj = map.get(s);
+ if (obj == null)
+ return null;
+ if (tk.hasMoreTokens()) {
+ if (obj instanceof HashMap)
+ map = (HashMap)obj;
+ else
+ return null;
+ }
+ else {
+ if (obj instanceof HashMap)
+ return null;
+ else {
+ if (((PdfObject)obj).isString())
+ return ((PdfString)obj).toUnicodeString();
+ else
+ return PdfName.decodeName(obj.toString());
+ }
+ }
+ }
+ }
+
+ /** Sets the field value as a name.
+ * @param field the fully qualified field name
+ * @param value the value
+ * @return true
if the value was inserted,
+ * false
if the name is incompatible with
+ * an existing field
+ */
+ public boolean setFieldAsName(String field, String value) {
+ return setField(field, new PdfName(value));
+ }
+
+ /** Sets the field value as a string.
+ * @param field the fully qualified field name
+ * @param value the value
+ * @return true
if the value was inserted,
+ * false
if the name is incompatible with
+ * an existing field
+ */
+ public boolean setFieldAsString(String field, String value) {
+ return setField(field, new PdfString(value, PdfObject.TEXT_UNICODE));
+ }
+
+ /** Sets all the fields from this FdfReader
+ * @param fdf the FdfReader
+ */
+ public void setFields(FdfReader fdf) {
+ HashMap map = fdf.getFields();
+ for (Iterator it = map.keySet().iterator(); it.hasNext();) {
+ String key = (String)it.next();
+ PdfDictionary dic = (PdfDictionary)map.get(key);
+ PdfObject v = dic.get(PdfName.V);
+ if (v != null) {
+ setField(key, v);
+ }
+ }
+ }
+
+ /** Sets all the fields from this PdfReader
+ * @param pdf the PdfReader
+ */
+ public void setFields(PdfReader pdf) {
+ setFields(pdf.getAcroFields());
+ }
+
+ /** Sets all the fields from this AcroFields
+ * @param af the AcroFields
+ */
+ public void setFields(AcroFields af) {
+ for (Iterator it = af.getFields().entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry)it.next();
+ String fn = (String)entry.getKey();
+ AcroFields.Item item = (AcroFields.Item)entry.getValue();
+ PdfDictionary dic = (PdfDictionary)item.merged.get(0);
+ PdfObject v = PdfReader.getPdfObjectRelease(dic.get(PdfName.V));
+ if (v == null)
+ continue;
+ PdfObject ft = PdfReader.getPdfObjectRelease(dic.get(PdfName.FT));
+ if (ft == null || PdfName.SIG.equals(ft))
+ continue;
+ setField(fn, v);
+ }
+ }
+
+ /** Gets the PDF file name associated with the FDF.
+ * @return the PDF file name associated with the FDF
+ */
+ public String getFile() {
+ return this.file;
+ }
+
+ /** Sets the PDF file name associated with the FDF.
+ * @param file the PDF file name associated with the FDF
+ *
+ */
+ public void setFile(String file) {
+ this.file = file;
+ }
+
+ static class Wrt extends PdfWriter {
+ private FdfWriter fdf;
+
+ Wrt(OutputStream os, FdfWriter fdf) throws DocumentException, IOException {
+ super(new PdfDocument(), os);
+ this.fdf = fdf;
+ this.os.write(HEADER_FDF);
+ body = new PdfBody(this);
+ }
+
+ void writeTo() throws DocumentException, IOException {
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.FIELDS, calculate(fdf.fields));
+ if (fdf.file != null)
+ dic.put(PdfName.F, new PdfString(fdf.file, PdfObject.TEXT_UNICODE));
+ PdfDictionary fd = new PdfDictionary();
+ fd.put(PdfName.FDF, dic);
+ PdfIndirectReference ref = addToBody(fd).getIndirectReference();
+ os.write(getISOBytes("trailer\n"));
+ PdfDictionary trailer = new PdfDictionary();
+ trailer.put(PdfName.ROOT, ref);
+ trailer.toPdf(null, os);
+ os.write(getISOBytes("\n%%EOF\n"));
+ os.close();
+ }
+
+
+ PdfArray calculate(HashMap map) throws IOException {
+ PdfArray ar = new PdfArray();
+ for (Iterator it = map.keySet().iterator(); it.hasNext();) {
+ String key = (String)it.next();
+ Object v = map.get(key);
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.T, new PdfString(key, PdfObject.TEXT_UNICODE));
+ if (v instanceof HashMap) {
+ dic.put(PdfName.KIDS, calculate((HashMap)v));
+ }
+ else {
+ dic.put(PdfName.V, (PdfObject)v);
+ }
+ ar.add(dic);
+ }
+ return ar;
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/FontDetails.java b/src/main/java/com/lowagie/text/pdf/FontDetails.java
new file mode 100644
index 0000000..6aa72e4
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/FontDetails.java
@@ -0,0 +1,275 @@
+/*
+ * $Id: FontDetails.java,v 1.46 2006/02/23 16:45:49 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.ExceptionConverter;
+import java.util.HashMap;
+import java.io.UnsupportedEncodingException;
+/** Each font in the document will have an instance of this class
+ * where the characters used will be represented.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+class FontDetails {
+
+ /** The indirect reference to this font
+ */
+ PdfIndirectReference indirectReference;
+ /** The font name that appears in the document body stream
+ */
+ PdfName fontName;
+ /** The font
+ */
+ BaseFont baseFont;
+ /** The font if its an instance of TrueTypeFontUnicode
+ */
+ TrueTypeFontUnicode ttu;
+
+ CJKFont cjkFont;
+ /** The array used with single byte encodings
+ */
+ byte shortTag[];
+ /** The map used with double byte encodings. The key is Integer(glyph) and the
+ * value is int[]{glyph, width, Unicode code}
+ */
+ HashMap longTag;
+
+ IntHashtable cjkTag;
+ /** The font type
+ */
+ int fontType;
+ /** true
if the font is symbolic
+ */
+ boolean symbolic;
+ /** Indicates if all the glyphs and widths for that particular
+ * encoding should be included in the document.
+ */
+ protected boolean subset = true;
+ /** Each font used in a document has an instance of this class.
+ * This class stores the characters used in the document and other
+ * specifics unique to the current working document.
+ * @param fontName the font name
+ * @param indirectReference the indirect reference to the font
+ * @param baseFont the BaseFont
+ */
+ FontDetails(PdfName fontName, PdfIndirectReference indirectReference, BaseFont baseFont) {
+ this.fontName = fontName;
+ this.indirectReference = indirectReference;
+ this.baseFont = baseFont;
+ fontType = baseFont.getFontType();
+ switch (fontType) {
+ case BaseFont.FONT_TYPE_T1:
+ case BaseFont.FONT_TYPE_TT:
+ shortTag = new byte[256];
+ break;
+ case BaseFont.FONT_TYPE_CJK:
+ cjkTag = new IntHashtable();
+ cjkFont = (CJKFont)baseFont;
+ break;
+ case BaseFont.FONT_TYPE_TTUNI:
+ longTag = new HashMap();
+ ttu = (TrueTypeFontUnicode)baseFont;
+ symbolic = baseFont.isFontSpecific();
+ break;
+ }
+ }
+
+ /** Gets the indirect reference to this font.
+ * @return the indirect reference to this font
+ */
+ PdfIndirectReference getIndirectReference() {
+ return indirectReference;
+ }
+
+ /** Gets the font name as it appears in the document body.
+ * @return the font name
+ */
+ PdfName getFontName() {
+ return fontName;
+ }
+
+ /** Gets the BaseFont
of this font.
+ * @return the BaseFont
of this font
+ */
+ BaseFont getBaseFont() {
+ return baseFont;
+ }
+
+ /** Converts the text into bytes to be placed in the document.
+ * The conversion is done according to the font and the encoding and the characters
+ * used are stored.
+ * @param text the text to convert
+ * @return the conversion
+ */
+ byte[] convertToBytes(String text) {
+ byte b[] = null;
+ switch (fontType) {
+ case BaseFont.FONT_TYPE_T3:
+ return baseFont.convertToBytes(text);
+ case BaseFont.FONT_TYPE_T1:
+ case BaseFont.FONT_TYPE_TT: {
+ b = baseFont.convertToBytes(text);
+ int len = b.length;
+ for (int k = 0; k < len; ++k)
+ shortTag[((int)b[k]) & 0xff] = 1;
+ break;
+ }
+ case BaseFont.FONT_TYPE_CJK: {
+ int len = text.length();
+ for (int k = 0; k < len; ++k)
+ cjkTag.put(cjkFont.getCidCode(text.charAt(k)), 0);
+ b = baseFont.convertToBytes(text);
+ break;
+ }
+ case BaseFont.FONT_TYPE_DOCUMENT: {
+ b = baseFont.convertToBytes(text);
+ break;
+ }
+ case BaseFont.FONT_TYPE_TTUNI: {
+ try {
+ int len = text.length();
+ int metrics[] = null;
+ char glyph[] = new char[len];
+ int i = 0;
+ if (symbolic) {
+ b = PdfEncodings.convertToBytes(text, "symboltt");
+ len = b.length;
+ for (int k = 0; k < len; ++k) {
+ metrics = ttu.getMetricsTT(b[k] & 0xff);
+ if (metrics == null)
+ continue;
+ longTag.put(new Integer(metrics[0]), new int[]{metrics[0], metrics[1], ttu.getUnicodeDifferences(b[k] & 0xff)});
+ glyph[i++] = (char)metrics[0];
+ }
+ }
+ else {
+ for (int k = 0; k < len; ++k) {
+ char c = text.charAt(k);
+ metrics = ttu.getMetricsTT(c);
+ if (metrics == null)
+ continue;
+ int m0 = metrics[0];
+ Integer gl = new Integer(m0);
+ if (!longTag.containsKey(gl))
+ longTag.put(gl, new int[]{m0, metrics[1], c});
+ glyph[i++] = (char)m0;
+ }
+ }
+ String s = new String(glyph, 0, i);
+ b = s.getBytes(CJKFont.CJK_ENCODING);
+ }
+ catch (UnsupportedEncodingException e) {
+ throw new ExceptionConverter(e);
+ }
+ break;
+ }
+ }
+ return b;
+ }
+
+ /** Writes the font definition to the document.
+ * @param writer the PdfWriter
of this document
+ */
+ void writeFont(PdfWriter writer) {
+ try {
+ switch (fontType) {
+ case BaseFont.FONT_TYPE_T3:
+ baseFont.writeFont(writer, indirectReference, null);
+ break;
+ case BaseFont.FONT_TYPE_T1:
+ case BaseFont.FONT_TYPE_TT: {
+ int firstChar;
+ int lastChar;
+ for (firstChar = 0; firstChar < 256; ++firstChar) {
+ if (shortTag[firstChar] != 0)
+ break;
+ }
+ for (lastChar = 255; lastChar >= firstChar; --lastChar) {
+ if (shortTag[lastChar] != 0)
+ break;
+ }
+ if (firstChar > 255) {
+ firstChar = 255;
+ lastChar = 255;
+ }
+ baseFont.writeFont(writer, indirectReference, new Object[]{new Integer(firstChar), new Integer(lastChar), shortTag, new Boolean(subset)});
+ break;
+ }
+ case BaseFont.FONT_TYPE_CJK:
+ baseFont.writeFont(writer, indirectReference, new Object[]{cjkTag});
+ break;
+ case BaseFont.FONT_TYPE_TTUNI:
+ baseFont.writeFont(writer, indirectReference, new Object[]{longTag, new Boolean(subset)});
+ break;
+ }
+ }
+ catch(Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /** Indicates if all the glyphs and widths for that particular
+ * encoding should be included in the document.
+ * @return false
to include all the glyphs and widths.
+ */
+ public boolean isSubset() {
+ return subset;
+ }
+
+ /** Indicates if all the glyphs and widths for that particular
+ * encoding should be included in the document. Set to false
+ * to include all.
+ * @param subset new value of property subset
+ */
+ public void setSubset(boolean subset) {
+ this.subset = subset;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/FontMapper.java b/src/main/java/com/lowagie/text/pdf/FontMapper.java
new file mode 100644
index 0000000..331a3cb
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/FontMapper.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2002 by Jeremy Bowman Font
to be searched for valid characters.
+ * @param font the Font
+ */
+ public void addFont(Font font) {
+ if (font.getBaseFont() != null) {
+ fonts.add(font);
+ return;
+ }
+ BaseFont bf = font.getCalculatedBaseFont(true);
+ Font f2 = new Font(bf, font.size(), font.getCalculatedStyle(), font.color());
+ fonts.add(f2);
+ }
+
+ /**
+ * Process the text so that it will render with a combination of fonts
+ * if needed.
+ * @param text the text
+ * @return a Phrase
with one or more chunks
+ */
+ public Phrase process(String text) {
+ int fsize = fonts.size();
+ if (fsize == 0)
+ throw new IndexOutOfBoundsException("No font is defined.");
+ char cc[] = text.toCharArray();
+ int len = cc.length;
+ StringBuffer sb = new StringBuffer();
+ Font font = null;
+ int lastidx = -1;
+ Phrase ret = new Phrase();
+ for (int k = 0; k < len; ++k) {
+ char c = cc[k];
+ if (c == '\n' || c == '\r') {
+ sb.append(c);
+ continue;
+ }
+ for (int f = 0; f < fsize; ++f) {
+ font = (Font)fonts.get(f);
+ if (font.getBaseFont().charExists(c)) {
+ if (lastidx == f)
+ sb.append(c);
+ else {
+ if (sb.length() > 0 && lastidx != -1) {
+ Chunk ck = new Chunk(sb.toString(), (Font)fonts.get(lastidx));
+ ret.add(ck);
+ sb.setLength(0);
+ }
+ sb.append(c);
+ lastidx = f;
+ }
+ break;
+ }
+ }
+ }
+ if (sb.length() > 0) {
+ Chunk ck = new Chunk(sb.toString(), (Font)fonts.get(lastidx));
+ ret.add(ck);
+ }
+ return ret;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/GlyphList.java b/src/main/java/com/lowagie/text/pdf/GlyphList.java
new file mode 100644
index 0000000..59a3dc1
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/GlyphList.java
@@ -0,0 +1,2200 @@
+/*
+ * $Id: GlyphList.java,v 1.52 2005/05/04 14:32:31 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.HashMap;
+
+public class GlyphList
+{
+ private static HashMap unicode2names = new HashMap();
+ private static HashMap names2unicode = new HashMap();
+
+ private static final int unicode[] =
+ {
+ 0x0041,
+ 0x00C6,
+ 0x01FC,
+ 0xF7E6,
+ 0x00C1,
+ 0xF7E1,
+ 0x0102,
+ 0x00C2,
+ 0xF7E2,
+ 0xF6C9,
+ 0xF7B4,
+ 0x00C4,
+ 0xF7E4,
+ 0x00C0,
+ 0xF7E0,
+ 0x0391,
+ 0x0386,
+ 0x0100,
+ 0x0104,
+ 0x00C5,
+ 0x01FA,
+ 0xF7E5,
+ 0xF761,
+ 0x00C3,
+ 0xF7E3,
+ 0x0042,
+ 0x0392,
+ 0xF6F4,
+ 0xF762,
+ 0x0043,
+ 0x0106,
+ 0xF6CA,
+ 0xF6F5,
+ 0x010C,
+ 0x00C7,
+ 0xF7E7,
+ 0x0108,
+ 0x010A,
+ 0xF7B8,
+ 0x03A7,
+ 0xF6F6,
+ 0xF763,
+ 0x0044,
+ 0x010E,
+ 0x0110,
+ 0x2206,
+ 0x0394,
+ 0xF6CB,
+ 0xF6CC,
+ 0xF6CD,
+ 0xF7A8,
+ 0xF6F7,
+ 0xF764,
+ 0x0045,
+ 0x00C9,
+ 0xF7E9,
+ 0x0114,
+ 0x011A,
+ 0x00CA,
+ 0xF7EA,
+ 0x00CB,
+ 0xF7EB,
+ 0x0116,
+ 0x00C8,
+ 0xF7E8,
+ 0x0112,
+ 0x014A,
+ 0x0118,
+ 0x0395,
+ 0x0388,
+ 0xF765,
+ 0x0397,
+ 0x0389,
+ 0x00D0,
+ 0xF7F0,
+ 0x20AC,
+ 0x0046,
+ 0xF766,
+ 0x0047,
+ 0x0393,
+ 0x011E,
+ 0x01E6,
+ 0x011C,
+ 0x0122,
+ 0x0120,
+ 0xF6CE,
+ 0xF760,
+ 0xF767,
+ 0x0048,
+ 0x25CF,
+ 0x25AA,
+ 0x25AB,
+ 0x25A1,
+ 0x0126,
+ 0x0124,
+ 0xF768,
+ 0xF6CF,
+ 0xF6F8,
+ 0x0049,
+ 0x0132,
+ 0x00CD,
+ 0xF7ED,
+ 0x012C,
+ 0x00CE,
+ 0xF7EE,
+ 0x00CF,
+ 0xF7EF,
+ 0x0130,
+ 0x2111,
+ 0x00CC,
+ 0xF7EC,
+ 0x012A,
+ 0x012E,
+ 0x0399,
+ 0x03AA,
+ 0x038A,
+ 0xF769,
+ 0x0128,
+ 0x004A,
+ 0x0134,
+ 0xF76A,
+ 0x004B,
+ 0x039A,
+ 0x0136,
+ 0xF76B,
+ 0x004C,
+ 0xF6BF,
+ 0x0139,
+ 0x039B,
+ 0x013D,
+ 0x013B,
+ 0x013F,
+ 0x0141,
+ 0xF6F9,
+ 0xF76C,
+ 0x004D,
+ 0xF6D0,
+ 0xF7AF,
+ 0xF76D,
+ 0x039C,
+ 0x004E,
+ 0x0143,
+ 0x0147,
+ 0x0145,
+ 0xF76E,
+ 0x00D1,
+ 0xF7F1,
+ 0x039D,
+ 0x004F,
+ 0x0152,
+ 0xF6FA,
+ 0x00D3,
+ 0xF7F3,
+ 0x014E,
+ 0x00D4,
+ 0xF7F4,
+ 0x00D6,
+ 0xF7F6,
+ 0xF6FB,
+ 0x00D2,
+ 0xF7F2,
+ 0x01A0,
+ 0x0150,
+ 0x014C,
+ 0x2126,
+ 0x03A9,
+ 0x038F,
+ 0x039F,
+ 0x038C,
+ 0x00D8,
+ 0x01FE,
+ 0xF7F8,
+ 0xF76F,
+ 0x00D5,
+ 0xF7F5,
+ 0x0050,
+ 0x03A6,
+ 0x03A0,
+ 0x03A8,
+ 0xF770,
+ 0x0051,
+ 0xF771,
+ 0x0052,
+ 0x0154,
+ 0x0158,
+ 0x0156,
+ 0x211C,
+ 0x03A1,
+ 0xF6FC,
+ 0xF772,
+ 0x0053,
+ 0x250C,
+ 0x2514,
+ 0x2510,
+ 0x2518,
+ 0x253C,
+ 0x252C,
+ 0x2534,
+ 0x251C,
+ 0x2524,
+ 0x2500,
+ 0x2502,
+ 0x2561,
+ 0x2562,
+ 0x2556,
+ 0x2555,
+ 0x2563,
+ 0x2551,
+ 0x2557,
+ 0x255D,
+ 0x255C,
+ 0x255B,
+ 0x255E,
+ 0x255F,
+ 0x255A,
+ 0x2554,
+ 0x2569,
+ 0x2566,
+ 0x2560,
+ 0x2550,
+ 0x256C,
+ 0x2567,
+ 0x2568,
+ 0x2564,
+ 0x2565,
+ 0x2559,
+ 0x2558,
+ 0x2552,
+ 0x2553,
+ 0x256B,
+ 0x256A,
+ 0x015A,
+ 0x0160,
+ 0xF6FD,
+ 0x015E,
+ 0xF6C1,
+ 0x015C,
+ 0x0218,
+ 0x03A3,
+ 0xF773,
+ 0x0054,
+ 0x03A4,
+ 0x0166,
+ 0x0164,
+ 0x0162,
+ 0x021A,
+ 0x0398,
+ 0x00DE,
+ 0xF7FE,
+ 0xF6FE,
+ 0xF774,
+ 0x0055,
+ 0x00DA,
+ 0xF7FA,
+ 0x016C,
+ 0x00DB,
+ 0xF7FB,
+ 0x00DC,
+ 0xF7FC,
+ 0x00D9,
+ 0xF7F9,
+ 0x01AF,
+ 0x0170,
+ 0x016A,
+ 0x0172,
+ 0x03A5,
+ 0x03D2,
+ 0x03AB,
+ 0x038E,
+ 0x016E,
+ 0xF775,
+ 0x0168,
+ 0x0056,
+ 0xF776,
+ 0x0057,
+ 0x1E82,
+ 0x0174,
+ 0x1E84,
+ 0x1E80,
+ 0xF777,
+ 0x0058,
+ 0x039E,
+ 0xF778,
+ 0x0059,
+ 0x00DD,
+ 0xF7FD,
+ 0x0176,
+ 0x0178,
+ 0xF7FF,
+ 0x1EF2,
+ 0xF779,
+ 0x005A,
+ 0x0179,
+ 0x017D,
+ 0xF6FF,
+ 0x017B,
+ 0x0396,
+ 0xF77A,
+ 0x0061,
+ 0x00E1,
+ 0x0103,
+ 0x00E2,
+ 0x00B4,
+ 0x0301,
+ 0x00E4,
+ 0x00E6,
+ 0x01FD,
+ 0x2015,
+ 0x0410,
+ 0x0411,
+ 0x0412,
+ 0x0413,
+ 0x0414,
+ 0x0415,
+ 0x0401,
+ 0x0416,
+ 0x0417,
+ 0x0418,
+ 0x0419,
+ 0x041A,
+ 0x041B,
+ 0x041C,
+ 0x041D,
+ 0x041E,
+ 0x041F,
+ 0x0420,
+ 0x0421,
+ 0x0422,
+ 0x0423,
+ 0x0424,
+ 0x0425,
+ 0x0426,
+ 0x0427,
+ 0x0428,
+ 0x0429,
+ 0x042A,
+ 0x042B,
+ 0x042C,
+ 0x042D,
+ 0x042E,
+ 0x042F,
+ 0x0490,
+ 0x0402,
+ 0x0403,
+ 0x0404,
+ 0x0405,
+ 0x0406,
+ 0x0407,
+ 0x0408,
+ 0x0409,
+ 0x040A,
+ 0x040B,
+ 0x040C,
+ 0x040E,
+ 0xF6C4,
+ 0xF6C5,
+ 0x0430,
+ 0x0431,
+ 0x0432,
+ 0x0433,
+ 0x0434,
+ 0x0435,
+ 0x0451,
+ 0x0436,
+ 0x0437,
+ 0x0438,
+ 0x0439,
+ 0x043A,
+ 0x043B,
+ 0x043C,
+ 0x043D,
+ 0x043E,
+ 0x043F,
+ 0x0440,
+ 0x0441,
+ 0x0442,
+ 0x0443,
+ 0x0444,
+ 0x0445,
+ 0x0446,
+ 0x0447,
+ 0x0448,
+ 0x0449,
+ 0x044A,
+ 0x044B,
+ 0x044C,
+ 0x044D,
+ 0x044E,
+ 0x044F,
+ 0x0491,
+ 0x0452,
+ 0x0453,
+ 0x0454,
+ 0x0455,
+ 0x0456,
+ 0x0457,
+ 0x0458,
+ 0x0459,
+ 0x045A,
+ 0x045B,
+ 0x045C,
+ 0x045E,
+ 0x040F,
+ 0x0462,
+ 0x0472,
+ 0x0474,
+ 0xF6C6,
+ 0x045F,
+ 0x0463,
+ 0x0473,
+ 0x0475,
+ 0xF6C7,
+ 0xF6C8,
+ 0x04D9,
+ 0x200E,
+ 0x200F,
+ 0x200D,
+ 0x066A,
+ 0x060C,
+ 0x0660,
+ 0x0661,
+ 0x0662,
+ 0x0663,
+ 0x0664,
+ 0x0665,
+ 0x0666,
+ 0x0667,
+ 0x0668,
+ 0x0669,
+ 0x061B,
+ 0x061F,
+ 0x0621,
+ 0x0622,
+ 0x0623,
+ 0x0624,
+ 0x0625,
+ 0x0626,
+ 0x0627,
+ 0x0628,
+ 0x0629,
+ 0x062A,
+ 0x062B,
+ 0x062C,
+ 0x062D,
+ 0x062E,
+ 0x062F,
+ 0x0630,
+ 0x0631,
+ 0x0632,
+ 0x0633,
+ 0x0634,
+ 0x0635,
+ 0x0636,
+ 0x0637,
+ 0x0638,
+ 0x0639,
+ 0x063A,
+ 0x0640,
+ 0x0641,
+ 0x0642,
+ 0x0643,
+ 0x0644,
+ 0x0645,
+ 0x0646,
+ 0x0648,
+ 0x0649,
+ 0x064A,
+ 0x064B,
+ 0x064C,
+ 0x064D,
+ 0x064E,
+ 0x064F,
+ 0x0650,
+ 0x0651,
+ 0x0652,
+ 0x0647,
+ 0x06A4,
+ 0x067E,
+ 0x0686,
+ 0x0698,
+ 0x06AF,
+ 0x0679,
+ 0x0688,
+ 0x0691,
+ 0x06BA,
+ 0x06D2,
+ 0x06D5,
+ 0x20AA,
+ 0x05BE,
+ 0x05C3,
+ 0x05D0,
+ 0x05D1,
+ 0x05D2,
+ 0x05D3,
+ 0x05D4,
+ 0x05D5,
+ 0x05D6,
+ 0x05D7,
+ 0x05D8,
+ 0x05D9,
+ 0x05DA,
+ 0x05DB,
+ 0x05DC,
+ 0x05DD,
+ 0x05DE,
+ 0x05DF,
+ 0x05E0,
+ 0x05E1,
+ 0x05E2,
+ 0x05E3,
+ 0x05E4,
+ 0x05E5,
+ 0x05E6,
+ 0x05E7,
+ 0x05E8,
+ 0x05E9,
+ 0x05EA,
+ 0xFB2A,
+ 0xFB2B,
+ 0xFB4B,
+ 0xFB1F,
+ 0x05F0,
+ 0x05F1,
+ 0x05F2,
+ 0xFB35,
+ 0x05B4,
+ 0x05B5,
+ 0x05B6,
+ 0x05BB,
+ 0x05B8,
+ 0x05B7,
+ 0x05B0,
+ 0x05B2,
+ 0x05B1,
+ 0x05B3,
+ 0x05C2,
+ 0x05C1,
+ 0x05B9,
+ 0x05BC,
+ 0x05BD,
+ 0x05BF,
+ 0x05C0,
+ 0x02BC,
+ 0x2105,
+ 0x2113,
+ 0x2116,
+ 0x202C,
+ 0x202D,
+ 0x202E,
+ 0x200C,
+ 0x066D,
+ 0x02BD,
+ 0x00E0,
+ 0x2135,
+ 0x03B1,
+ 0x03AC,
+ 0x0101,
+ 0x0026,
+ 0xF726,
+ 0x2220,
+ 0x2329,
+ 0x232A,
+ 0x0387,
+ 0x0105,
+ 0x2248,
+ 0x00E5,
+ 0x01FB,
+ 0x2194,
+ 0x21D4,
+ 0x21D3,
+ 0x21D0,
+ 0x21D2,
+ 0x21D1,
+ 0x2193,
+ 0xF8E7,
+ 0x2190,
+ 0x2192,
+ 0x2191,
+ 0x2195,
+ 0x21A8,
+ 0xF8E6,
+ 0x005E,
+ 0x007E,
+ 0x002A,
+ 0x2217,
+ 0xF6E9,
+ 0x0040,
+ 0x00E3,
+ 0x0062,
+ 0x005C,
+ 0x007C,
+ 0x03B2,
+ 0x2588,
+ 0xF8F4,
+ 0x007B,
+ 0xF8F3,
+ 0xF8F2,
+ 0xF8F1,
+ 0x007D,
+ 0xF8FE,
+ 0xF8FD,
+ 0xF8FC,
+ 0x005B,
+ 0xF8F0,
+ 0xF8EF,
+ 0xF8EE,
+ 0x005D,
+ 0xF8FB,
+ 0xF8FA,
+ 0xF8F9,
+ 0x02D8,
+ 0x00A6,
+ 0xF6EA,
+ 0x2022,
+ 0x0063,
+ 0x0107,
+ 0x02C7,
+ 0x21B5,
+ 0x010D,
+ 0x00E7,
+ 0x0109,
+ 0x010B,
+ 0x00B8,
+ 0x00A2,
+ 0xF6DF,
+ 0xF7A2,
+ 0xF6E0,
+ 0x03C7,
+ 0x25CB,
+ 0x2297,
+ 0x2295,
+ 0x02C6,
+ 0x2663,
+ 0x003A,
+ 0x20A1,
+ 0x002C,
+ 0xF6C3,
+ 0xF6E1,
+ 0xF6E2,
+ 0x2245,
+ 0x00A9,
+ 0xF8E9,
+ 0xF6D9,
+ 0x00A4,
+ 0xF6D1,
+ 0xF6D2,
+ 0xF6D4,
+ 0xF6D5,
+ 0x0064,
+ 0x2020,
+ 0x2021,
+ 0xF6D3,
+ 0xF6D6,
+ 0x010F,
+ 0x0111,
+ 0x00B0,
+ 0x03B4,
+ 0x2666,
+ 0x00A8,
+ 0xF6D7,
+ 0xF6D8,
+ 0x0385,
+ 0x00F7,
+ 0x2593,
+ 0x2584,
+ 0x0024,
+ 0xF6E3,
+ 0xF724,
+ 0xF6E4,
+ 0x20AB,
+ 0x02D9,
+ 0x0323,
+ 0x0131,
+ 0xF6BE,
+ 0x22C5,
+ 0xF6EB,
+ 0x0065,
+ 0x00E9,
+ 0x0115,
+ 0x011B,
+ 0x00EA,
+ 0x00EB,
+ 0x0117,
+ 0x00E8,
+ 0x0038,
+ 0x2088,
+ 0xF738,
+ 0x2078,
+ 0x2208,
+ 0x2026,
+ 0x0113,
+ 0x2014,
+ 0x2205,
+ 0x2013,
+ 0x014B,
+ 0x0119,
+ 0x03B5,
+ 0x03AD,
+ 0x003D,
+ 0x2261,
+ 0x212E,
+ 0xF6EC,
+ 0x03B7,
+ 0x03AE,
+ 0x00F0,
+ 0x0021,
+ 0x203C,
+ 0x00A1,
+ 0xF7A1,
+ 0xF721,
+ 0x2203,
+ 0x0066,
+ 0x2640,
+ 0xFB00,
+ 0xFB03,
+ 0xFB04,
+ 0xFB01,
+ 0x2012,
+ 0x25A0,
+ 0x25AC,
+ 0x0035,
+ 0x215D,
+ 0x2085,
+ 0xF735,
+ 0x2075,
+ 0xFB02,
+ 0x0192,
+ 0x0034,
+ 0x2084,
+ 0xF734,
+ 0x2074,
+ 0x2044,
+ 0x2215,
+ 0x20A3,
+ 0x0067,
+ 0x03B3,
+ 0x011F,
+ 0x01E7,
+ 0x011D,
+ 0x0123,
+ 0x0121,
+ 0x00DF,
+ 0x2207,
+ 0x0060,
+ 0x0300,
+ 0x003E,
+ 0x2265,
+ 0x00AB,
+ 0x00BB,
+ 0x2039,
+ 0x203A,
+ 0x0068,
+ 0x0127,
+ 0x0125,
+ 0x2665,
+ 0x0309,
+ 0x2302,
+ 0x02DD,
+ 0x002D,
+ 0x00AD,
+ 0xF6E5,
+ 0xF6E6,
+ 0x0069,
+ 0x00ED,
+ 0x012D,
+ 0x00EE,
+ 0x00EF,
+ 0x00EC,
+ 0x0133,
+ 0x012B,
+ 0x221E,
+ 0x222B,
+ 0x2321,
+ 0xF8F5,
+ 0x2320,
+ 0x2229,
+ 0x25D8,
+ 0x25D9,
+ 0x263B,
+ 0x012F,
+ 0x03B9,
+ 0x03CA,
+ 0x0390,
+ 0x03AF,
+ 0xF6ED,
+ 0x0129,
+ 0x006A,
+ 0x0135,
+ 0x006B,
+ 0x03BA,
+ 0x0137,
+ 0x0138,
+ 0x006C,
+ 0x013A,
+ 0x03BB,
+ 0x013E,
+ 0x013C,
+ 0x0140,
+ 0x003C,
+ 0x2264,
+ 0x258C,
+ 0x20A4,
+ 0xF6C0,
+ 0x2227,
+ 0x00AC,
+ 0x2228,
+ 0x017F,
+ 0x25CA,
+ 0x0142,
+ 0xF6EE,
+ 0x2591,
+ 0x006D,
+ 0x00AF,
+ 0x02C9,
+ 0x2642,
+ 0x2212,
+ 0x2032,
+ 0xF6EF,
+ 0x00B5,
+ 0x03BC,
+ 0x00D7,
+ 0x266A,
+ 0x266B,
+ 0x006E,
+ 0x0144,
+ 0x0149,
+ 0x0148,
+ 0x0146,
+ 0x0039,
+ 0x2089,
+ 0xF739,
+ 0x2079,
+ 0x2209,
+ 0x2260,
+ 0x2284,
+ 0x207F,
+ 0x00F1,
+ 0x03BD,
+ 0x0023,
+ 0x006F,
+ 0x00F3,
+ 0x014F,
+ 0x00F4,
+ 0x00F6,
+ 0x0153,
+ 0x02DB,
+ 0x00F2,
+ 0x01A1,
+ 0x0151,
+ 0x014D,
+ 0x03C9,
+ 0x03D6,
+ 0x03CE,
+ 0x03BF,
+ 0x03CC,
+ 0x0031,
+ 0x2024,
+ 0x215B,
+ 0xF6DC,
+ 0x00BD,
+ 0x2081,
+ 0xF731,
+ 0x00BC,
+ 0x00B9,
+ 0x2153,
+ 0x25E6,
+ 0x00AA,
+ 0x00BA,
+ 0x221F,
+ 0x00F8,
+ 0x01FF,
+ 0xF6F0,
+ 0x00F5,
+ 0x0070,
+ 0x00B6,
+ 0x0028,
+ 0xF8ED,
+ 0xF8EC,
+ 0x208D,
+ 0x207D,
+ 0xF8EB,
+ 0x0029,
+ 0xF8F8,
+ 0xF8F7,
+ 0x208E,
+ 0x207E,
+ 0xF8F6,
+ 0x2202,
+ 0x0025,
+ 0x002E,
+ 0x00B7,
+ 0x2219,
+ 0xF6E7,
+ 0xF6E8,
+ 0x22A5,
+ 0x2030,
+ 0x20A7,
+ 0x03C6,
+ 0x03D5,
+ 0x03C0,
+ 0x002B,
+ 0x00B1,
+ 0x211E,
+ 0x220F,
+ 0x2282,
+ 0x2283,
+ 0x221D,
+ 0x03C8,
+ 0x0071,
+ 0x003F,
+ 0x00BF,
+ 0xF7BF,
+ 0xF73F,
+ 0x0022,
+ 0x201E,
+ 0x201C,
+ 0x201D,
+ 0x2018,
+ 0x201B,
+ 0x2019,
+ 0x201A,
+ 0x0027,
+ 0x0072,
+ 0x0155,
+ 0x221A,
+ 0xF8E5,
+ 0x0159,
+ 0x0157,
+ 0x2286,
+ 0x2287,
+ 0x00AE,
+ 0xF8E8,
+ 0xF6DA,
+ 0x2310,
+ 0x03C1,
+ 0x02DA,
+ 0xF6F1,
+ 0x2590,
+ 0xF6DD,
+ 0x0073,
+ 0x015B,
+ 0x0161,
+ 0x015F,
+ 0xF6C2,
+ 0x015D,
+ 0x0219,
+ 0x2033,
+ 0x00A7,
+ 0x003B,
+ 0x0037,
+ 0x215E,
+ 0x2087,
+ 0xF737,
+ 0x2077,
+ 0x2592,
+ 0x03C3,
+ 0x03C2,
+ 0x223C,
+ 0x0036,
+ 0x2086,
+ 0xF736,
+ 0x2076,
+ 0x002F,
+ 0x263A,
+ 0x0020,
+ 0x00A0,
+ 0x2660,
+ 0xF6F2,
+ 0x00A3,
+ 0x220B,
+ 0x2211,
+ 0x263C,
+ 0x0074,
+ 0x03C4,
+ 0x0167,
+ 0x0165,
+ 0x0163,
+ 0x021B,
+ 0x2234,
+ 0x03B8,
+ 0x03D1,
+ 0x00FE,
+ 0x0033,
+ 0x215C,
+ 0x2083,
+ 0xF733,
+ 0x00BE,
+ 0xF6DE,
+ 0x00B3,
+ 0x02DC,
+ 0x0303,
+ 0x0384,
+ 0x2122,
+ 0xF8EA,
+ 0xF6DB,
+ 0x25BC,
+ 0x25C4,
+ 0x25BA,
+ 0x25B2,
+ 0xF6F3,
+ 0x0032,
+ 0x2025,
+ 0x2082,
+ 0xF732,
+ 0x00B2,
+ 0x2154,
+ 0x0075,
+ 0x00FA,
+ 0x016D,
+ 0x00FB,
+ 0x00FC,
+ 0x00F9,
+ 0x01B0,
+ 0x0171,
+ 0x016B,
+ 0x005F,
+ 0x2017,
+ 0x222A,
+ 0x2200,
+ 0x0173,
+ 0x2580,
+ 0x03C5,
+ 0x03CB,
+ 0x03B0,
+ 0x03CD,
+ 0x016F,
+ 0x0169,
+ 0x0076,
+ 0x0077,
+ 0x1E83,
+ 0x0175,
+ 0x1E85,
+ 0x2118,
+ 0x1E81,
+ 0x0078,
+ 0x03BE,
+ 0x0079,
+ 0x00FD,
+ 0x0177,
+ 0x00FF,
+ 0x00A5,
+ 0x1EF3,
+ 0x007A,
+ 0x017A,
+ 0x017E,
+ 0x017C,
+ 0x0030,
+ 0x2080,
+ 0xF730,
+ 0x2070,
+ 0x03B6
+ };
+
+ private static final String names[] =
+ {
+ "A",
+ "AE",
+ "AEacute",
+ "AEsmall",
+ "Aacute",
+ "Aacutesmall",
+ "Abreve",
+ "Acircumflex",
+ "Acircumflexsmall",
+ "Acute",
+ "Acutesmall",
+ "Adieresis",
+ "Adieresissmall",
+ "Agrave",
+ "Agravesmall",
+ "Alpha",
+ "Alphatonos",
+ "Amacron",
+ "Aogonek",
+ "Aring",
+ "Aringacute",
+ "Aringsmall",
+ "Asmall",
+ "Atilde",
+ "Atildesmall",
+ "B",
+ "Beta",
+ "Brevesmall",
+ "Bsmall",
+ "C",
+ "Cacute",
+ "Caron",
+ "Caronsmall",
+ "Ccaron",
+ "Ccedilla",
+ "Ccedillasmall",
+ "Ccircumflex",
+ "Cdotaccent",
+ "Cedillasmall",
+ "Chi",
+ "Circumflexsmall",
+ "Csmall",
+ "D",
+ "Dcaron",
+ "Dcroat",
+ "Delta",
+ "Delta",
+ "Dieresis",
+ "DieresisAcute",
+ "DieresisGrave",
+ "Dieresissmall",
+ "Dotaccentsmall",
+ "Dsmall",
+ "E",
+ "Eacute",
+ "Eacutesmall",
+ "Ebreve",
+ "Ecaron",
+ "Ecircumflex",
+ "Ecircumflexsmall",
+ "Edieresis",
+ "Edieresissmall",
+ "Edotaccent",
+ "Egrave",
+ "Egravesmall",
+ "Emacron",
+ "Eng",
+ "Eogonek",
+ "Epsilon",
+ "Epsilontonos",
+ "Esmall",
+ "Eta",
+ "Etatonos",
+ "Eth",
+ "Ethsmall",
+ "Euro",
+ "F",
+ "Fsmall",
+ "G",
+ "Gamma",
+ "Gbreve",
+ "Gcaron",
+ "Gcircumflex",
+ "Gcommaaccent",
+ "Gdotaccent",
+ "Grave",
+ "Gravesmall",
+ "Gsmall",
+ "H",
+ "H18533",
+ "H18543",
+ "H18551",
+ "H22073",
+ "Hbar",
+ "Hcircumflex",
+ "Hsmall",
+ "Hungarumlaut",
+ "Hungarumlautsmall",
+ "I",
+ "IJ",
+ "Iacute",
+ "Iacutesmall",
+ "Ibreve",
+ "Icircumflex",
+ "Icircumflexsmall",
+ "Idieresis",
+ "Idieresissmall",
+ "Idotaccent",
+ "Ifraktur",
+ "Igrave",
+ "Igravesmall",
+ "Imacron",
+ "Iogonek",
+ "Iota",
+ "Iotadieresis",
+ "Iotatonos",
+ "Ismall",
+ "Itilde",
+ "J",
+ "Jcircumflex",
+ "Jsmall",
+ "K",
+ "Kappa",
+ "Kcommaaccent",
+ "Ksmall",
+ "L",
+ "LL",
+ "Lacute",
+ "Lambda",
+ "Lcaron",
+ "Lcommaaccent",
+ "Ldot",
+ "Lslash",
+ "Lslashsmall",
+ "Lsmall",
+ "M",
+ "Macron",
+ "Macronsmall",
+ "Msmall",
+ "Mu",
+ "N",
+ "Nacute",
+ "Ncaron",
+ "Ncommaaccent",
+ "Nsmall",
+ "Ntilde",
+ "Ntildesmall",
+ "Nu",
+ "O",
+ "OE",
+ "OEsmall",
+ "Oacute",
+ "Oacutesmall",
+ "Obreve",
+ "Ocircumflex",
+ "Ocircumflexsmall",
+ "Odieresis",
+ "Odieresissmall",
+ "Ogoneksmall",
+ "Ograve",
+ "Ogravesmall",
+ "Ohorn",
+ "Ohungarumlaut",
+ "Omacron",
+ "Omega",
+ "Omega",
+ "Omegatonos",
+ "Omicron",
+ "Omicrontonos",
+ "Oslash",
+ "Oslashacute",
+ "Oslashsmall",
+ "Osmall",
+ "Otilde",
+ "Otildesmall",
+ "P",
+ "Phi",
+ "Pi",
+ "Psi",
+ "Psmall",
+ "Q",
+ "Qsmall",
+ "R",
+ "Racute",
+ "Rcaron",
+ "Rcommaaccent",
+ "Rfraktur",
+ "Rho",
+ "Ringsmall",
+ "Rsmall",
+ "S",
+ "SF010000",
+ "SF020000",
+ "SF030000",
+ "SF040000",
+ "SF050000",
+ "SF060000",
+ "SF070000",
+ "SF080000",
+ "SF090000",
+ "SF100000",
+ "SF110000",
+ "SF190000",
+ "SF200000",
+ "SF210000",
+ "SF220000",
+ "SF230000",
+ "SF240000",
+ "SF250000",
+ "SF260000",
+ "SF270000",
+ "SF280000",
+ "SF360000",
+ "SF370000",
+ "SF380000",
+ "SF390000",
+ "SF400000",
+ "SF410000",
+ "SF420000",
+ "SF430000",
+ "SF440000",
+ "SF450000",
+ "SF460000",
+ "SF470000",
+ "SF480000",
+ "SF490000",
+ "SF500000",
+ "SF510000",
+ "SF520000",
+ "SF530000",
+ "SF540000",
+ "Sacute",
+ "Scaron",
+ "Scaronsmall",
+ "Scedilla",
+ "Scedilla",
+ "Scircumflex",
+ "Scommaaccent",
+ "Sigma",
+ "Ssmall",
+ "T",
+ "Tau",
+ "Tbar",
+ "Tcaron",
+ "Tcommaaccent",
+ "Tcommaaccent",
+ "Theta",
+ "Thorn",
+ "Thornsmall",
+ "Tildesmall",
+ "Tsmall",
+ "U",
+ "Uacute",
+ "Uacutesmall",
+ "Ubreve",
+ "Ucircumflex",
+ "Ucircumflexsmall",
+ "Udieresis",
+ "Udieresissmall",
+ "Ugrave",
+ "Ugravesmall",
+ "Uhorn",
+ "Uhungarumlaut",
+ "Umacron",
+ "Uogonek",
+ "Upsilon",
+ "Upsilon1",
+ "Upsilondieresis",
+ "Upsilontonos",
+ "Uring",
+ "Usmall",
+ "Utilde",
+ "V",
+ "Vsmall",
+ "W",
+ "Wacute",
+ "Wcircumflex",
+ "Wdieresis",
+ "Wgrave",
+ "Wsmall",
+ "X",
+ "Xi",
+ "Xsmall",
+ "Y",
+ "Yacute",
+ "Yacutesmall",
+ "Ycircumflex",
+ "Ydieresis",
+ "Ydieresissmall",
+ "Ygrave",
+ "Ysmall",
+ "Z",
+ "Zacute",
+ "Zcaron",
+ "Zcaronsmall",
+ "Zdotaccent",
+ "Zeta",
+ "Zsmall",
+ "a",
+ "aacute",
+ "abreve",
+ "acircumflex",
+ "acute",
+ "acutecomb",
+ "adieresis",
+ "ae",
+ "aeacute",
+ "afii00208",
+ "afii10017",
+ "afii10018",
+ "afii10019",
+ "afii10020",
+ "afii10021",
+ "afii10022",
+ "afii10023",
+ "afii10024",
+ "afii10025",
+ "afii10026",
+ "afii10027",
+ "afii10028",
+ "afii10029",
+ "afii10030",
+ "afii10031",
+ "afii10032",
+ "afii10033",
+ "afii10034",
+ "afii10035",
+ "afii10036",
+ "afii10037",
+ "afii10038",
+ "afii10039",
+ "afii10040",
+ "afii10041",
+ "afii10042",
+ "afii10043",
+ "afii10044",
+ "afii10045",
+ "afii10046",
+ "afii10047",
+ "afii10048",
+ "afii10049",
+ "afii10050",
+ "afii10051",
+ "afii10052",
+ "afii10053",
+ "afii10054",
+ "afii10055",
+ "afii10056",
+ "afii10057",
+ "afii10058",
+ "afii10059",
+ "afii10060",
+ "afii10061",
+ "afii10062",
+ "afii10063",
+ "afii10064",
+ "afii10065",
+ "afii10066",
+ "afii10067",
+ "afii10068",
+ "afii10069",
+ "afii10070",
+ "afii10071",
+ "afii10072",
+ "afii10073",
+ "afii10074",
+ "afii10075",
+ "afii10076",
+ "afii10077",
+ "afii10078",
+ "afii10079",
+ "afii10080",
+ "afii10081",
+ "afii10082",
+ "afii10083",
+ "afii10084",
+ "afii10085",
+ "afii10086",
+ "afii10087",
+ "afii10088",
+ "afii10089",
+ "afii10090",
+ "afii10091",
+ "afii10092",
+ "afii10093",
+ "afii10094",
+ "afii10095",
+ "afii10096",
+ "afii10097",
+ "afii10098",
+ "afii10099",
+ "afii10100",
+ "afii10101",
+ "afii10102",
+ "afii10103",
+ "afii10104",
+ "afii10105",
+ "afii10106",
+ "afii10107",
+ "afii10108",
+ "afii10109",
+ "afii10110",
+ "afii10145",
+ "afii10146",
+ "afii10147",
+ "afii10148",
+ "afii10192",
+ "afii10193",
+ "afii10194",
+ "afii10195",
+ "afii10196",
+ "afii10831",
+ "afii10832",
+ "afii10846",
+ "afii299",
+ "afii300",
+ "afii301",
+ "afii57381",
+ "afii57388",
+ "afii57392",
+ "afii57393",
+ "afii57394",
+ "afii57395",
+ "afii57396",
+ "afii57397",
+ "afii57398",
+ "afii57399",
+ "afii57400",
+ "afii57401",
+ "afii57403",
+ "afii57407",
+ "afii57409",
+ "afii57410",
+ "afii57411",
+ "afii57412",
+ "afii57413",
+ "afii57414",
+ "afii57415",
+ "afii57416",
+ "afii57417",
+ "afii57418",
+ "afii57419",
+ "afii57420",
+ "afii57421",
+ "afii57422",
+ "afii57423",
+ "afii57424",
+ "afii57425",
+ "afii57426",
+ "afii57427",
+ "afii57428",
+ "afii57429",
+ "afii57430",
+ "afii57431",
+ "afii57432",
+ "afii57433",
+ "afii57434",
+ "afii57440",
+ "afii57441",
+ "afii57442",
+ "afii57443",
+ "afii57444",
+ "afii57445",
+ "afii57446",
+ "afii57448",
+ "afii57449",
+ "afii57450",
+ "afii57451",
+ "afii57452",
+ "afii57453",
+ "afii57454",
+ "afii57455",
+ "afii57456",
+ "afii57457",
+ "afii57458",
+ "afii57470",
+ "afii57505",
+ "afii57506",
+ "afii57507",
+ "afii57508",
+ "afii57509",
+ "afii57511",
+ "afii57512",
+ "afii57513",
+ "afii57514",
+ "afii57519",
+ "afii57534",
+ "afii57636",
+ "afii57645",
+ "afii57658",
+ "afii57664",
+ "afii57665",
+ "afii57666",
+ "afii57667",
+ "afii57668",
+ "afii57669",
+ "afii57670",
+ "afii57671",
+ "afii57672",
+ "afii57673",
+ "afii57674",
+ "afii57675",
+ "afii57676",
+ "afii57677",
+ "afii57678",
+ "afii57679",
+ "afii57680",
+ "afii57681",
+ "afii57682",
+ "afii57683",
+ "afii57684",
+ "afii57685",
+ "afii57686",
+ "afii57687",
+ "afii57688",
+ "afii57689",
+ "afii57690",
+ "afii57694",
+ "afii57695",
+ "afii57700",
+ "afii57705",
+ "afii57716",
+ "afii57717",
+ "afii57718",
+ "afii57723",
+ "afii57793",
+ "afii57794",
+ "afii57795",
+ "afii57796",
+ "afii57797",
+ "afii57798",
+ "afii57799",
+ "afii57800",
+ "afii57801",
+ "afii57802",
+ "afii57803",
+ "afii57804",
+ "afii57806",
+ "afii57807",
+ "afii57839",
+ "afii57841",
+ "afii57842",
+ "afii57929",
+ "afii61248",
+ "afii61289",
+ "afii61352",
+ "afii61573",
+ "afii61574",
+ "afii61575",
+ "afii61664",
+ "afii63167",
+ "afii64937",
+ "agrave",
+ "aleph",
+ "alpha",
+ "alphatonos",
+ "amacron",
+ "ampersand",
+ "ampersandsmall",
+ "angle",
+ "angleleft",
+ "angleright",
+ "anoteleia",
+ "aogonek",
+ "approxequal",
+ "aring",
+ "aringacute",
+ "arrowboth",
+ "arrowdblboth",
+ "arrowdbldown",
+ "arrowdblleft",
+ "arrowdblright",
+ "arrowdblup",
+ "arrowdown",
+ "arrowhorizex",
+ "arrowleft",
+ "arrowright",
+ "arrowup",
+ "arrowupdn",
+ "arrowupdnbse",
+ "arrowvertex",
+ "asciicircum",
+ "asciitilde",
+ "asterisk",
+ "asteriskmath",
+ "asuperior",
+ "at",
+ "atilde",
+ "b",
+ "backslash",
+ "bar",
+ "beta",
+ "block",
+ "braceex",
+ "braceleft",
+ "braceleftbt",
+ "braceleftmid",
+ "bracelefttp",
+ "braceright",
+ "bracerightbt",
+ "bracerightmid",
+ "bracerighttp",
+ "bracketleft",
+ "bracketleftbt",
+ "bracketleftex",
+ "bracketlefttp",
+ "bracketright",
+ "bracketrightbt",
+ "bracketrightex",
+ "bracketrighttp",
+ "breve",
+ "brokenbar",
+ "bsuperior",
+ "bullet",
+ "c",
+ "cacute",
+ "caron",
+ "carriagereturn",
+ "ccaron",
+ "ccedilla",
+ "ccircumflex",
+ "cdotaccent",
+ "cedilla",
+ "cent",
+ "centinferior",
+ "centoldstyle",
+ "centsuperior",
+ "chi",
+ "circle",
+ "circlemultiply",
+ "circleplus",
+ "circumflex",
+ "club",
+ "colon",
+ "colonmonetary",
+ "comma",
+ "commaaccent",
+ "commainferior",
+ "commasuperior",
+ "congruent",
+ "copyright",
+ "copyrightsans",
+ "copyrightserif",
+ "currency",
+ "cyrBreve",
+ "cyrFlex",
+ "cyrbreve",
+ "cyrflex",
+ "d",
+ "dagger",
+ "daggerdbl",
+ "dblGrave",
+ "dblgrave",
+ "dcaron",
+ "dcroat",
+ "degree",
+ "delta",
+ "diamond",
+ "dieresis",
+ "dieresisacute",
+ "dieresisgrave",
+ "dieresistonos",
+ "divide",
+ "dkshade",
+ "dnblock",
+ "dollar",
+ "dollarinferior",
+ "dollaroldstyle",
+ "dollarsuperior",
+ "dong",
+ "dotaccent",
+ "dotbelowcomb",
+ "dotlessi",
+ "dotlessj",
+ "dotmath",
+ "dsuperior",
+ "e",
+ "eacute",
+ "ebreve",
+ "ecaron",
+ "ecircumflex",
+ "edieresis",
+ "edotaccent",
+ "egrave",
+ "eight",
+ "eightinferior",
+ "eightoldstyle",
+ "eightsuperior",
+ "element",
+ "ellipsis",
+ "emacron",
+ "emdash",
+ "emptyset",
+ "endash",
+ "eng",
+ "eogonek",
+ "epsilon",
+ "epsilontonos",
+ "equal",
+ "equivalence",
+ "estimated",
+ "esuperior",
+ "eta",
+ "etatonos",
+ "eth",
+ "exclam",
+ "exclamdbl",
+ "exclamdown",
+ "exclamdownsmall",
+ "exclamsmall",
+ "existential",
+ "f",
+ "female",
+ "ff",
+ "ffi",
+ "ffl",
+ "fi",
+ "figuredash",
+ "filledbox",
+ "filledrect",
+ "five",
+ "fiveeighths",
+ "fiveinferior",
+ "fiveoldstyle",
+ "fivesuperior",
+ "fl",
+ "florin",
+ "four",
+ "fourinferior",
+ "fouroldstyle",
+ "foursuperior",
+ "fraction",
+ "fraction",
+ "franc",
+ "g",
+ "gamma",
+ "gbreve",
+ "gcaron",
+ "gcircumflex",
+ "gcommaaccent",
+ "gdotaccent",
+ "germandbls",
+ "gradient",
+ "grave",
+ "gravecomb",
+ "greater",
+ "greaterequal",
+ "guillemotleft",
+ "guillemotright",
+ "guilsinglleft",
+ "guilsinglright",
+ "h",
+ "hbar",
+ "hcircumflex",
+ "heart",
+ "hookabovecomb",
+ "house",
+ "hungarumlaut",
+ "hyphen",
+ "hyphen",
+ "hypheninferior",
+ "hyphensuperior",
+ "i",
+ "iacute",
+ "ibreve",
+ "icircumflex",
+ "idieresis",
+ "igrave",
+ "ij",
+ "imacron",
+ "infinity",
+ "integral",
+ "integralbt",
+ "integralex",
+ "integraltp",
+ "intersection",
+ "invbullet",
+ "invcircle",
+ "invsmileface",
+ "iogonek",
+ "iota",
+ "iotadieresis",
+ "iotadieresistonos",
+ "iotatonos",
+ "isuperior",
+ "itilde",
+ "j",
+ "jcircumflex",
+ "k",
+ "kappa",
+ "kcommaaccent",
+ "kgreenlandic",
+ "l",
+ "lacute",
+ "lambda",
+ "lcaron",
+ "lcommaaccent",
+ "ldot",
+ "less",
+ "lessequal",
+ "lfblock",
+ "lira",
+ "ll",
+ "logicaland",
+ "logicalnot",
+ "logicalor",
+ "longs",
+ "lozenge",
+ "lslash",
+ "lsuperior",
+ "ltshade",
+ "m",
+ "macron",
+ "macron",
+ "male",
+ "minus",
+ "minute",
+ "msuperior",
+ "mu",
+ "mu",
+ "multiply",
+ "musicalnote",
+ "musicalnotedbl",
+ "n",
+ "nacute",
+ "napostrophe",
+ "ncaron",
+ "ncommaaccent",
+ "nine",
+ "nineinferior",
+ "nineoldstyle",
+ "ninesuperior",
+ "notelement",
+ "notequal",
+ "notsubset",
+ "nsuperior",
+ "ntilde",
+ "nu",
+ "numbersign",
+ "o",
+ "oacute",
+ "obreve",
+ "ocircumflex",
+ "odieresis",
+ "oe",
+ "ogonek",
+ "ograve",
+ "ohorn",
+ "ohungarumlaut",
+ "omacron",
+ "omega",
+ "omega1",
+ "omegatonos",
+ "omicron",
+ "omicrontonos",
+ "one",
+ "onedotenleader",
+ "oneeighth",
+ "onefitted",
+ "onehalf",
+ "oneinferior",
+ "oneoldstyle",
+ "onequarter",
+ "onesuperior",
+ "onethird",
+ "openbullet",
+ "ordfeminine",
+ "ordmasculine",
+ "orthogonal",
+ "oslash",
+ "oslashacute",
+ "osuperior",
+ "otilde",
+ "p",
+ "paragraph",
+ "parenleft",
+ "parenleftbt",
+ "parenleftex",
+ "parenleftinferior",
+ "parenleftsuperior",
+ "parenlefttp",
+ "parenright",
+ "parenrightbt",
+ "parenrightex",
+ "parenrightinferior",
+ "parenrightsuperior",
+ "parenrighttp",
+ "partialdiff",
+ "percent",
+ "period",
+ "periodcentered",
+ "periodcentered",
+ "periodinferior",
+ "periodsuperior",
+ "perpendicular",
+ "perthousand",
+ "peseta",
+ "phi",
+ "phi1",
+ "pi",
+ "plus",
+ "plusminus",
+ "prescription",
+ "product",
+ "propersubset",
+ "propersuperset",
+ "proportional",
+ "psi",
+ "q",
+ "question",
+ "questiondown",
+ "questiondownsmall",
+ "questionsmall",
+ "quotedbl",
+ "quotedblbase",
+ "quotedblleft",
+ "quotedblright",
+ "quoteleft",
+ "quotereversed",
+ "quoteright",
+ "quotesinglbase",
+ "quotesingle",
+ "r",
+ "racute",
+ "radical",
+ "radicalex",
+ "rcaron",
+ "rcommaaccent",
+ "reflexsubset",
+ "reflexsuperset",
+ "registered",
+ "registersans",
+ "registerserif",
+ "revlogicalnot",
+ "rho",
+ "ring",
+ "rsuperior",
+ "rtblock",
+ "rupiah",
+ "s",
+ "sacute",
+ "scaron",
+ "scedilla",
+ "scedilla",
+ "scircumflex",
+ "scommaaccent",
+ "second",
+ "section",
+ "semicolon",
+ "seven",
+ "seveneighths",
+ "seveninferior",
+ "sevenoldstyle",
+ "sevensuperior",
+ "shade",
+ "sigma",
+ "sigma1",
+ "similar",
+ "six",
+ "sixinferior",
+ "sixoldstyle",
+ "sixsuperior",
+ "slash",
+ "smileface",
+ "space",
+ "space",
+ "spade",
+ "ssuperior",
+ "sterling",
+ "suchthat",
+ "summation",
+ "sun",
+ "t",
+ "tau",
+ "tbar",
+ "tcaron",
+ "tcommaaccent",
+ "tcommaaccent",
+ "therefore",
+ "theta",
+ "theta1",
+ "thorn",
+ "three",
+ "threeeighths",
+ "threeinferior",
+ "threeoldstyle",
+ "threequarters",
+ "threequartersemdash",
+ "threesuperior",
+ "tilde",
+ "tildecomb",
+ "tonos",
+ "trademark",
+ "trademarksans",
+ "trademarkserif",
+ "triagdn",
+ "triaglf",
+ "triagrt",
+ "triagup",
+ "tsuperior",
+ "two",
+ "twodotenleader",
+ "twoinferior",
+ "twooldstyle",
+ "twosuperior",
+ "twothirds",
+ "u",
+ "uacute",
+ "ubreve",
+ "ucircumflex",
+ "udieresis",
+ "ugrave",
+ "uhorn",
+ "uhungarumlaut",
+ "umacron",
+ "underscore",
+ "underscoredbl",
+ "union",
+ "universal",
+ "uogonek",
+ "upblock",
+ "upsilon",
+ "upsilondieresis",
+ "upsilondieresistonos",
+ "upsilontonos",
+ "uring",
+ "utilde",
+ "v",
+ "w",
+ "wacute",
+ "wcircumflex",
+ "wdieresis",
+ "weierstrass",
+ "wgrave",
+ "x",
+ "xi",
+ "y",
+ "yacute",
+ "ycircumflex",
+ "ydieresis",
+ "yen",
+ "ygrave",
+ "z",
+ "zacute",
+ "zcaron",
+ "zdotaccent",
+ "zero",
+ "zeroinferior",
+ "zerooldstyle",
+ "zerosuperior",
+ "zeta"
+ };
+
+ static
+ {
+ for (int k = 0; k < unicode.length; ++k)
+ {
+ Integer num = new Integer(unicode[k]);
+ unicode2names.put(num, names[k]);
+ int code[] = (int[])names2unicode.get(names[k]);
+ if (code == null)
+ {
+ names2unicode.put(names[k], new int[]{unicode[k]});
+ }
+ else
+ {
+ int code2[] = new int[code.length + 1];
+ System.arraycopy(code, 0, code2, 0, code.length);
+ code2[code.length] = unicode[k];
+ names2unicode.put(names[k], code2);
+ }
+ }
+ }
+
+ public static int[] nameToUnicode(String name)
+ {
+ return (int[])names2unicode.get(name);
+ }
+
+ public static String unicodeToName(int num)
+ {
+ return (String)unicode2names.get(new Integer(num));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/GrayColor.java b/src/main/java/com/lowagie/text/pdf/GrayColor.java
new file mode 100644
index 0000000..4c14e79
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/GrayColor.java
@@ -0,0 +1,85 @@
+/*
+ * $Id: GrayColor.java,v 1.44 2006/02/22 10:53:08 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class GrayColor extends ExtendedColor {
+
+ private float gray;
+
+ public static final GrayColor GRAYBLACK = new GrayColor(0f);
+ public static final GrayColor GRAYWHITE = new GrayColor(1f);
+
+ public GrayColor(int intGray) {
+ this((float)intGray / 255f);
+ }
+
+ public GrayColor(float floatGray) {
+ super(TYPE_GRAY, floatGray, floatGray, floatGray);
+ gray = normalize(floatGray);
+ }
+
+ public float getGray() {
+ return gray;
+ }
+
+ public boolean equals(Object obj) {
+ return obj instanceof GrayColor && ((GrayColor)obj).gray == this.gray;
+ }
+
+ public int hashCode() {
+ return Float.floatToIntBits(gray);
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/HyphenationAuto.java b/src/main/java/com/lowagie/text/pdf/HyphenationAuto.java
new file mode 100644
index 0000000..3e49d79
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/HyphenationAuto.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.pdf.hyphenation.Hyphenator;
+import com.lowagie.text.pdf.hyphenation.Hyphenation;
+
+/** Hyphenates words automatically accordingly to the language and country.
+ * The hyphenator engine was taken from FOP and uses the TEX patterns. If a language
+ * is not provided and a TEX pattern for it exists, it can be easily adapted.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class HyphenationAuto implements HyphenationEvent {
+
+ /** The hyphenator engine.
+ */
+ protected Hyphenator hyphenator;
+ /** The second part of the hyphenated word.
+ */
+ protected String post;
+
+ /** Creates a new hyphenation instance usable in Chunk
.
+ * @param lang the language ("en" for english, for example)
+ * @param country the country ("GB" for Great-Britain or "none" for no country, for example)
+ * @param leftMin the minimun number of letters before the hyphen
+ * @param rightMin the minimun number of letters after the hyphen
+ */
+ public HyphenationAuto(String lang, String country, int leftMin, int rightMin) {
+ hyphenator = new Hyphenator(lang, country, leftMin, rightMin);
+ }
+
+ /** Gets the hyphen symbol.
+ * @return the hyphen symbol
+ */
+ public String getHyphenSymbol() {
+ return "-";
+ }
+
+ /** Hyphenates a word and returns the first part of it. To get
+ * the second part of the hyphenated word call getHyphenatedWordPost()
.
+ * @param word the word to hyphenate
+ * @param font the font used by this word
+ * @param fontSize the font size used by this word
+ * @param remainingWidth the width available to fit this word in
+ * @return the first part of the hyphenated word including
+ * the hyphen symbol, if any
+ */
+ public String getHyphenatedWordPre(String word, BaseFont font, float fontSize, float remainingWidth) {
+ post = word;
+ String hyphen = getHyphenSymbol();
+ float hyphenWidth = font.getWidthPoint(hyphen, fontSize);
+ if (hyphenWidth > remainingWidth)
+ return "";
+ Hyphenation hyphenation = hyphenator.hyphenate(word);
+ if (hyphenation == null) {
+ return "";
+ }
+ int len = hyphenation.length();
+ int k;
+ for (k = 0; k < len; ++k) {
+ if (font.getWidthPoint(hyphenation.getPreHyphenText(k), fontSize) + hyphenWidth > remainingWidth)
+ break;
+ }
+ --k;
+ if (k < 0)
+ return "";
+ post = hyphenation.getPostHyphenText(k);
+ return hyphenation.getPreHyphenText(k) + hyphen;
+ }
+
+ /** Gets the second part of the hyphenated word. Must be called
+ * after getHyphenatedWordPre()
.
+ * @return the second part of the hyphenated word
+ */
+ public String getHyphenatedWordPost() {
+ return post;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/HyphenationEvent.java b/src/main/java/com/lowagie/text/pdf/HyphenationEvent.java
new file mode 100644
index 0000000..b465cea
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/HyphenationEvent.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/** Called by Chunk
to hyphenate a word.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public interface HyphenationEvent {
+
+ /** Gets the hyphen symbol.
+ * @return the hyphen symbol
+ */
+ public String getHyphenSymbol();
+
+ /** Hyphenates a word and returns the first part of it. To get
+ * the second part of the hyphenated word call getHyphenatedWordPost()
.
+ * @param word the word to hyphenate
+ * @param font the font used by this word
+ * @param fontSize the font size used by this word
+ * @param remainingWidth the width available to fit this word in
+ * @return the first part of the hyphenated word including
+ * the hyphen symbol, if any
+ */
+ public String getHyphenatedWordPre(String word, BaseFont font, float fontSize, float remainingWidth);
+
+ /** Gets the second part of the hyphenated word. Must be called
+ * after getHyphenatedWordPre()
.
+ * @return the second part of the hyphenated word
+ */
+ public String getHyphenatedWordPost();
+}
+
diff --git a/src/main/java/com/lowagie/text/pdf/IntHashtable.java b/src/main/java/com/lowagie/text/pdf/IntHashtable.java
new file mode 100644
index 0000000..633439a
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/IntHashtable.java
@@ -0,0 +1,338 @@
+// IntHashtable - a Hashtable that uses ints as the keys
+//
+// This is 90% based on JavaSoft's java.util.Hashtable.
+//
+// Visit the ACME Labs Java page for up-to-date versions of this and other
+// fine Java utilities: http://www.acme.com/java/
+
+package com.lowagie.text.pdf;
+
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+import java.util.Iterator;
+
+/// A Hashtable that uses ints as the keys.
+//
+// Fetch the entire Acme package.
+// newString
to the end of oldString
.
+ */
+ public byte[] composeString(byte oldString[], byte newString) {
+ int length = oldString.length;
+ byte string[] = new byte[length + 1];
+ System.arraycopy(oldString, 0, string, 0, length);
+ string[length] = newString;
+
+ return string;
+ }
+
+ // Returns the next 9, 10, 11 or 12 bits
+ public int getNextCode() {
+ // Attempt to get the next code. The exception is caught to make
+ // this robust to cases wherein the EndOfInformation code has been
+ // omitted from a strip. Examples of such cases have been observed
+ // in practice.
+ try {
+ nextData = (nextData << 8) | (data[bytePointer++] & 0xff);
+ nextBits += 8;
+
+ if (nextBits < bitsToGet) {
+ nextData = (nextData << 8) | (data[bytePointer++] & 0xff);
+ nextBits += 8;
+ }
+
+ int code =
+ (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet-9];
+ nextBits -= bitsToGet;
+
+ return code;
+ } catch(ArrayIndexOutOfBoundsException e) {
+ // Strip not terminated as expected: return EndOfInformation code.
+ return 257;
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/MultiColumnText.java b/src/main/java/com/lowagie/text/pdf/MultiColumnText.java
new file mode 100644
index 0000000..4de2069
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/MultiColumnText.java
@@ -0,0 +1,575 @@
+/*
+ * $Id: MultiColumnText.java,v 1.19 2006/03/20 12:11:46 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2004 Steve Appling
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999-2005 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000-2005 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.*;
+
+import java.util.ArrayList;
+
+/**
+ * Formats content into one or more columns bounded by a
+ * rectangle. The columns may be simple rectangles or
+ * more complicated shapes. Add all of the columns before
+ * adding content. Column continuation is supported. A MultiColumnText object may be added to
+ * a document using Document.add
.
+ * @author Steve Appling
+ */
+public class MultiColumnText implements Element {
+
+ /** special constant for automatic calculation of height */
+ public static final float AUTOMATIC = -1f;
+
+ /**
+ * total desiredHeight of columns. If AUTOMATIC
, this means fill pages until done.
+ * This may be larger than one page
+ */
+ private float desiredHeight;
+
+ /**
+ * total height of element written out so far
+ */
+ private float totalHeight;
+
+ /**
+ * true if all the text could not be written out due to height restriction
+ */
+ private boolean overflow;
+
+ /**
+ * Top of the columns - y position on starting page.
+ * If AUTOMATIC
, it means current y position when added to document
+ */
+ private float top;
+
+ /**
+ * used to store the y position of the bottom of the page
+ */
+ private float pageBottom;
+
+ /**
+ * ColumnText object used to do all the real work. This same object is used for all columns
+ */
+ private ColumnText columnText;
+
+ /**
+ * Array of ColumnDef
objects used to define the columns
+ */
+ private ArrayList columnDefs;
+
+ /**
+ * true if all columns are simple (rectangular)
+ */
+ private boolean simple = true;
+
+ private int currentColumn = 0;
+
+ private float nextY = AUTOMATIC;
+
+ private boolean columnsRightToLeft = false;
+
+ private PdfDocument document;
+ /**
+ * Default constructor. Sets height to AUTOMATIC
.
+ * Columns will repeat on each page as necessary to accomodate content length.
+ */
+ public MultiColumnText() {
+ this(AUTOMATIC);
+ }
+
+ /**
+ * Construct a MultiColumnText container of the specified height.
+ * If height is AUTOMATIC
, fill complete pages until done.
+ * If a specific height is used, it may span one or more pages.
+ *
+ * @param height
+ */
+ public MultiColumnText(float height) {
+ columnDefs = new ArrayList();
+ desiredHeight = height;
+ top = AUTOMATIC;
+ // canvas will be set later
+ columnText = new ColumnText(null);
+ totalHeight = 0f;
+ }
+
+ /**
+ * Construct a MultiColumnText container of the specified height
+ * starting at the specified Y position.
+ *
+ * @param height
+ * @param top
+ */
+ public MultiColumnText(float top, float height) {
+ columnDefs = new ArrayList();
+ desiredHeight = height;
+ this.top = top;
+ nextY = top;
+ // canvas will be set later
+ columnText = new ColumnText(null);
+ totalHeight = 0f;
+ }
+
+ /**
+ * Indicates that all of the text did not fit in the
+ * specified height. Note that isOverflow will return
+ * false before the MultiColumnText object has been
+ * added to the document. It will always be false if
+ * the height is AUTOMATIC.
+ *
+ * @return true if there is still space left in the column
+ */
+ public boolean isOverflow() {
+ return overflow;
+ }
+
+ /**
+ * Copy the parameters from the specified ColumnText to use
+ * when rendering. Parameters like setArabicOptions
+ * must be set in this way.
+ *
+ * @param sourceColumn
+ */
+ public void useColumnParams(ColumnText sourceColumn) {
+ // note that canvas will be overwritten later
+ columnText.setSimpleVars(sourceColumn);
+ }
+
+ /**
+ * Add a new column. The parameters are limits for each column
+ * wall in the format of a sequence of points (x1,y1,x2,y2,...).
+ *
+ * @param left limits for left column
+ * @param right limits for right column
+ */
+ public void addColumn(float[] left, float[] right) {
+ ColumnDef nextDef = new ColumnDef(left, right);
+ simple = nextDef.isSimple();
+ columnDefs.add(nextDef);
+ }
+
+ /**
+ * Add a simple rectangular column with specified left
+ * and right x position boundaries.
+ *
+ * @param left left boundary
+ * @param right right boundary
+ */
+ public void addSimpleColumn(float left, float right) {
+ ColumnDef newCol = new ColumnDef(left, right);
+ columnDefs.add(newCol);
+ }
+
+ /**
+ * Add the specified number of evenly spaced rectangular columns.
+ * Columns will be seperated by the specified gutterWidth.
+ *
+ * @param left left boundary of first column
+ * @param right right boundary of last column
+ * @param gutterWidth width of gutter spacing between columns
+ * @param numColumns number of columns to add
+ */
+ public void addRegularColumns(float left, float right, float gutterWidth, int numColumns) {
+ float currX = left;
+ float width = right - left;
+ float colWidth = (width - (gutterWidth * (numColumns - 1))) / numColumns;
+ for (int i = 0; i < numColumns; i++) {
+ addSimpleColumn(currX, currX + colWidth);
+ currX += colWidth + gutterWidth;
+ }
+ }
+
+ /**
+ * Add an element to be rendered in a column.
+ * Note that you can only add a Phrase
+ * or a Chunk
if the columns are
+ * not all simple. This is an underlying restriction in
+ * {@link com.lowagie.text.pdf.ColumnText}
+ *
+ * @param element element to add
+ * @throws DocumentException if element can't be added
+ */
+ public void addElement(Element element) throws DocumentException {
+ if (simple) {
+ columnText.addElement(element);
+ } else if (element instanceof Phrase) {
+ columnText.addText((Phrase) element);
+ } else if (element instanceof Chunk) {
+ columnText.addText((Chunk) element);
+ } else {
+ throw new DocumentException("Can't add " + element.getClass() + " to MultiColumnText with complex columns");
+ }
+ }
+
+
+ /**
+ * Write out the columns. After writing, use
+ * {@link #isOverflow()} to see if all text was written.
+ * @param canvas PdfContentByte to write with
+ * @param document document to write to (only used to get page limit info)
+ * @param documentY starting y position to begin writing at
+ * @return the current height (y position) after writing the columns
+ * @throws DocumentException on error
+ */
+ public float write(PdfContentByte canvas, PdfDocument document, float documentY) throws DocumentException {
+ this.document = document;
+ columnText.setCanvas(canvas);
+ if (columnDefs.size() == 0) {
+ throw new DocumentException("MultiColumnText has no columns");
+ }
+ overflow = false;
+ pageBottom = document.bottom();
+ float currentHeight = 0;
+ boolean done = false;
+ try {
+ while (!done) {
+ if (nextY == AUTOMATIC) {
+ nextY = document.getVerticalPosition(true); // RS - 07/07/2005 - - Get current doc writing position for top of columns on new page.
+ }
+ if (top == AUTOMATIC) {
+ top = document.getVerticalPosition(true); // RS - 07/07/2005 - Get current doc writing position for top of columns on new page.
+ }
+
+ ColumnDef currentDef = (ColumnDef) columnDefs.get(getCurrentColumn());
+ columnText.setYLine(top);
+
+ float[] left = currentDef.resolvePositions(Rectangle.LEFT);
+ float[] right = currentDef.resolvePositions(Rectangle.RIGHT);
+ if (document.isMarginMirroring() && document.getPageNumber() % 2 == 0){
+ float delta = document.rightMargin() - document.left();
+ left = (float[])left.clone();
+ right = (float[])right.clone();
+ for (int i = 0; i < left.length; i += 2) {
+ left[i] -= delta;
+ }
+ for (int i = 0; i < right.length; i += 2) {
+ right[i] -= delta;
+ }
+ }
+
+ currentHeight = Math.max(currentHeight, getHeight(left, right));
+
+ if (currentDef.isSimple()) {
+ columnText.setSimpleColumn(left[2], left[3], right[0], right[1]);
+ } else {
+ columnText.setColumns(left, right);
+ }
+
+ int result = columnText.go();
+ if ((result & ColumnText.NO_MORE_TEXT) != 0) {
+ done = true;
+ top = columnText.getYLine();
+ } else if (shiftCurrentColumn()) {
+ top = nextY;
+ } else { // check if we are done because of height
+ totalHeight += currentHeight;
+ if ((desiredHeight != AUTOMATIC) && (totalHeight >= desiredHeight)) {
+ overflow = true;
+ break;
+ } else { // need to start new page and reset the columns
+ documentY = nextY;
+ newPage();
+ currentHeight = 0;
+ }
+ }
+ }
+ } catch (DocumentException ex) {
+ ex.printStackTrace();
+ throw ex;
+ }
+ if (desiredHeight == AUTOMATIC && columnDefs.size() == 1) {
+ currentHeight = documentY - columnText.getYLine();
+ }
+ return currentHeight;
+ }
+
+ private void newPage() throws DocumentException {
+ resetCurrentColumn();
+ if (desiredHeight == AUTOMATIC) {
+ top = nextY = AUTOMATIC;
+ }
+ else {
+ top = nextY;
+ }
+ totalHeight = 0;
+ if (document != null) {
+ document.newPage();
+ }
+ }
+
+ /**
+ * Figure out the height of a column from the border extents
+ *
+ * @param left left border
+ * @param right right border
+ * @return height
+ */
+ private float getHeight(float[] left, float[] right) {
+ float max = Float.MIN_VALUE;
+ float min = Float.MAX_VALUE;
+ for (int i = 0; i < left.length; i += 2) {
+ min = Math.min(min, left[i + 1]);
+ max = Math.max(max, left[i + 1]);
+ }
+ for (int i = 0; i < right.length; i += 2) {
+ min = Math.min(min, right[i + 1]);
+ max = Math.max(max, right[i + 1]);
+ }
+ return max - min;
+ }
+
+
+ /**
+ * Processes the element by adding it to an
+ * ElementListener
.
+ *
+ * @param listener an ElementListener
+ * @return true
if the element was processed successfully
+ */
+ public boolean process(ElementListener listener) {
+ try {
+ return listener.add(this);
+ } catch (DocumentException de) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return Element.MULTI_COLUMN_TEXT;
+ }
+
+ /**
+ * Returns null - not used
+ *
+ * @return null
+ */
+
+ public ArrayList getChunks() {
+ return null;
+ }
+
+ /**
+ * Calculates the appropriate y position for the bottom
+ * of the columns on this page.
+ *
+ * @return the y position of the bottom of the columns
+ */
+ private float getColumnBottom() {
+ if (desiredHeight == AUTOMATIC) {
+ return pageBottom;
+ } else {
+ return Math.max(top - (desiredHeight - totalHeight), pageBottom);
+ }
+ }
+
+ /**
+ * Moves the text insertion point to the beginning of the next column, issuing a page break if
+ * needed.
+ * @throws DocumentException on error
+ */
+ public void nextColumn() throws DocumentException {
+ currentColumn = (currentColumn + 1) % columnDefs.size();
+ top = nextY;
+ if (currentColumn == 0) {
+ newPage();
+ }
+ }
+
+ /**
+ * Gets the current column.
+ * @return the current column
+ */
+ public int getCurrentColumn() {
+ if (columnsRightToLeft) {
+ return (columnDefs.size() - currentColumn - 1);
+ }
+ return currentColumn;
+ }
+
+ /**
+ * Resets the current column.
+ */
+ public void resetCurrentColumn() {
+ currentColumn = 0;
+ }
+
+ /**
+ * Shifts the current column.
+ * @return true if the currentcolumn has changed
+ */
+ public boolean shiftCurrentColumn() {
+ if (currentColumn + 1 < columnDefs.size()) {
+ currentColumn++;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Sets the direction of the columns.
+ * @param direction true = right2left; false = left2right
+ */
+ public void setColumnsRightToLeft(boolean direction) {
+ columnsRightToLeft = direction;
+ }
+
+ /** Sets the ratio between the extra word spacing and the extra character spacing
+ * when the text is fully justified.
+ * Extra word spacing will grow spaceCharRatio
times more than extra character spacing.
+ * If the ratio is PdfWriter.NO_SPACE_CHAR_RATIO
then the extra character spacing
+ * will be zero.
+ * @param spaceCharRatio the ratio between the extra word spacing and the extra character spacing
+ */
+ public void setSpaceCharRatio(float spaceCharRatio) {
+ columnText.setSpaceCharRatio(spaceCharRatio);
+ }
+
+ /** Sets the run direction.
+ * @param runDirection the run direction
+ */
+ public void setRunDirection(int runDirection) {
+ columnText.setRunDirection(runDirection);
+ }
+
+ /** Sets the arabic shaping options. The option can be AR_NOVOWEL,
+ * AR_COMPOSEDTASHKEEL and AR_LIG.
+ * @param arabicOptions the arabic shaping options
+ */
+ public void setArabicOptions(int arabicOptions) {
+ columnText.setArabicOptions(arabicOptions);
+ }
+
+ /** Sets the default alignment
+ * @param alignment the default alignment
+ */
+ public void setAlignment(int alignment) {
+ columnText.setAlignment(alignment);
+ }
+
+ /**
+ * Inner class used to define a column
+ */
+ private class ColumnDef {
+ private float[] left;
+ private float[] right;
+
+ ColumnDef(float[] newLeft, float[] newRight) {
+ left = newLeft;
+ right = newRight;
+ }
+
+ ColumnDef(float leftPosition, float rightPosition) {
+ left = new float[4];
+ left[0] = leftPosition; // x1
+ left[1] = top; // y1
+ left[2] = leftPosition; // x2
+ if (desiredHeight == AUTOMATIC || top == AUTOMATIC) {
+ left[3] = AUTOMATIC;
+ } else {
+ left[3] = top - desiredHeight;
+ }
+
+ right = new float[4];
+ right[0] = rightPosition; // x1
+ right[1] = top; // y1
+ right[2] = rightPosition; // x2
+ if (desiredHeight == AUTOMATIC || top == AUTOMATIC) {
+ right[3] = AUTOMATIC;
+ } else {
+ right[3] = top - desiredHeight;
+ }
+ }
+
+ /**
+ * Resolves the positions for the specified side of the column
+ * into real numbers once the top of the column is known.
+ *
+ * @param side either Rectangle.LEFT
+ * or Rectangle.RIGHT
+ * @return the array of floats for the side
+ */
+ float[] resolvePositions(int side) {
+ if (side == Rectangle.LEFT) {
+ return resolvePositions(left);
+ } else {
+ return resolvePositions(right);
+ }
+ }
+
+ private float[] resolvePositions(float[] positions) {
+ if (!isSimple()) {
+ return positions;
+ }
+ if (top == AUTOMATIC) {
+ // this is bad - must be programmer error
+ throw new RuntimeException("resolvePositions called with top=AUTOMATIC (-1). " +
+ "Top position must be set befure lines can be resolved");
+ }
+ positions[1] = top;
+ positions[3] = getColumnBottom();
+ return positions;
+ }
+
+ /**
+ * Checks if column definition is a simple rectangle
+ * @return true if it is a simple column
+ */
+ private boolean isSimple() {
+ return (left.length == 4 && right.length == 4) && (left[0] == left[2] && right[0] == right[2]);
+ }
+
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/OutputStreamCounter.java b/src/main/java/com/lowagie/text/pdf/OutputStreamCounter.java
new file mode 100644
index 0000000..bae72bc
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/OutputStreamCounter.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ *
+ * @author psoares
+ */
+public class OutputStreamCounter extends OutputStream {
+
+ protected OutputStream out;
+ protected int counter = 0;
+
+ /** Creates a new instance of OutputStreamCounter */
+ public OutputStreamCounter(OutputStream out) {
+ this.out = out;
+ }
+
+ /** Closes this output stream and releases any system resources
+ * associated with this stream. The general contract of close
+ * is that it closes the output stream. A closed stream cannot perform
+ * output operations and cannot be reopened.
+ * close
method of OutputStream
does nothing.
+ *
+ * @exception IOException if an I/O error occurs.
+ *
+ */
+ public void close() throws IOException {
+ out.close();
+ }
+
+ /** Flushes this output stream and forces any buffered output bytes
+ * to be written out. The general contract of flush
is
+ * that calling it is an indication that, if any bytes previously
+ * written have been buffered by the implementation of the output
+ * stream, such bytes should immediately be written to their
+ * intended destination.
+ * flush
method of OutputStream
does nothing.
+ *
+ * @exception IOException if an I/O error occurs.
+ *
+ */
+ public void flush() throws IOException {
+ out.flush();
+ }
+
+ /** Writes b.length
bytes from the specified byte array
+ * to this output stream. The general contract for write(b)
+ * is that it should have exactly the same effect as the call
+ * write(b, 0, b.length)
.
+ *
+ * @param b the data.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.OutputStream#write(byte[], int, int)
+ *
+ */
+ public void write(byte[] b) throws IOException {
+ counter += b.length;
+ out.write(b);
+ }
+
+ /** Writes the specified byte to this output stream. The general
+ * contract for write
is that one byte is written
+ * to the output stream. The byte to be written is the eight
+ * low-order bits of the argument b
. The 24
+ * high-order bits of b
are ignored.
+ * OutputStream
must provide an
+ * implementation for this method.
+ *
+ * @param b the byte
.
+ * @exception IOException if an I/O error occurs. In particular,
+ * an IOException
may be thrown if the
+ * output stream has been closed.
+ *
+ */
+ public void write(int b) throws IOException {
+ ++counter;
+ out.write(b);
+ }
+
+ /** Writes len
bytes from the specified byte array
+ * starting at offset off
to this output stream.
+ * The general contract for write(b, off, len)
is that
+ * some of the bytes in the array b
are written to the
+ * output stream in order; element b[off]
is the first
+ * byte written and b[off+len-1]
is the last byte written
+ * by this operation.
+ * write
method of OutputStream
calls
+ * the write method of one argument on each of the bytes to be
+ * written out. Subclasses are encouraged to override this method and
+ * provide a more efficient implementation.
+ * b
is null
, a
+ * NullPointerException
is thrown.
+ * off
is negative, or len
is negative, or
+ * off+len
is greater than the length of the array
+ * b
, then an IndexOutOfBoundsException is thrown.
+ *
+ * @param b the data.
+ * @param off the start offset in the data.
+ * @param len the number of bytes to write.
+ * @exception IOException if an I/O error occurs. In particular,
+ * an IOException
is thrown if the output
+ * stream is closed.
+ *
+ */
+ public void write(byte[] b, int off, int len) throws IOException {
+ counter += len;
+ out.write(b, off, len);
+ }
+
+ public int getCounter() {
+ return counter;
+ }
+
+ public void resetCounter() {
+ counter = 0;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PRAcroForm.java b/src/main/java/com/lowagie/text/pdf/PRAcroForm.java
new file mode 100644
index 0000000..986b2ab
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PRAcroForm.java
@@ -0,0 +1,210 @@
+/*
+ * $Id: PRAcroForm.java,v 1.35 2005/05/04 14:31:42 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * This class written by Mark Thompson, Copyright (C) 2002 by Mark Thompson.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashMap;
+
+/**
+ * This class captures an AcroForm on input. Basically, it extends Dictionary
+ * by indexing the fields of an AcroForm
+ * @author Mark Thompson
+ */
+
+public class PRAcroForm extends PdfDictionary {
+
+ /**
+ * This class holds the information for a single field
+ */
+ public static class FieldInformation {
+ String name;
+ PdfDictionary info;
+ PRIndirectReference ref;
+
+ FieldInformation(String name, PdfDictionary info, PRIndirectReference ref) {
+ this.name = name; this.info = info; this.ref = ref;
+ }
+ public String getName() { return name; }
+ public PdfDictionary getInfo() { return info; }
+ public PRIndirectReference getRef() { return ref; }
+ };
+ ArrayList fields;
+ ArrayList stack;
+ HashMap fieldByName;
+ PdfReader reader;
+
+ /**
+ * Constructor
+ * @param reader reader of the input file
+ */
+ public PRAcroForm(PdfReader reader) {
+ this.reader = reader;
+ fields = new ArrayList();
+ fieldByName = new HashMap();
+ stack = new ArrayList();
+ }
+ /**
+ * Number of fields found
+ * @return size
+ */
+ public int size() {
+ return fields.size();
+ }
+
+ public ArrayList getFields() {
+ return fields;
+ }
+
+ public FieldInformation getField(String name) {
+ return (FieldInformation)fieldByName.get(name);
+ }
+
+ /**
+ * Given the title (/T) of a reference, return the associated reference
+ * @param name a string containing the path
+ * @return a reference to the field, or null
+ */
+ public PRIndirectReference getRefByName(String name) {
+ FieldInformation fi = (FieldInformation)fieldByName.get(name);
+ if (fi == null) return null;
+ return fi.getRef();
+ }
+ /**
+ * Read, and comprehend the acroform
+ * @param root the docment root
+ */
+ public void readAcroForm(PdfDictionary root) {
+ hashMap = root.hashMap;
+ pushAttrib(root);
+ PdfArray fieldlist = (PdfArray)PdfReader.getPdfObjectRelease(root.get(PdfName.FIELDS));
+ iterateFields(fieldlist, null, null);
+ }
+
+ /**
+ * After reading, we index all of the fields. Recursive.
+ * @param fieldlist An array of fields
+ * @param fieldDict the last field dictionary we encountered (recursively)
+ * @param title the pathname of the field, up to this point or null
+ */
+ protected void iterateFields(PdfArray fieldlist, PRIndirectReference fieldDict, String title) {
+ for (Iterator it = fieldlist.getArrayList().iterator(); it.hasNext();) {
+ PRIndirectReference ref = (PRIndirectReference)it.next();
+ PdfDictionary dict = (PdfDictionary) PdfReader.getPdfObjectRelease(ref);
+
+ // if we are not a field dictionary, pass our parent's values
+ PRIndirectReference myFieldDict = fieldDict;
+ String myTitle = title;
+ PdfString tField = (PdfString)dict.get(PdfName.T);
+ boolean isFieldDict = tField != null;
+
+ if (isFieldDict) {
+ myFieldDict = ref;
+ if (title == null) myTitle = tField.toString();
+ else myTitle = title + '.' + tField.toString();
+ }
+
+ PdfArray kids = (PdfArray)dict.get(PdfName.KIDS);
+ if (kids != null) {
+ pushAttrib(dict);
+ iterateFields(kids, myFieldDict, myTitle);
+ stack.remove(stack.size() - 1); // pop
+ }
+ else { // leaf node
+ if (myFieldDict != null) {
+ PdfDictionary mergedDict = (PdfDictionary)stack.get(stack.size() - 1);
+ if (isFieldDict)
+ mergedDict = mergeAttrib(mergedDict, dict);
+
+ mergedDict.put(PdfName.T, new PdfString(myTitle));
+ FieldInformation fi = new FieldInformation(myTitle, mergedDict, myFieldDict);
+ fields.add(fi);
+ fieldByName.put(myTitle, fi);
+ }
+ }
+ }
+ }
+ /**
+ * merge field attributes from two dictionaries
+ * @param parent one dictionary
+ * @param child the other dictionary
+ * @return a merged dictionary
+ */
+ protected PdfDictionary mergeAttrib(PdfDictionary parent, PdfDictionary child) {
+ PdfDictionary targ = new PdfDictionary();
+ if (parent != null) targ.putAll(parent);
+
+ for (Iterator it = child.getKeys().iterator(); it.hasNext();) {
+ PdfName key = (PdfName) it.next();
+ if (key.equals(PdfName.DR) || key.equals(PdfName.DA) ||
+ key.equals(PdfName.Q) || key.equals(PdfName.FF) ||
+ key.equals(PdfName.DV) || key.equals(PdfName.V)
+ || key.equals(PdfName.FT)
+ || key.equals(PdfName.F)) {
+ targ.put(key,child.get(key));
+ }
+ }
+ return targ;
+ }
+ /**
+ * stack a level of dictionary. Merge in a dictionary from this level
+ */
+ protected void pushAttrib(PdfDictionary dict) {
+ PdfDictionary dic = null;
+ if (stack.size() != 0) {
+ dic = (PdfDictionary)stack.get(stack.size() - 1);
+ }
+ dic = mergeAttrib(dic, dict);
+ stack.add(dic);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PRIndirectReference.java b/src/main/java/com/lowagie/text/pdf/PRIndirectReference.java
new file mode 100644
index 0000000..2ff852f
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PRIndirectReference.java
@@ -0,0 +1,103 @@
+/*
+ * $Id: PRIndirectReference.java,v 1.46 2005/05/04 14:33:11 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public class PRIndirectReference extends PdfIndirectReference {
+
+ protected PdfReader reader;
+ // membervariables
+
+ // constructors
+
+/**
+ * Constructs a PdfIndirectReference
.
+ *
+ * @param reader a PdfReader
+ * @param number the object number.
+ * @param generation the generation number.
+ */
+
+ PRIndirectReference(PdfReader reader, int number, int generation) {
+ type = INDIRECT;
+ this.number = number;
+ this.generation = generation;
+ this.reader = reader;
+ }
+
+/**
+ * Constructs a PdfIndirectReference
.
+ *
+ * @param reader a PdfReader
+ * @param number the object number.
+ */
+
+ PRIndirectReference(PdfReader reader, int number) {
+ this(reader, number, 0);
+ }
+
+ // methods
+
+ public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
+ int n = writer.getNewObjectNumber(reader, number, generation);
+ os.write(PdfEncodings.convertToBytes(new StringBuffer().append(n).append(" 0 R").toString(), null));
+ }
+
+ public PdfReader getReader() {
+ return reader;
+ }
+
+ public void setNumber(int number, int generation) {
+ this.number = number;
+ this.generation = generation;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PRStream.java b/src/main/java/com/lowagie/text/pdf/PRStream.java
new file mode 100644
index 0000000..272a45f
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PRStream.java
@@ -0,0 +1,229 @@
+/*
+ * $Id: PRStream.java,v 1.46 2005/05/04 14:32:34 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.*;
+import com.lowagie.text.ExceptionConverter;
+import java.util.zip.DeflaterOutputStream;
+import com.lowagie.text.Document;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public class PRStream extends PdfStream {
+
+ protected PdfReader reader;
+ protected int offset;
+ protected int length;
+
+ //added by ujihara for decryption
+ protected int objNum = 0;
+ protected int objGen = 0;
+
+ public PRStream(PRStream stream, PdfDictionary newDic) {
+ reader = stream.reader;
+ offset = stream.offset;
+ length = stream.length;
+ compressed = stream.compressed;
+ streamBytes = stream.streamBytes;
+ bytes = stream.bytes;
+ objNum = stream.objNum;
+ objGen = stream.objGen;
+ if (newDic != null)
+ putAll(newDic);
+ else
+ hashMap.putAll(stream.hashMap);
+ }
+
+ public PRStream(PRStream stream, PdfDictionary newDic, PdfReader reader) {
+ this(stream, newDic);
+ this.reader = reader;
+ }
+
+ public PRStream(PdfReader reader, int offset) {
+ this.reader = reader;
+ this.offset = offset;
+ }
+
+ public PRStream(PdfReader reader, byte conts[]) {
+ this.reader = reader;
+ this.offset = -1;
+ if (Document.compress) {
+ try {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ DeflaterOutputStream zip = new DeflaterOutputStream(stream);
+ zip.write(conts);
+ zip.close();
+ bytes = stream.toByteArray();
+ }
+ catch (IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ put(PdfName.FILTER, PdfName.FLATEDECODE);
+ }
+ else
+ bytes = conts;
+ setLength(bytes.length);
+ }
+
+ /**Sets the data associated with the stream
+ * @param data raw data, decrypted and uncompressed.
+ */
+ public void setData(byte[] data) {
+ remove(PdfName.FILTER);
+ this.offset = -1;
+ if (Document.compress) {
+ try {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ DeflaterOutputStream zip = new DeflaterOutputStream(stream);
+ zip.write(data);
+ zip.close();
+ bytes = stream.toByteArray();
+ }
+ catch (IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ put(PdfName.FILTER, PdfName.FLATEDECODE);
+ }
+ else
+ bytes = data;
+ setLength(bytes.length);
+ }
+
+ public void setLength(int length) {
+ this.length = length;
+ put(PdfName.LENGTH, new PdfNumber(length));
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public PdfReader getReader() {
+ return reader;
+ }
+
+ public byte[] getBytes() {
+ return bytes;
+ }
+
+ public void setObjNum(int objNum, int objGen) {
+ this.objNum = objNum;
+ this.objGen = objGen;
+ }
+
+ int getObjNum() {
+ return objNum;
+ }
+
+ int getObjGen() {
+ return objGen;
+ }
+
+ public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
+ superToPdf(writer, os);
+ os.write(STARTSTREAM);
+ if (length > 0) {
+ PdfEncryption crypto = null;
+ if (writer != null)
+ crypto = writer.getEncryption();
+ if (offset < 0) {
+ if (crypto == null)
+ os.write(bytes);
+ else {
+ crypto.prepareKey();
+ byte buf[] = new byte[length];
+ System.arraycopy(bytes, 0, buf, 0, length);
+ crypto.encryptRC4(buf);
+ os.write(buf);
+ }
+ }
+ else {
+ byte buf[] = new byte[Math.min(length, 4092)];
+ RandomAccessFileOrArray file = writer.getReaderFile(reader);
+ boolean isOpen = file.isOpen();
+ try {
+ file.seek(offset);
+ int size = length;
+
+ //added by ujihara for decryption
+ PdfEncryption decrypt = reader.getDecrypt();
+ if (decrypt != null) {
+ decrypt.setHashKey(objNum, objGen);
+ decrypt.prepareKey();
+ }
+
+ if (crypto != null)
+ crypto.prepareKey();
+ while (size > 0) {
+ int r = file.read(buf, 0, Math.min(size, buf.length));
+ size -= r;
+
+ if (decrypt != null)
+ decrypt.encryptRC4(buf, 0, r); //added by ujihara for decryption
+
+ if (crypto != null)
+ crypto.encryptRC4(buf, 0, r);
+ os.write(buf, 0, r);
+ }
+ }
+ finally {
+ if (!isOpen)
+ try{file.close();}catch(Exception e){}
+ }
+ }
+ }
+ os.write(ENDSTREAM);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PRTokeniser.java b/src/main/java/com/lowagie/text/pdf/PRTokeniser.java
new file mode 100644
index 0000000..9aaa881
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PRTokeniser.java
@@ -0,0 +1,585 @@
+/*
+ * $Id: PRTokeniser.java,v 1.51 2005/12/22 18:29:21 psoares33 Exp $
+ *
+ * Copyright 2001, 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.*;
+/**
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PRTokeniser {
+
+ public static final int TK_NUMBER = 1;
+ public static final int TK_STRING = 2;
+ public static final int TK_NAME = 3;
+ public static final int TK_COMMENT = 4;
+ public static final int TK_START_ARRAY = 5;
+ public static final int TK_END_ARRAY = 6;
+ public static final int TK_START_DIC = 7;
+ public static final int TK_END_DIC = 8;
+ public static final int TK_REF = 9;
+ public static final int TK_OTHER = 10;
+ public static final boolean delims[] = {
+ true, true, false, false, false, false, false, false, false, false,
+ true, true, false, true, true, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, true, false, false, false, false, true, false,
+ false, true, true, false, false, false, false, false, true, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, true, false, true, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, true, false, true, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false};
+
+ static final String EMPTY = "";
+
+
+ protected RandomAccessFileOrArray file;
+ protected int type;
+ protected String stringValue;
+ protected int reference;
+ protected int generation;
+ protected boolean hexString;
+
+ private static final int LINE_SEGMENT_SIZE = 256;
+
+ public PRTokeniser(String filename) throws IOException {
+ file = new RandomAccessFileOrArray(filename);
+ }
+
+ public PRTokeniser(byte pdfIn[]) {
+ file = new RandomAccessFileOrArray(pdfIn);
+ }
+
+ public PRTokeniser(RandomAccessFileOrArray file) {
+ this.file = file;
+ }
+
+ public void seek(int pos) throws IOException {
+ file.seek(pos);
+ }
+
+ public int getFilePointer() throws IOException {
+ return file.getFilePointer();
+ }
+
+ public void close() throws IOException {
+ file.close();
+ }
+
+ public int length() throws IOException {
+ return file.length();
+ }
+
+ public int read() throws IOException {
+ return file.read();
+ }
+
+ public RandomAccessFileOrArray getSafeFile() {
+ return new RandomAccessFileOrArray(file);
+ }
+
+ public RandomAccessFileOrArray getFile() {
+ return file;
+ }
+
+ public String readString(int size) throws IOException {
+ StringBuffer buf = new StringBuffer();
+ int ch;
+ while ((size--) > 0) {
+ ch = file.read();
+ if (ch == -1)
+ break;
+ buf.append((char)ch);
+ }
+ return buf.toString();
+ }
+
+ public static final boolean isWhitespace(int ch) {
+ return (ch == 0 || ch == 9 || ch == 10 || ch == 12 || ch == 13 || ch == 32);
+ }
+
+ public static final boolean isDelimiter(int ch) {
+ return (ch == '(' || ch == ')' || ch == '<' || ch == '>' || ch == '[' || ch == ']' || ch == '/' || ch == '%');
+ }
+
+ public static final boolean isDelimiterWhitespace(int ch) {
+ return delims[ch + 1];
+ }
+
+ public int getTokenType() {
+ return type;
+ }
+
+ public String getStringValue() {
+ return stringValue;
+ }
+
+ public int getReference() {
+ return reference;
+ }
+
+ public int getGeneration() {
+ return generation;
+ }
+
+ public void backOnePosition(int ch) throws IOException {
+ if (ch != -1)
+ file.pushBack((byte)ch);
+ }
+
+ public void throwError(String error) throws IOException {
+ throw new IOException(error + " at file pointer " + file.getFilePointer());
+ }
+
+ public char checkPdfHeader() throws IOException {
+ file.setStartOffset(0);
+ String str = readString(1024);
+ int idx = str.indexOf("%PDF-1.");
+ if (idx < 0)
+ throw new IOException("PDF header signature not found.");
+ file.setStartOffset(idx);
+ return str.charAt(idx + 7);
+ }
+
+ public void checkFdfHeader() throws IOException {
+ file.setStartOffset(0);
+ String str = readString(1024);
+ int idx = str.indexOf("%FDF-1.2");
+ if (idx < 0)
+ throw new IOException("FDF header signature not found.");
+ file.setStartOffset(idx);
+ }
+
+ public int getStartxref() throws IOException {
+ int size = Math.min(1024, file.length());
+ int pos = file.length() - size;
+ file.seek(pos);
+ String str = readString(1024);
+ int idx = str.lastIndexOf("startxref");
+ if (idx < 0)
+ throw new IOException("PDF startxref not found.");
+ return pos + idx;
+ }
+
+ public static int getHex(int v) {
+ if (v >= '0' && v <= '9')
+ return v - '0';
+ if (v >= 'A' && v <= 'F')
+ return v - 'A' + 10;
+ if (v >= 'a' && v <= 'f')
+ return v - 'a' + 10;
+ return -1;
+ }
+
+ public void nextValidToken() throws IOException {
+ int level = 0;
+ String n1 = null;
+ String n2 = null;
+ int ptr = 0;
+ while (nextToken()) {
+ if (type == TK_COMMENT)
+ continue;
+ switch (level) {
+ case 0:
+ {
+ if (type != TK_NUMBER)
+ return;
+ ptr = file.getFilePointer();
+ n1 = stringValue;
+ ++level;
+ break;
+ }
+ case 1:
+ {
+ if (type != TK_NUMBER) {
+ file.seek(ptr);
+ type = TK_NUMBER;
+ stringValue = n1;
+ return;
+ }
+ n2 = stringValue;
+ ++level;
+ break;
+ }
+ default:
+ {
+ if (type != TK_OTHER || !stringValue.equals("R")) {
+ file.seek(ptr);
+ type = TK_NUMBER;
+ stringValue = n1;
+ return;
+ }
+ type = TK_REF;
+ reference = Integer.parseInt(n1);
+ generation = Integer.parseInt(n2);
+ return;
+ }
+ }
+ }
+ throwError("Unexpected end of file");
+ }
+
+ public boolean nextToken() throws IOException {
+ StringBuffer outBuf = null;
+ stringValue = EMPTY;
+ int ch = 0;
+ do {
+ ch = file.read();
+ } while (ch != -1 && isWhitespace(ch));
+ if (ch == -1)
+ return false;
+ switch (ch) {
+ case '[':
+ type = TK_START_ARRAY;
+ break;
+ case ']':
+ type = TK_END_ARRAY;
+ break;
+ case '/':
+ {
+ outBuf = new StringBuffer();
+ type = TK_NAME;
+ while (true) {
+ ch = file.read();
+ if (delims[ch + 1])
+ break;
+ if (ch == '#') {
+ ch = (getHex(file.read()) << 4) + getHex(file.read());
+ }
+ outBuf.append((char)ch);
+ }
+ backOnePosition(ch);
+ break;
+ }
+ case '>':
+ ch = file.read();
+ if (ch != '>')
+ throwError("'>' not expected");
+ type = TK_END_DIC;
+ break;
+ case '<':
+ {
+ int v1 = file.read();
+ if (v1 == '<') {
+ type = TK_START_DIC;
+ break;
+ }
+ outBuf = new StringBuffer();
+ type = TK_STRING;
+ hexString = true;
+ int v2 = 0;
+ while (true) {
+ while (isWhitespace(v1))
+ v1 = file.read();
+ if (v1 == '>')
+ break;
+ v1 = getHex(v1);
+ if (v1 < 0)
+ break;
+ v2 = file.read();
+ while (isWhitespace(v2))
+ v2 = file.read();
+ if (v2 == '>') {
+ ch = v1 << 4;
+ outBuf.append((char)ch);
+ break;
+ }
+ v2 = getHex(v2);
+ if (v2 < 0)
+ break;
+ ch = (v1 << 4) + v2;
+ outBuf.append((char)ch);
+ v1 = file.read();
+ }
+ if (v1 < 0 || v2 < 0)
+ throwError("Error reading string");
+ break;
+ }
+ case '%':
+ type = TK_COMMENT;
+ do {
+ ch = file.read();
+ } while (ch != -1 && ch != '\r' && ch != '\n');
+ break;
+ case '(':
+ {
+ outBuf = new StringBuffer();
+ type = TK_STRING;
+ hexString = false;
+ int nesting = 0;
+ while (true) {
+ ch = file.read();
+ if (ch == -1)
+ break;
+ if (ch == '(') {
+ ++nesting;
+ }
+ else if (ch == ')') {
+ --nesting;
+ }
+ else if (ch == '\\') {
+ boolean lineBreak = false;
+ ch = file.read();
+ switch (ch) {
+ case 'n':
+ ch = '\n';
+ break;
+ case 'r':
+ ch = '\r';
+ break;
+ case 't':
+ ch = '\t';
+ break;
+ case 'b':
+ ch = '\b';
+ break;
+ case 'f':
+ ch = '\f';
+ break;
+ case '(':
+ case ')':
+ case '\\':
+ break;
+ case '\r':
+ lineBreak = true;
+ ch = file.read();
+ if (ch != '\n')
+ backOnePosition(ch);
+ break;
+ case '\n':
+ lineBreak = true;
+ break;
+ default:
+ {
+ if (ch < '0' || ch > '7') {
+ break;
+ }
+ int octal = ch - '0';
+ ch = file.read();
+ if (ch < '0' || ch > '7') {
+ backOnePosition(ch);
+ ch = octal;
+ break;
+ }
+ octal = (octal << 3) + ch - '0';
+ ch = file.read();
+ if (ch < '0' || ch > '7') {
+ backOnePosition(ch);
+ ch = octal;
+ break;
+ }
+ octal = (octal << 3) + ch - '0';
+ ch = octal & 0xff;
+ break;
+ }
+ }
+ if (lineBreak)
+ continue;
+ if (ch < 0)
+ break;
+ }
+ else if (ch == '\r') {
+ ch = file.read();
+ if (ch < 0)
+ break;
+ if (ch != '\n') {
+ backOnePosition(ch);
+ ch = '\n';
+ }
+ }
+ if (nesting == -1)
+ break;
+ outBuf.append((char)ch);
+ }
+ if (ch == -1)
+ throwError("Error reading string");
+ break;
+ }
+ default:
+ {
+ outBuf = new StringBuffer();
+ if (ch == '-' || ch == '+' || ch == '.' || (ch >= '0' && ch <= '9')) {
+ type = TK_NUMBER;
+ do {
+ outBuf.append((char)ch);
+ ch = file.read();
+ } while (ch != -1 && ((ch >= '0' && ch <= '9') || ch == '.'));
+ }
+ else {
+ type = TK_OTHER;
+ do {
+ outBuf.append((char)ch);
+ ch = file.read();
+ } while (!delims[ch + 1]);
+ }
+ backOnePosition(ch);
+ break;
+ }
+ }
+ if (outBuf != null)
+ stringValue = outBuf.toString();
+ return true;
+ }
+
+ public int intValue() {
+ return Integer.parseInt(stringValue);
+ }
+
+ public boolean readLineSegment(byte input[]) throws IOException {
+ int c = -1;
+ boolean eol = false;
+ int ptr = 0;
+ int len = input.length;
+ // ssteward, pdftk-1.10, 040922:
+ // skip initial whitespace; added this because PdfReader.rebuildXref()
+ // assumes that line provided by readLineSegment does not have init. whitespace;
+ if ( ptr < len ) {
+ while ( isWhitespace( (c = read()) ) );
+ }
+ while ( !eol && ptr < len ) {
+ switch (c) {
+ case -1:
+ case '\n':
+ eol = true;
+ break;
+ case '\r':
+ eol = true;
+ int cur = getFilePointer();
+ if ((read()) != '\n') {
+ seek(cur);
+ }
+ break;
+ default:
+ input[ptr++] = (byte)c;
+ break;
+ }
+
+ // break loop? do it before we read() again
+ if( eol || len <= ptr ) {
+ break;
+ }
+ else {
+ c = read();
+ }
+ }
+ if (ptr >= len) {
+ eol = false;
+ while (!eol) {
+ switch (c = read()) {
+ case -1:
+ case '\n':
+ eol = true;
+ break;
+ case '\r':
+ eol = true;
+ int cur = getFilePointer();
+ if ((read()) != '\n') {
+ seek(cur);
+ }
+ break;
+ }
+ }
+ }
+
+ if ((c == -1) && (ptr == 0)) {
+ return false;
+ }
+ if (ptr + 2 <= len) {
+ input[ptr++] = (byte)' ';
+ input[ptr] = (byte)'X';
+ }
+ return true;
+ }
+
+ public static int[] checkObjectStart(byte line[]) {
+ try {
+ PRTokeniser tk = new PRTokeniser(line);
+ int num = 0;
+ int gen = 0;
+ if (!tk.nextToken() || tk.getTokenType() != TK_NUMBER)
+ return null;
+ num = tk.intValue();
+ if (!tk.nextToken() || tk.getTokenType() != TK_NUMBER)
+ return null;
+ gen = tk.intValue();
+ if (!tk.nextToken())
+ return null;
+ if (!tk.getStringValue().equals("obj"))
+ return null;
+ return new int[]{num, gen};
+ }
+ catch (Exception ioe) {
+ // empty on purpose
+ }
+ return null;
+ }
+
+ public boolean isHexString() {
+ return this.hexString;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PageResources.java b/src/main/java/com/lowagie/text/pdf/PageResources.java
new file mode 100644
index 0000000..e065945
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PageResources.java
@@ -0,0 +1,187 @@
+/*
+ * $Id: PageResources.java,v 1.14 2005/10/17 17:35:34 psoares33 Exp $
+ *
+ * Copyright 2003-2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+class PageResources {
+
+ protected PdfDictionary fontDictionary = new PdfDictionary();
+ protected PdfDictionary xObjectDictionary = new PdfDictionary();
+ protected PdfDictionary colorDictionary = new PdfDictionary();
+ protected PdfDictionary patternDictionary = new PdfDictionary();
+ protected PdfDictionary shadingDictionary = new PdfDictionary();
+ protected PdfDictionary extGStateDictionary = new PdfDictionary();
+ protected PdfDictionary propertyDictionary = new PdfDictionary();
+ protected HashMap forbiddenNames;
+ protected PdfDictionary originalResources;
+ protected int namePtr[] = {0};
+ protected HashMap usedNames;
+
+ PageResources() {
+ }
+
+ void setOriginalResources(PdfDictionary resources, int newNamePtr[]) {
+ if (newNamePtr != null)
+ namePtr = newNamePtr;
+ originalResources = resources;
+ forbiddenNames = new HashMap();
+ usedNames = new HashMap();
+ if (resources == null)
+ return;
+ for (Iterator i = resources.getKeys().iterator(); i.hasNext();) {
+ PdfObject sub = PdfReader.getPdfObject(resources.get((PdfName)i.next()));
+ if (sub.isDictionary()) {
+ PdfDictionary dic = (PdfDictionary)sub;
+ for (Iterator j = dic.getKeys().iterator(); j.hasNext();) {
+ forbiddenNames.put(j.next(), null);
+ }
+ }
+ }
+ }
+
+ PdfName translateName(PdfName name) {
+ PdfName translated = name;
+ if (forbiddenNames != null) {
+ translated = (PdfName)usedNames.get(name);
+ if (translated == null) {
+ while (true) {
+ translated = new PdfName("Xi" + (namePtr[0]++));
+ if (!forbiddenNames.containsKey(translated))
+ break;
+ }
+ usedNames.put(name, translated);
+ }
+ }
+ return translated;
+ }
+
+ PdfName addFont(PdfName name, PdfIndirectReference reference) {
+ name = translateName(name);
+ fontDictionary.put(name, reference);
+ return name;
+ }
+
+ PdfName addXObject(PdfName name, PdfIndirectReference reference) {
+ name = translateName(name);
+ xObjectDictionary.put(name, reference);
+ return name;
+ }
+
+ PdfName addColor(PdfName name, PdfIndirectReference reference) {
+ name = translateName(name);
+ colorDictionary.put(name, reference);
+ return name;
+ }
+
+ void addDefaultColor(PdfName name, PdfObject obj) {
+ if (obj == null || obj.isNull())
+ colorDictionary.remove(name);
+ else
+ colorDictionary.put(name, obj);
+ }
+
+ void addDefaultColor(PdfDictionary dic) {
+ colorDictionary.merge(dic);
+ }
+
+ void addDefaultColorDiff(PdfDictionary dic) {
+ colorDictionary.mergeDifferent(dic);
+ }
+
+ PdfName addShading(PdfName name, PdfIndirectReference reference) {
+ name = translateName(name);
+ shadingDictionary.put(name, reference);
+ return name;
+ }
+
+ PdfName addPattern(PdfName name, PdfIndirectReference reference) {
+ name = translateName(name);
+ patternDictionary.put(name, reference);
+ return name;
+ }
+
+ PdfName addExtGState(PdfName name, PdfIndirectReference reference) {
+ name = translateName(name);
+ extGStateDictionary.put(name, reference);
+ return name;
+ }
+
+ PdfName addProperty(PdfName name, PdfIndirectReference reference) {
+ name = translateName(name);
+ propertyDictionary.put(name, reference);
+ return name;
+ }
+
+ PdfDictionary getResources() {
+ PdfResources resources = new PdfResources();
+ if (originalResources != null)
+ resources.putAll(originalResources);
+ resources.put(PdfName.PROCSET, new PdfLiteral("[/PDF /Text /ImageB /ImageC /ImageI]"));
+ resources.add(PdfName.FONT, fontDictionary);
+ resources.add(PdfName.XOBJECT, xObjectDictionary);
+ resources.add(PdfName.COLORSPACE, colorDictionary);
+ resources.add(PdfName.PATTERN, patternDictionary);
+ resources.add(PdfName.SHADING, shadingDictionary);
+ resources.add(PdfName.EXTGSTATE, extGStateDictionary);
+ resources.add(PdfName.PROPERTIES, propertyDictionary);
+ return resources;
+ }
+
+ boolean hasResources() {
+ return (fontDictionary.size() > 0
+ || xObjectDictionary.size() > 0
+ || colorDictionary.size() > 0
+ || patternDictionary.size() > 0
+ || shadingDictionary.size() > 0
+ || extGStateDictionary.size() > 0
+ || propertyDictionary.size() > 0);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PatternColor.java b/src/main/java/com/lowagie/text/pdf/PatternColor.java
new file mode 100644
index 0000000..4268e1c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PatternColor.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+/** Represents a pattern. Can be used in high-level constructs (Paragraph, Cell, etc.).
+ */
+public class PatternColor extends ExtendedColor {
+ /**
+ * The actual pattern.
+ */
+ PdfPatternPainter painter;
+
+ /** Creates a color representing a pattern.
+ * @param painter the actual pattern
+ */
+ public PatternColor(PdfPatternPainter painter) {
+ super(TYPE_PATTERN, .5f, .5f, .5f);
+ this.painter = painter;
+ }
+
+ /** Gets the pattern.
+ * @return the pattern
+ */
+ public PdfPatternPainter getPainter() {
+ return this.painter;
+ }
+
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ public int hashCode() {
+ return painter.hashCode();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfAcroForm.java b/src/main/java/com/lowagie/text/pdf/PdfAcroForm.java
new file mode 100644
index 0000000..7893eb8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfAcroForm.java
@@ -0,0 +1,744 @@
+/*
+ * $Id: PdfAcroForm.java,v 1.44 2005/03/30 10:10:58 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.Iterator;
+import java.util.HashMap;
+
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.ExceptionConverter;
+
+/**
+ * Each PDF document can contain maximum 1 AcroForm.
+ */
+
+public class PdfAcroForm extends PdfDictionary {
+
+ private PdfWriter writer;
+
+
+ /** This is a map containing FieldTemplates. */
+ private HashMap fieldTemplates = new HashMap();
+
+ /** This is an array containing DocumentFields. */
+ private PdfArray documentFields = new PdfArray();
+
+ /** This is an array containing the calculationorder of the fields. */
+ private PdfArray calculationOrder = new PdfArray();
+
+ /** Contains the signature flags. */
+ private int sigFlags = 0;
+
+ /** Creates new PdfAcroForm
+ * @param writer*/
+ PdfAcroForm(PdfWriter writer) {
+ super();
+ this.writer = writer;
+ }
+
+ /**
+ * Adds fieldTemplates.
+ * @param ft
+ */
+
+ void addFieldTemplates(HashMap ft) {
+ fieldTemplates.putAll(ft);
+ }
+
+ /**
+ * Adds documentFields.
+ * @param ref
+ */
+
+ void addDocumentField(PdfIndirectReference ref) {
+ documentFields.add(ref);
+ }
+
+ /**
+ * Checks if the Acroform is valid
+ * @return true if the Acroform is valid
+ */
+
+ boolean isValid() {
+ if (documentFields.size() == 0) return false;
+ put(PdfName.FIELDS, documentFields);
+ if (sigFlags != 0)
+ put(PdfName.SIGFLAGS, new PdfNumber(sigFlags));
+ if (calculationOrder.size() > 0)
+ put(PdfName.CO, calculationOrder);
+ if (fieldTemplates.size() == 0) return true;
+ PdfDictionary dic = new PdfDictionary();
+ for (Iterator it = fieldTemplates.keySet().iterator(); it.hasNext();) {
+ PdfTemplate template = (PdfTemplate)it.next();
+ PdfFormField.mergeResources(dic, (PdfDictionary)template.getResources());
+ }
+ put(PdfName.DR, dic);
+ PdfDictionary fonts = (PdfDictionary)dic.get(PdfName.FONT);
+ if (fonts != null) {
+ put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g "));
+ writer.eliminateFontSubset(fonts);
+ }
+ return true;
+ }
+
+ /**
+ * Adds an object to the calculationOrder.
+ * @param formField
+ */
+
+ public void addCalculationOrder(PdfFormField formField) {
+ calculationOrder.add(formField.getIndirectReference());
+ }
+
+ /**
+ * Sets the signature flags.
+ * @param f
+ */
+
+ public void setSigFlags(int f) {
+ sigFlags |= f;
+ }
+
+ /**
+ * Adds a formfield to the AcroForm.
+ * @param formField
+ */
+
+ public void addFormField(PdfFormField formField) {
+ writer.addAnnotation(formField);
+ }
+
+ /**
+ * @param name
+ * @param caption
+ * @param value
+ * @param url
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addHtmlPostButton(String name, String caption, String value, String url, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfAction action = PdfAction.createSubmitForm(url, null, PdfAction.SUBMIT_HTML_FORMAT);
+ PdfFormField button = new PdfFormField(writer, llx, lly, urx, ury, action);
+ setButtonParams(button, PdfFormField.FF_PUSHBUTTON, name, value);
+ drawButton(button, caption, font, fontSize, llx, lly, urx, ury);
+ addFormField(button);
+ return button;
+ }
+
+ /**
+ * @param name
+ * @param caption
+ * @param value
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addResetButton(String name, String caption, String value, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfAction action = PdfAction.createResetForm(null, 0);
+ PdfFormField button = new PdfFormField(writer, llx, lly, urx, ury, action);
+ setButtonParams(button, PdfFormField.FF_PUSHBUTTON, name, value);
+ drawButton(button, caption, font, fontSize, llx, lly, urx, ury);
+ addFormField(button);
+ return button;
+ }
+
+ /**
+ * @param name
+ * @param value
+ * @param url
+ * @param appearance
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addMap(String name, String value, String url, PdfContentByte appearance, float llx, float lly, float urx, float ury) {
+ PdfAction action = PdfAction.createSubmitForm(url, null, PdfAction.SUBMIT_HTML_FORMAT | PdfAction.SUBMIT_COORDINATES);
+ PdfFormField button = new PdfFormField(writer, llx, lly, urx, ury, action);
+ setButtonParams(button, PdfFormField.FF_PUSHBUTTON, name, null);
+ PdfContentByte cb = writer.getDirectContent();
+ PdfAppearance pa = cb.createAppearance(urx - llx, ury - lly);
+ pa.add(appearance);
+ button.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, pa);
+ addFormField(button);
+ return button;
+ }
+
+ /**
+ * @param button
+ * @param characteristics
+ * @param name
+ * @param value
+ */
+ public void setButtonParams(PdfFormField button, int characteristics, String name, String value) {
+ button.setButton(characteristics);
+ button.setFlags(PdfAnnotation.FLAGS_PRINT);
+ button.setPage();
+ button.setFieldName(name);
+ if (value != null) button.setValueAsString(value);
+ }
+
+ /**
+ * @param button
+ * @param caption
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void drawButton(PdfFormField button, String caption, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfContentByte cb = writer.getDirectContent();
+ PdfAppearance pa = cb.createAppearance(urx - llx, ury - lly);
+ pa.drawButton(0f, 0f, urx - llx, ury - lly, caption, font, fontSize);
+ button.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, pa);
+ }
+
+ /**
+ * @param name
+ * @param value
+ * @return a PdfFormField
+ */
+ public PdfFormField addHiddenField(String name, String value) {
+ PdfFormField hidden = PdfFormField.createEmpty(writer);
+ hidden.setFieldName(name);
+ hidden.setValueAsName(value);
+ addFormField(hidden);
+ return hidden;
+ }
+
+ /**
+ * @param name
+ * @param text
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addSingleLineTextField(String name, String text, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfFormField field = PdfFormField.createTextField(writer, PdfFormField.SINGLELINE, PdfFormField.PLAINTEXT, 0);
+ setTextFieldParams(field, text, name, llx, lly, urx, ury);
+ drawSingleLineOfText(field, text, font, fontSize, llx, lly, urx, ury);
+ addFormField(field);
+ return field;
+ }
+
+ /**
+ * @param name
+ * @param text
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addMultiLineTextField(String name, String text, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfFormField field = PdfFormField.createTextField(writer, PdfFormField.MULTILINE, PdfFormField.PLAINTEXT, 0);
+ setTextFieldParams(field, text, name, llx, lly, urx, ury);
+ drawMultiLineOfText(field, text, font, fontSize, llx, lly, urx, ury);
+ addFormField(field);
+ return field;
+ }
+
+ /**
+ * @param name
+ * @param text
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return PdfFormField
+ */
+ public PdfFormField addSingleLinePasswordField(String name, String text, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfFormField field = PdfFormField.createTextField(writer, PdfFormField.SINGLELINE, PdfFormField.PASSWORD, 0);
+ setTextFieldParams(field, text, name, llx, lly, urx, ury);
+ drawSingleLineOfText(field, text, font, fontSize, llx, lly, urx, ury);
+ addFormField(field);
+ return field;
+ }
+
+ /**
+ * @param field
+ * @param text
+ * @param name
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void setTextFieldParams(PdfFormField field, String text, String name, float llx, float lly, float urx, float ury) {
+ field.setWidget(new Rectangle(llx, lly, urx, ury), PdfAnnotation.HIGHLIGHT_INVERT);
+ field.setValueAsString(text);
+ field.setDefaultValueAsString(text);
+ field.setFieldName(name);
+ field.setFlags(PdfAnnotation.FLAGS_PRINT);
+ field.setPage();
+ }
+
+ /**
+ * @param field
+ * @param text
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void drawSingleLineOfText(PdfFormField field, String text, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfContentByte cb = writer.getDirectContent();
+ PdfAppearance tp = cb.createAppearance(urx - llx, ury - lly);
+ PdfAppearance tp2 = (PdfAppearance)tp.getDuplicate();
+ tp2.setFontAndSize(font, fontSize);
+ tp2.resetRGBColorFill();
+ field.setDefaultAppearanceString(tp2);
+ tp.drawTextField(0f, 0f, urx - llx, ury - lly);
+ tp.beginVariableText();
+ tp.saveState();
+ tp.rectangle(3f, 3f, urx - llx - 6f, ury - lly - 6f);
+ tp.clip();
+ tp.newPath();
+ tp.beginText();
+ tp.setFontAndSize(font, fontSize);
+ tp.resetRGBColorFill();
+ tp.setTextMatrix(4, (ury - lly) / 2 - (fontSize * 0.3f));
+ tp.showText(text);
+ tp.endText();
+ tp.restoreState();
+ tp.endVariableText();
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tp);
+ }
+
+ /**
+ * @param field
+ * @param text
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void drawMultiLineOfText(PdfFormField field, String text, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfContentByte cb = writer.getDirectContent();
+ PdfAppearance tp = cb.createAppearance(urx - llx, ury - lly);
+ PdfAppearance tp2 = (PdfAppearance)tp.getDuplicate();
+ tp2.setFontAndSize(font, fontSize);
+ tp2.resetRGBColorFill();
+ field.setDefaultAppearanceString(tp2);
+ tp.drawTextField(0f, 0f, urx - llx, ury - lly);
+ tp.beginVariableText();
+ tp.saveState();
+ tp.rectangle(3f, 3f, urx - llx - 6f, ury - lly - 6f);
+ tp.clip();
+ tp.newPath();
+ tp.beginText();
+ tp.setFontAndSize(font, fontSize);
+ tp.resetRGBColorFill();
+ tp.setTextMatrix(4, 5);
+ java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(text, "\n");
+ float yPos = ury - lly;
+ while (tokenizer.hasMoreTokens()) {
+ yPos -= fontSize * 1.2f;
+ tp.showTextAligned(PdfContentByte.ALIGN_LEFT, tokenizer.nextToken(), 3, yPos, 0);
+ }
+ tp.endText();
+ tp.restoreState();
+ tp.endVariableText();
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tp);
+ }
+
+ /**
+ * @param name
+ * @param value
+ * @param status
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addCheckBox(String name, String value, boolean status, float llx, float lly, float urx, float ury) {
+ PdfFormField field = PdfFormField.createCheckBox(writer);
+ setCheckBoxParams(field, name, value, status, llx, lly, urx, ury);
+ drawCheckBoxAppearences(field, value, llx, lly, urx, ury);
+ addFormField(field);
+ return field;
+ }
+
+ /**
+ * @param field
+ * @param name
+ * @param value
+ * @param status
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void setCheckBoxParams(PdfFormField field, String name, String value, boolean status, float llx, float lly, float urx, float ury) {
+ field.setWidget(new Rectangle(llx, lly, urx, ury), PdfAnnotation.HIGHLIGHT_TOGGLE);
+ field.setFieldName(name);
+ if (status) {
+ field.setValueAsName(value);
+ field.setAppearanceState(value);
+ }
+ else {
+ field.setValueAsName("Off");
+ field.setAppearanceState("Off");
+ }
+ field.setFlags(PdfAnnotation.FLAGS_PRINT);
+ field.setPage();
+ field.setBorderStyle(new PdfBorderDictionary(1, PdfBorderDictionary.STYLE_SOLID));
+ }
+
+ /**
+ * @param field
+ * @param value
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void drawCheckBoxAppearences(PdfFormField field, String value, float llx, float lly, float urx, float ury) {
+ BaseFont font = null;
+ try {
+ font = BaseFont.createFont(BaseFont.ZAPFDINGBATS, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
+ }
+ catch(Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ float size = (ury - lly);
+ PdfContentByte cb = writer.getDirectContent();
+ PdfAppearance tpOn = cb.createAppearance(urx - llx, ury - lly);
+ PdfAppearance tp2 = (PdfAppearance)tpOn.getDuplicate();
+ tp2.setFontAndSize(font, size);
+ tp2.resetRGBColorFill();
+ field.setDefaultAppearanceString(tp2);
+ tpOn.drawTextField(0f, 0f, urx - llx, ury - lly);
+ tpOn.saveState();
+ tpOn.resetRGBColorFill();
+ tpOn.beginText();
+ tpOn.setFontAndSize(font, size);
+ tpOn.showTextAligned(PdfContentByte.ALIGN_CENTER, "4", (urx - llx) / 2, (ury - lly) / 2 - (size * 0.3f), 0);
+ tpOn.endText();
+ tpOn.restoreState();
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, value, tpOn);
+ PdfAppearance tpOff = cb.createAppearance(urx - llx, ury - lly);
+ tpOff.drawTextField(0f, 0f, urx - llx, ury - lly);
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, "Off", tpOff);
+ }
+
+ /**
+ * @param name
+ * @param defaultValue
+ * @param noToggleToOff
+ * @return a PdfFormField
+ */
+ public PdfFormField getRadioGroup(String name, String defaultValue, boolean noToggleToOff) {
+ PdfFormField radio = PdfFormField.createRadioButton(writer, noToggleToOff);
+ radio.setFieldName(name);
+ radio.setValueAsName(defaultValue);
+ return radio;
+ }
+
+ /**
+ * @param radiogroup
+ */
+ public void addRadioGroup(PdfFormField radiogroup) {
+ addFormField(radiogroup);
+ }
+
+ /**
+ * @param radiogroup
+ * @param value
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addRadioButton(PdfFormField radiogroup, String value, float llx, float lly, float urx, float ury) {
+ PdfFormField radio = PdfFormField.createEmpty(writer);
+ radio.setWidget(new Rectangle(llx, lly, urx, ury), PdfAnnotation.HIGHLIGHT_TOGGLE);
+ String name = ((PdfName)radiogroup.get(PdfName.V)).toString().substring(1);
+ if (name.equals(value)) {
+ radio.setAppearanceState(value);
+ }
+ else {
+ radio.setAppearanceState("Off");
+ }
+ drawRadioAppearences(radio, value, llx, lly, urx, ury);
+ radiogroup.addKid(radio);
+ return radio;
+ }
+
+ /**
+ * @param field
+ * @param value
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void drawRadioAppearences(PdfFormField field, String value, float llx, float lly, float urx, float ury) {
+ PdfContentByte cb = writer.getDirectContent();
+ PdfAppearance tpOn = cb.createAppearance(urx - llx, ury - lly);
+ tpOn.drawRadioField(0f, 0f, urx - llx, ury - lly, true);
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, value, tpOn);
+ PdfAppearance tpOff = cb.createAppearance(urx - llx, ury - lly);
+ tpOff.drawRadioField(0f, 0f, urx - llx, ury - lly, false);
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, "Off", tpOff);
+ }
+
+ /**
+ * @param name
+ * @param options
+ * @param defaultValue
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addSelectList(String name, String[] options, String defaultValue, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfFormField choice = PdfFormField.createList(writer, options, 0);
+ setChoiceParams(choice, name, defaultValue, llx, lly, urx, ury);
+ StringBuffer text = new StringBuffer();
+ for (int i = 0; i < options.length; i++) {
+ text.append(options[i]).append("\n");
+ }
+ drawMultiLineOfText(choice, text.toString(), font, fontSize, llx, lly, urx, ury);
+ addFormField(choice);
+ return choice;
+ }
+
+ /**
+ * @param name
+ * @param options
+ * @param defaultValue
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addSelectList(String name, String[][] options, String defaultValue, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfFormField choice = PdfFormField.createList(writer, options, 0);
+ setChoiceParams(choice, name, defaultValue, llx, lly, urx, ury);
+ StringBuffer text = new StringBuffer();
+ for (int i = 0; i < options.length; i++) {
+ text.append(options[i][1]).append("\n");
+ }
+ drawMultiLineOfText(choice, text.toString(), font, fontSize, llx, lly, urx, ury);
+ addFormField(choice);
+ return choice;
+ }
+
+ /**
+ * @param name
+ * @param options
+ * @param defaultValue
+ * @param editable
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addComboBox(String name, String[] options, String defaultValue, boolean editable, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfFormField choice = PdfFormField.createCombo(writer, editable, options, 0);
+ setChoiceParams(choice, name, defaultValue, llx, lly, urx, ury);
+ if (defaultValue == null) {
+ defaultValue = options[0];
+ }
+ drawSingleLineOfText(choice, defaultValue, font, fontSize, llx, lly, urx, ury);
+ addFormField(choice);
+ return choice;
+ }
+
+ /**
+ * @param name
+ * @param options
+ * @param defaultValue
+ * @param editable
+ * @param font
+ * @param fontSize
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addComboBox(String name, String[][] options, String defaultValue, boolean editable, BaseFont font, float fontSize, float llx, float lly, float urx, float ury) {
+ PdfFormField choice = PdfFormField.createCombo(writer, editable, options, 0);
+ setChoiceParams(choice, name, defaultValue, llx, lly, urx, ury);
+ String value = null;
+ for (int i = 0; i < options.length; i++) {
+ if (options[i][0].equals(defaultValue)) {
+ value = options[i][1];
+ break;
+ }
+ }
+ if (value == null) {
+ value = options[0][1];
+ }
+ drawSingleLineOfText(choice, value, font, fontSize, llx, lly, urx, ury);
+ addFormField(choice);
+ return choice;
+ }
+
+ /**
+ * @param field
+ * @param name
+ * @param defaultValue
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void setChoiceParams(PdfFormField field, String name, String defaultValue, float llx, float lly, float urx, float ury) {
+ field.setWidget(new Rectangle(llx, lly, urx, ury), PdfAnnotation.HIGHLIGHT_INVERT);
+ if (defaultValue != null) {
+ field.setValueAsString(defaultValue);
+ field.setDefaultValueAsString(defaultValue);
+ }
+ field.setFieldName(name);
+ field.setFlags(PdfAnnotation.FLAGS_PRINT);
+ field.setPage();
+ field.setBorderStyle(new PdfBorderDictionary(2, PdfBorderDictionary.STYLE_SOLID));
+ }
+
+ /**
+ * @param name
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @return a PdfFormField
+ */
+ public PdfFormField addSignature(String name,
+ float llx, float lly, float urx, float ury) {
+ PdfFormField signature = PdfFormField.createSignature(writer);
+ setSignatureParams(signature, name, llx, lly, urx, ury);
+ drawSignatureAppearences(signature, llx, lly, urx, ury);
+ addFormField(signature);
+ return signature;
+ }
+
+ /**
+ * @param field
+ * @param name
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void setSignatureParams(PdfFormField field, String name,
+ float llx, float lly, float urx, float ury) {
+ field.setWidget(new Rectangle(llx, lly, urx, ury), PdfAnnotation.HIGHLIGHT_INVERT);
+ field.setFieldName(name);
+ field.setFlags(PdfAnnotation.FLAGS_PRINT);
+ field.setPage();
+ field.setMKBorderColor(java.awt.Color.black);
+ field.setMKBackgroundColor(java.awt.Color.white);
+ }
+
+ /**
+ * @param field
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void drawSignatureAppearences(PdfFormField field,
+ float llx, float lly, float urx, float ury) {
+ PdfContentByte cb = writer.getDirectContent();
+ PdfAppearance tp = cb.createAppearance(urx - llx, ury - lly);
+ tp.setGrayFill(1.0f);
+ tp.rectangle(0, 0, urx - llx, ury - lly);
+ tp.fill();
+ tp.setGrayStroke(0);
+ tp.setLineWidth(1);
+ tp.rectangle(0.5f, 0.5f, urx - llx - 0.5f, ury - lly - 0.5f);
+ tp.closePathStroke();
+ tp.saveState();
+ tp.rectangle(1, 1, urx - llx - 2, ury - lly - 2);
+ tp.clip();
+ tp.newPath();
+ tp.restoreState();
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tp);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfAction.java b/src/main/java/com/lowagie/text/pdf/PdfAction.java
new file mode 100644
index 0000000..054fd30
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfAction.java
@@ -0,0 +1,558 @@
+/*
+ * $Id: PdfAction.java,v 1.71 2006/05/18 09:38:35 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.net.URL;
+import com.lowagie.text.ExceptionConverter;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * A PdfAction
defines an action that can be triggered from a PDF file.
+ *
+ * @see PdfDictionary
+ */
+
+public class PdfAction extends PdfDictionary {
+
+ /** A named action to go to the first page.
+ */
+ public static final int FIRSTPAGE = 1;
+ /** A named action to go to the previous page.
+ */
+ public static final int PREVPAGE = 2;
+ /** A named action to go to the next page.
+ */
+ public static final int NEXTPAGE = 3;
+ /** A named action to go to the last page.
+ */
+ public static final int LASTPAGE = 4;
+
+ /** A named action to open a print dialog.
+ */
+ public static final int PRINTDIALOG = 5;
+
+ /** a possible submitvalue */
+ public static final int SUBMIT_EXCLUDE = 1;
+ /** a possible submitvalue */
+ public static final int SUBMIT_INCLUDE_NO_VALUE_FIELDS = 2;
+ /** a possible submitvalue */
+ public static final int SUBMIT_HTML_FORMAT = 4;
+ /** a possible submitvalue */
+ public static final int SUBMIT_HTML_GET = 8;
+ /** a possible submitvalue */
+ public static final int SUBMIT_COORDINATES = 16;
+ /** a possible submitvalue */
+ public static final int SUBMIT_XFDF = 32;
+ /** a possible submitvalue */
+ public static final int SUBMIT_INCLUDE_APPEND_SAVES = 64;
+ /** a possible submitvalue */
+ public static final int SUBMIT_INCLUDE_ANNOTATIONS = 128;
+ /** a possible submitvalue */
+ public static final int SUBMIT_PDF = 256;
+ /** a possible submitvalue */
+ public static final int SUBMIT_CANONICAL_FORMAT = 512;
+ /** a possible submitvalue */
+ public static final int SUBMIT_EXCL_NON_USER_ANNOTS = 1024;
+ /** a possible submitvalue */
+ public static final int SUBMIT_EXCL_F_KEY = 2048;
+ /** a possible submitvalue */
+ public static final int SUBMIT_EMBED_FORM = 8196;
+ /** a possible submitvalue */
+ public static final int RESET_EXCLUDE = 1;
+
+ // constructors
+
+ /** Create an empty action.
+ */
+ public PdfAction() {
+ }
+
+ /**
+ * Constructs a new PdfAction
of Subtype URI.
+ *
+ * @param url the Url to go to
+ */
+
+ public PdfAction(URL url) {
+ this(url.toExternalForm());
+ }
+
+ /**
+ * Construct a new PdfAction
of Subtype URI that accepts the x and y coordinate of the position that was clicked.
+ * @param url
+ * @param isMap
+ */
+ public PdfAction(URL url, boolean isMap) {
+ this(url.toExternalForm(), isMap);
+ }
+
+ /**
+ * Constructs a new PdfAction
of Subtype URI.
+ *
+ * @param url the url to go to
+ */
+
+ public PdfAction(String url) {
+ this(url, false);
+ }
+
+ /**
+ * Construct a new PdfAction
of Subtype URI that accepts the x and y coordinate of the position that was clicked.
+ * @param url
+ * @param isMap
+ */
+
+ public PdfAction(String url, boolean isMap) {
+ put(PdfName.S, PdfName.URI);
+ put(PdfName.URI, new PdfString(url));
+ if (isMap)
+ put(PdfName.ISMAP, PdfBoolean.PDFTRUE);
+ }
+
+ /**
+ * Constructs a new PdfAction
of Subtype GoTo.
+ * @param destination the destination to go to
+ */
+
+ PdfAction(PdfIndirectReference destination) {
+ put(PdfName.S, PdfName.GOTO);
+ put(PdfName.D, destination);
+ }
+
+ /**
+ * Constructs a new PdfAction
of Subtype GoToR.
+ * @param filename the file name to go to
+ * @param name the named destination to go to
+ */
+
+ public PdfAction(String filename, String name) {
+ put(PdfName.S, PdfName.GOTOR);
+ put(PdfName.F, new PdfString(filename));
+ put(PdfName.D, new PdfString(name));
+ }
+
+ /**
+ * Constructs a new PdfAction
of Subtype GoToR.
+ * @param filename the file name to go to
+ * @param page the page destination to go to
+ */
+
+ public PdfAction(String filename, int page) {
+ put(PdfName.S, PdfName.GOTOR);
+ put(PdfName.F, new PdfString(filename));
+ put(PdfName.D, new PdfLiteral("[" + (page - 1) + " /FitH 10000]"));
+ }
+
+ /** Implements name actions. The action can be FIRSTPAGE, LASTPAGE,
+ * NEXTPAGE, PREVPAGE and PRINTDIALOG.
+ * @param named the named action
+ */
+ public PdfAction(int named) {
+ put(PdfName.S, PdfName.NAMED);
+ switch (named) {
+ case FIRSTPAGE:
+ put(PdfName.N, PdfName.FIRSTPAGE);
+ break;
+ case LASTPAGE:
+ put(PdfName.N, PdfName.LASTPAGE);
+ break;
+ case NEXTPAGE:
+ put(PdfName.N, PdfName.NEXTPAGE);
+ break;
+ case PREVPAGE:
+ put(PdfName.N, PdfName.PREVPAGE);
+ break;
+ case PRINTDIALOG:
+ put(PdfName.S, PdfName.JAVASCRIPT);
+ put(PdfName.JS, new PdfString("this.print(true);\r"));
+ break;
+ default:
+ throw new RuntimeException("Invalid named action.");
+ }
+ }
+
+ /** Launchs an application or a document.
+ * @param application the application to be launched or the document to be opened or printed.
+ * @param parameters (Windows-specific) A parameter string to be passed to the application.
+ * It can be null
.
+ * @param operation (Windows-specific) the operation to perform: "open" - Open a document,
+ * "print" - Print a document.
+ * It can be null
.
+ * @param defaultDir (Windows-specific) the default directory in standard DOS syntax.
+ * It can be null
.
+ */
+ public PdfAction(String application, String parameters, String operation, String defaultDir) {
+ put(PdfName.S, PdfName.LAUNCH);
+ if (parameters == null && operation == null && defaultDir == null)
+ put(PdfName.F, new PdfString(application));
+ else {
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.F, new PdfString(application));
+ if (parameters != null)
+ dic.put(PdfName.P, new PdfString(parameters));
+ if (operation != null)
+ dic.put(PdfName.O, new PdfString(operation));
+ if (defaultDir != null)
+ dic.put(PdfName.D, new PdfString(defaultDir));
+ put(PdfName.WIN, dic);
+ }
+ }
+
+ /** Launchs an application or a document.
+ * @param application the application to be launched or the document to be opened or printed.
+ * @param parameters (Windows-specific) A parameter string to be passed to the application.
+ * It can be null
.
+ * @param operation (Windows-specific) the operation to perform: "open" - Open a document,
+ * "print" - Print a document.
+ * It can be null
.
+ * @param defaultDir (Windows-specific) the default directory in standard DOS syntax.
+ * It can be null
.
+ * @return a Launch action
+ */
+ public static PdfAction createLaunch(String application, String parameters, String operation, String defaultDir) {
+ return new PdfAction(application, parameters, operation, defaultDir);
+ }
+
+ /**Creates a Rendition action
+ * @param file
+ * @param fs
+ * @param mimeType
+ * @param ref
+ * @return a Media Clip action
+ * @throws IOException
+ */
+ public static PdfAction rendition(String file, PdfFileSpecification fs, String mimeType, PdfIndirectReference ref) throws IOException {
+ PdfAction js = new PdfAction();
+ js.put(PdfName.S, PdfName.RENDITION);
+ js.put(PdfName.R, new PdfRendition(file, fs, mimeType));
+ js.put(new PdfName("OP"), new PdfNumber(0));
+ js.put(new PdfName("AN"), ref);
+ return js;
+ }
+
+ /** Creates a JavaScript action. If the JavaScript is smaller than
+ * 50 characters it will be placed as a string, otherwise it will
+ * be placed as a compressed stream.
+ * @param code the JavaScript code
+ * @param writer the writer for this action
+ * @param unicode select JavaScript unicode. Note that the internal
+ * Acrobat JavaScript engine does not support unicode,
+ * so this may or may not work for you
+ * @return the JavaScript action
+ */
+ public static PdfAction javaScript(String code, PdfWriter writer, boolean unicode) {
+ PdfAction js = new PdfAction();
+ js.put(PdfName.S, PdfName.JAVASCRIPT);
+ if (unicode && code.length() < 50) {
+ js.put(PdfName.JS, new PdfString(code, PdfObject.TEXT_UNICODE));
+ }
+ else if (!unicode && code.length() < 100) {
+ js.put(PdfName.JS, new PdfString(code));
+ }
+ else {
+ try {
+ byte b[] = PdfEncodings.convertToBytes(code, unicode ? PdfObject.TEXT_UNICODE : PdfObject.TEXT_PDFDOCENCODING);
+ PdfStream stream = new PdfStream(b);
+ stream.flateCompress();
+ js.put(PdfName.JS, writer.addToBody(stream).getIndirectReference());
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+ return js;
+ }
+
+ /** Creates a JavaScript action. If the JavaScript is smaller than
+ * 50 characters it will be place as a string, otherwise it will
+ * be placed as a compressed stream.
+ * @param code the JavaScript code
+ * @param writer the writer for this action
+ * @return the JavaScript action
+ */
+ public static PdfAction javaScript(String code, PdfWriter writer) {
+ return javaScript(code, writer, false);
+ }
+
+ /**
+ * A Hide action hides or shows an object.
+ * @param obj object to hide or show
+ * @param hide true is hide, false is show
+ * @return a Hide Action
+ */
+ static PdfAction createHide(PdfObject obj, boolean hide) {
+ PdfAction action = new PdfAction();
+ action.put(PdfName.S, PdfName.HIDE);
+ action.put(PdfName.T, obj);
+ if (!hide)
+ action.put(PdfName.H, PdfBoolean.PDFFALSE);
+ return action;
+ }
+
+ /**
+ * A Hide action hides or shows an annotation.
+ * @param annot
+ * @param hide
+ * @return A Hide Action
+ */
+ public static PdfAction createHide(PdfAnnotation annot, boolean hide) {
+ return createHide(annot.getIndirectReference(), hide);
+ }
+
+ /**
+ * A Hide action hides or shows an annotation.
+ * @param name
+ * @param hide
+ * @return A Hide Action
+ */
+ public static PdfAction createHide(String name, boolean hide) {
+ return createHide(new PdfString(name), hide);
+ }
+
+ static PdfArray buildArray(Object names[]) {
+ PdfArray array = new PdfArray();
+ for (int k = 0; k < names.length; ++k) {
+ Object obj = names[k];
+ if (obj instanceof String)
+ array.add(new PdfString((String)obj));
+ else if (obj instanceof PdfAnnotation)
+ array.add(((PdfAnnotation)obj).getIndirectReference());
+ else
+ throw new RuntimeException("The array must contain String or PdfAnnotation.");
+ }
+ return array;
+ }
+
+ /**
+ * A Hide action hides or shows objects.
+ * @param names
+ * @param hide
+ * @return A Hide Action
+ */
+ public static PdfAction createHide(Object names[], boolean hide) {
+ return createHide(buildArray(names), hide);
+ }
+
+ /**
+ * Creates a submit form.
+ * @param file the URI to submit the form to
+ * @param names the objects to submit
+ * @param flags submit properties
+ * @return A PdfAction
+ */
+ public static PdfAction createSubmitForm(String file, Object names[], int flags) {
+ PdfAction action = new PdfAction();
+ action.put(PdfName.S, PdfName.SUBMITFORM);
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.F, new PdfString(file));
+ dic.put(PdfName.FS, PdfName.URL);
+ action.put(PdfName.F, dic);
+ if (names != null)
+ action.put(PdfName.FIELDS, buildArray(names));
+ action.put(PdfName.FLAGS, new PdfNumber(flags));
+ return action;
+ }
+
+ /**
+ * Creates a resetform.
+ * @param names the objects to reset
+ * @param flags submit properties
+ * @return A PdfAction
+ */
+ public static PdfAction createResetForm(Object names[], int flags) {
+ PdfAction action = new PdfAction();
+ action.put(PdfName.S, PdfName.RESETFORM);
+ if (names != null)
+ action.put(PdfName.FIELDS, buildArray(names));
+ action.put(PdfName.FLAGS, new PdfNumber(flags));
+ return action;
+ }
+
+ /**
+ * Creates an Import field.
+ * @param file
+ * @return A PdfAction
+ */
+ public static PdfAction createImportData(String file) {
+ PdfAction action = new PdfAction();
+ action.put(PdfName.S, PdfName.IMPORTDATA);
+ action.put(PdfName.F, new PdfString(file));
+ return action;
+ }
+
+ /** Add a chained action.
+ * @param na the next action
+ */
+ public void next(PdfAction na) {
+ PdfObject nextAction = get(PdfName.NEXT);
+ if (nextAction == null)
+ put(PdfName.NEXT, na);
+ else if (nextAction.isDictionary()) {
+ PdfArray array = new PdfArray(nextAction);
+ array.add(na);
+ put(PdfName.NEXT, array);
+ }
+ else {
+ ((PdfArray)nextAction).add(na);
+ }
+ }
+
+ /** Creates a GoTo action to an internal page.
+ * @param page the page to go. First page is 1
+ * @param dest the destination for the page
+ * @param writer the writer for this action
+ * @return a GoTo action
+ */
+ public static PdfAction gotoLocalPage(int page, PdfDestination dest, PdfWriter writer) {
+ PdfIndirectReference ref = writer.getPageReference(page);
+ dest.addPage(ref);
+ PdfAction action = new PdfAction();
+ action.put(PdfName.S, PdfName.GOTO);
+ action.put(PdfName.D, dest);
+ return action;
+ }
+
+ /**
+ * Creates a GoTo action to a named destination.
+ * @param dest the named destination
+ * @param isName if true sets the destination as a name, if false sets it as a String
+ * @return a GoTo action
+ */
+ public static PdfAction gotoLocalPage(String dest, boolean isName) {
+ PdfAction action = new PdfAction();
+ action.put(PdfName.S, PdfName.GOTO);
+ if (isName)
+ action.put(PdfName.D, new PdfName(dest));
+ else
+ action.put(PdfName.D, new PdfString(dest, null));
+ return action;
+ }
+
+ /**
+ * Creates a GoToR action to a named destination.
+ * @param filename the file name to go to
+ * @param dest the destination name
+ * @param isName if true sets the destination as a name, if false sets it as a String
+ * @param newWindow open the document in a new window if true
, if false the current document is replaced by the new document.
+ * @return a GoToR action
+ */
+ public static PdfAction gotoRemotePage(String filename, String dest, boolean isName, boolean newWindow) {
+ PdfAction action = new PdfAction();
+ action.put(PdfName.F, new PdfString(filename));
+ action.put(PdfName.S, PdfName.GOTOR);
+ if (isName)
+ action.put(PdfName.D, new PdfName(dest));
+ else
+ action.put(PdfName.D, new PdfString(dest, null));
+ if (newWindow)
+ action.put(PdfName.NEWWINDOW, PdfBoolean.PDFTRUE);
+ return action;
+ }
+
+ /**
+ * A set-OCG-state action (PDF 1.5) sets the state of one or more optional content
+ * groups.
+ * @param state an array consisting of any number of sequences beginning with a PdfName
+ * or String
(ON, OFF, or Toggle) followed by one or more optional content group dictionaries
+ * PdfLayer
or a PdfIndirectReference
to a PdfLayer
.
+ * The array elements are processed from left to right; each name is applied
+ * to the subsequent groups until the next name is encountered:
+ *
+ *
+ * @param preserveRB if true
, indicates that radio-button state relationships between optional
+ * content groups (as specified by the RBGroups entry in the current configuration
+ * dictionary) should be preserved when the states in the
+ * state
array are applied. That is, if a group is set to ON (either by ON or Toggle) during
+ * processing of the state
array, any other groups belong to the same radio-button
+ * group are turned OFF. If a group is set to OFF, there is no effect on other groups.
+ * If false
, radio-button state relationships, if any, are ignored
+ * @return the action
+ */
+ public static PdfAction setOCGstate(ArrayList state, boolean preserveRB) {
+ PdfAction action = new PdfAction();
+ action.put(PdfName.S, PdfName.SETOCGSTATE);
+ PdfArray a = new PdfArray();
+ for (int k = 0; k < state.size(); ++k) {
+ Object o = state.get(k);
+ if (o == null)
+ continue;
+ if (o instanceof PdfIndirectReference)
+ a.add((PdfIndirectReference)o);
+ else if (o instanceof PdfLayer)
+ a.add(((PdfLayer)o).getRef());
+ else if (o instanceof PdfName)
+ a.add((PdfName)o);
+ else if (o instanceof String) {
+ PdfName name = null;
+ String s = (String)o;
+ if (s.equalsIgnoreCase("on"))
+ name = PdfName.ON;
+ else if (s.equalsIgnoreCase("off"))
+ name = PdfName.OFF;
+ else if (s.equalsIgnoreCase("toggle"))
+ name = PdfName.TOGGLE;
+ else
+ throw new IllegalArgumentException("A string '" + s + " was passed in state. Only 'ON', 'OFF' and 'Toggle' are allowed.");
+ a.add(name);
+ }
+ else
+ throw new IllegalArgumentException("Invalid type was passed in state: " + o.getClass().getName());
+ }
+ action.put(PdfName.STATE, a);
+ if (!preserveRB)
+ action.put(PdfName.PRESERVERB, PdfBoolean.PDFFALSE);
+ return action;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfAnnotation.java b/src/main/java/com/lowagie/text/pdf/PdfAnnotation.java
new file mode 100644
index 0000000..53deae7
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfAnnotation.java
@@ -0,0 +1,742 @@
+/*
+ * $Id: PdfAnnotation.java,v 1.66 2006/06/04 22:23:39 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.Rectangle;
+import java.util.HashMap;
+import java.awt.Color;
+import java.io.*;
+/**
+ * A PdfAnnotation
is a note that is associated with a page.
+ *
+ * @see PdfDictionary
+ */
+
+public class PdfAnnotation extends PdfDictionary {
+ /** highlight attributename */
+ public static final PdfName HIGHLIGHT_NONE = PdfName.N;
+ /** highlight attributename */
+ public static final PdfName HIGHLIGHT_INVERT = PdfName.I;
+ /** highlight attributename */
+ public static final PdfName HIGHLIGHT_OUTLINE = PdfName.O;
+ /** highlight attributename */
+ public static final PdfName HIGHLIGHT_PUSH = PdfName.P;
+ /** highlight attributename */
+ public static final PdfName HIGHLIGHT_TOGGLE = PdfName.T;
+ /** flagvalue */
+ public static final int FLAGS_INVISIBLE = 1;
+ /** flagvalue */
+ public static final int FLAGS_HIDDEN = 2;
+ /** flagvalue */
+ public static final int FLAGS_PRINT = 4;
+ /** flagvalue */
+ public static final int FLAGS_NOZOOM = 8;
+ /** flagvalue */
+ public static final int FLAGS_NOROTATE = 16;
+ /** flagvalue */
+ public static final int FLAGS_NOVIEW = 32;
+ /** flagvalue */
+ public static final int FLAGS_READONLY = 64;
+ /** flagvalue */
+ public static final int FLAGS_LOCKED = 128;
+ /** flagvalue */
+ public static final int FLAGS_TOGGLENOVIEW = 256;
+ /** appearance attributename */
+ public static final PdfName APPEARANCE_NORMAL = PdfName.N;
+ /** appearance attributename */
+ public static final PdfName APPEARANCE_ROLLOVER = PdfName.R;
+ /** appearance attributename */
+ public static final PdfName APPEARANCE_DOWN = PdfName.D;
+ /** attributevalue */
+ public static final PdfName AA_ENTER = PdfName.E;
+ /** attributevalue */
+ public static final PdfName AA_EXIT = PdfName.X;
+ /** attributevalue */
+ public static final PdfName AA_DOWN = PdfName.D;
+ /** attributevalue */
+ public static final PdfName AA_UP = PdfName.U;
+ /** attributevalue */
+ public static final PdfName AA_FOCUS = PdfName.FO;
+ /** attributevalue */
+ public static final PdfName AA_BLUR = PdfName.BL;
+ /** attributevalue */
+ public static final PdfName AA_JS_KEY = PdfName.K;
+ /** attributevalue */
+ public static final PdfName AA_JS_FORMAT = PdfName.F;
+ /** attributevalue */
+ public static final PdfName AA_JS_CHANGE = PdfName.V;
+ /** attributevalue */
+ public static final PdfName AA_JS_OTHER_CHANGE = PdfName.C;
+ /** attributevalue */
+ public static final int MARKUP_HIGHLIGHT = 0;
+ /** attributevalue */
+ public static final int MARKUP_UNDERLINE = 1;
+ /** attributevalue */
+ public static final int MARKUP_STRIKEOUT = 2;
+
+ protected PdfWriter writer;
+ protected PdfIndirectReference reference;
+ protected HashMap templates;
+ protected boolean form = false;
+ protected boolean annotation = true;
+
+ /** Holds value of property used. */
+ protected boolean used = false;
+
+ /** Holds value of property placeInPage. */
+ private int placeInPage = -1;
+
+ // constructors
+ public PdfAnnotation(PdfWriter writer, Rectangle rect) {
+ this.writer = writer;
+ if (rect != null)
+ put(PdfName.RECT, new PdfRectangle(rect));
+ }
+
+/**
+ * Constructs a new PdfAnnotation
of subtype text.
+ * @param writer
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @param title
+ * @param content
+ */
+
+ PdfAnnotation(PdfWriter writer, float llx, float lly, float urx, float ury, PdfString title, PdfString content) {
+ this.writer = writer;
+ put(PdfName.SUBTYPE, PdfName.TEXT);
+ put(PdfName.T, title);
+ put(PdfName.RECT, new PdfRectangle(llx, lly, urx, ury));
+ put(PdfName.CONTENTS, content);
+ }
+
+/**
+ * Constructs a new PdfAnnotation
of subtype link (Action).
+ * @param writer
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @param action
+ */
+
+ public PdfAnnotation(PdfWriter writer, float llx, float lly, float urx, float ury, PdfAction action) {
+ this.writer = writer;
+ put(PdfName.SUBTYPE, PdfName.LINK);
+ put(PdfName.RECT, new PdfRectangle(llx, lly, urx, ury));
+ put(PdfName.A, action);
+ put(PdfName.BORDER, new PdfBorderArray(0, 0, 0));
+ put(PdfName.C, new PdfColor(0x00, 0x00, 0xFF));
+ }
+
+ /**
+ * Creates a screen PdfAnnotation
+ * @param writer
+ * @param rect
+ * @param clipTitle
+ * @param fs
+ * @param mimeType
+ * @param playOnDisplay
+ * @return a screen PdfAnnotation
+ * @throws IOException
+ */
+ public static PdfAnnotation createScreen(PdfWriter writer, Rectangle rect, String clipTitle, PdfFileSpecification fs,
+ String mimeType, boolean playOnDisplay) throws IOException {
+ PdfAnnotation ann = new PdfAnnotation(writer, rect);
+ ann.put(PdfName.SUBTYPE, PdfName.SCREEN);
+ ann.put (PdfName.F, new PdfNumber(FLAGS_PRINT));
+ ann.put(PdfName.TYPE, PdfName.ANNOT);
+ ann.setPage();
+ PdfIndirectReference ref = ann.getIndirectReference();
+ PdfAction action = PdfAction.rendition(clipTitle,fs,mimeType, ref);
+ PdfIndirectReference actionRef = writer.addToBody(action).getIndirectReference();
+ // for play on display add trigger event
+ if (playOnDisplay)
+ {
+ PdfDictionary aa = new PdfDictionary();
+ aa.put(new PdfName("PV"), actionRef);
+ ann.put(PdfName.AA, aa);
+ }
+ ann.put(PdfName.A, actionRef);
+ return ann;
+ }
+
+ public PdfIndirectReference getIndirectReference() {
+ if (reference == null) {
+ reference = writer.getPdfIndirectReference();
+ }
+ return reference;
+ }
+
+ /**
+ * @param writer
+ * @param rect
+ * @param title
+ * @param contents
+ * @param open
+ * @param icon
+ * @return a PdfAnnotation
+ */
+ public static PdfAnnotation createText(PdfWriter writer, Rectangle rect, String title, String contents, boolean open, String icon) {
+ PdfAnnotation annot = new PdfAnnotation(writer, rect);
+ annot.put(PdfName.SUBTYPE, PdfName.TEXT);
+ if (title != null)
+ annot.put(PdfName.T, new PdfString(title, PdfObject.TEXT_UNICODE));
+ if (contents != null)
+ annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
+ if (open)
+ annot.put(PdfName.OPEN, PdfBoolean.PDFTRUE);
+ if (icon != null) {
+ annot.put(PdfName.NAME, new PdfName(icon));
+ }
+ return annot;
+ }
+
+ /**
+ * Creates a link.
+ * @param writer
+ * @param rect
+ * @param highlight
+ * @return A PdfAnnotation
+ */
+ protected static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight) {
+ PdfAnnotation annot = new PdfAnnotation(writer, rect);
+ annot.put(PdfName.SUBTYPE, PdfName.LINK);
+ if (!highlight.equals(HIGHLIGHT_INVERT))
+ annot.put(PdfName.H, highlight);
+ return annot;
+ }
+
+ /**
+ * Creates an Annotation with an Action.
+ * @param writer
+ * @param rect
+ * @param highlight
+ * @param action
+ * @return A PdfAnnotation
+ */
+ public static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight, PdfAction action) {
+ PdfAnnotation annot = createLink(writer, rect, highlight);
+ annot.putEx(PdfName.A, action);
+ return annot;
+ }
+
+ /**
+ * Creates an Annotation with an local destination.
+ * @param writer
+ * @param rect
+ * @param highlight
+ * @param namedDestination
+ * @return A PdfAnnotation
+ */
+ public static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight, String namedDestination) {
+ PdfAnnotation annot = createLink(writer, rect, highlight);
+ annot.put(PdfName.DEST, new PdfString(namedDestination));
+ return annot;
+ }
+
+ /**
+ * Creates an Annotation with a PdfDestination.
+ * @param writer
+ * @param rect
+ * @param highlight
+ * @param page
+ * @param dest
+ * @return A PdfAnnotation
+ */
+ public static PdfAnnotation createLink(PdfWriter writer, Rectangle rect, PdfName highlight, int page, PdfDestination dest) {
+ PdfAnnotation annot = createLink(writer, rect, highlight);
+ PdfIndirectReference ref = writer.getPageReference(page);
+ dest.addPage(ref);
+ annot.put(PdfName.DEST, dest);
+ return annot;
+ }
+
+ /**
+ * Add some free text to the document.
+ * @param writer
+ * @param rect
+ * @param contents
+ * @param defaultAppearance
+ * @return A PdfAnnotation
+ */
+ public static PdfAnnotation createFreeText(PdfWriter writer, Rectangle rect, String contents, PdfContentByte defaultAppearance) {
+ PdfAnnotation annot = new PdfAnnotation(writer, rect);
+ annot.put(PdfName.SUBTYPE, PdfName.FREETEXT);
+ annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
+ annot.setDefaultAppearanceString(defaultAppearance);
+ return annot;
+ }
+
+ /**
+ * Adds a line to the document. Move over the line and a tooltip is shown.
+ * @param writer
+ * @param rect
+ * @param contents
+ * @param x1
+ * @param y1
+ * @param x2
+ * @param y2
+ * @return A PdfAnnotation
+ */
+ public static PdfAnnotation createLine(PdfWriter writer, Rectangle rect, String contents, float x1, float y1, float x2, float y2) {
+ PdfAnnotation annot = new PdfAnnotation(writer, rect);
+ annot.put(PdfName.SUBTYPE, PdfName.LINE);
+ annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
+ PdfArray array = new PdfArray(new PdfNumber(x1));
+ array.add(new PdfNumber(y1));
+ array.add(new PdfNumber(x2));
+ array.add(new PdfNumber(y2));
+ annot.put(PdfName.L, array);
+ return annot;
+ }
+
+ /**
+ * Adds a circle or a square that shows a tooltip when you pass over it.
+ * @param writer
+ * @param rect
+ * @param contents The tooltip
+ * @param square true if you want a square, false if you want a circle
+ * @return A PdfAnnotation
+ */
+ public static PdfAnnotation createSquareCircle(PdfWriter writer, Rectangle rect, String contents, boolean square) {
+ PdfAnnotation annot = new PdfAnnotation(writer, rect);
+ if (square)
+ annot.put(PdfName.SUBTYPE, PdfName.SQUARE);
+ else
+ annot.put(PdfName.SUBTYPE, PdfName.CIRCLE);
+ annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
+ return annot;
+ }
+
+ public static PdfAnnotation createMarkup(PdfWriter writer, Rectangle rect, String contents, int type, float quadPoints[]) {
+ PdfAnnotation annot = new PdfAnnotation(writer, rect);
+ PdfName name = PdfName.HIGHLIGHT;
+ switch (type) {
+ case MARKUP_UNDERLINE:
+ name = PdfName.UNDERLINE;
+ break;
+ case MARKUP_STRIKEOUT:
+ name = PdfName.STRIKEOUT;
+ break;
+ }
+ annot.put(PdfName.SUBTYPE, name);
+ annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
+ PdfArray array = new PdfArray();
+ for (int k = 0; k < quadPoints.length; ++k)
+ array.add(new PdfNumber(quadPoints[k]));
+ annot.put(PdfName.QUADPOINTS, array);
+ return annot;
+ }
+
+ /**
+ * Adds a Stamp to your document. Move over the stamp and a tooltip is shown
+ * @param writer
+ * @param rect
+ * @param contents
+ * @param name
+ * @return A PdfAnnotation
+ */
+ public static PdfAnnotation createStamp(PdfWriter writer, Rectangle rect, String contents, String name) {
+ PdfAnnotation annot = new PdfAnnotation(writer, rect);
+ annot.put(PdfName.SUBTYPE, PdfName.STAMP);
+ annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
+ annot.put(PdfName.NAME, new PdfName(name));
+ return annot;
+ }
+
+ public static PdfAnnotation createInk(PdfWriter writer, Rectangle rect, String contents, float inkList[][]) {
+ PdfAnnotation annot = new PdfAnnotation(writer, rect);
+ annot.put(PdfName.SUBTYPE, PdfName.INK);
+ annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
+ PdfArray outer = new PdfArray();
+ for (int k = 0; k < inkList.length; ++k) {
+ PdfArray inner = new PdfArray();
+ float deep[] = inkList[k];
+ for (int j = 0; j < deep.length; ++j)
+ inner.add(new PdfNumber(deep[j]));
+ outer.add(inner);
+ }
+ annot.put(PdfName.INKLIST, outer);
+ return annot;
+ }
+
+ /** Creates a file attachment annotation.
+ * @param writer the PdfWriter
+ * @param rect the dimensions in the page of the annotation
+ * @param contents the file description
+ * @param fileStore an array with the file. If it's null
+ * the file will be read from the disk
+ * @param file the path to the file. It will only be used if
+ * fileStore
is not null
+ * @param fileDisplay the actual file name stored in the pdf
+ * @throws IOException on error
+ * @return the annotation
+ */
+ public static PdfAnnotation createFileAttachment(PdfWriter writer, Rectangle rect, String contents, byte fileStore[], String file, String fileDisplay) throws IOException {
+ return createFileAttachment(writer, rect, contents, PdfFileSpecification.fileEmbedded(writer, file, fileDisplay, fileStore));
+ }
+
+ /** Creates a file attachment annotation
+ * @param writer
+ * @param rect
+ * @param contents
+ * @param fs
+ * @return the annotation
+ * @throws IOException
+ */
+ public static PdfAnnotation createFileAttachment(PdfWriter writer, Rectangle rect, String contents, PdfFileSpecification fs) throws IOException {
+ PdfAnnotation annot = new PdfAnnotation(writer, rect);
+ annot.put(PdfName.SUBTYPE, PdfName.FILEATTACHMENT);
+ if (contents != null)
+ annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
+ annot.put(PdfName.FS, fs.getReference());
+ return annot;
+ }
+
+ /**
+ * Adds a popup to your document.
+ * @param writer
+ * @param rect
+ * @param contents
+ * @param open
+ * @return A PdfAnnotation
+ */
+ public static PdfAnnotation createPopup(PdfWriter writer, Rectangle rect, String contents, boolean open) {
+ PdfAnnotation annot = new PdfAnnotation(writer, rect);
+ annot.put(PdfName.SUBTYPE, PdfName.POPUP);
+ if (contents != null)
+ annot.put(PdfName.CONTENTS, new PdfString(contents, PdfObject.TEXT_UNICODE));
+ if (open)
+ annot.put(PdfName.OPEN, PdfBoolean.PDFTRUE);
+ return annot;
+ }
+
+ public void setDefaultAppearanceString(PdfContentByte cb) {
+ byte b[] = cb.getInternalBuffer().toByteArray();
+ int len = b.length;
+ for (int k = 0; k < len; ++k) {
+ if (b[k] == '\n')
+ b[k] = 32;
+ }
+ put(PdfName.DA, new PdfString(b));
+ }
+
+ public void setFlags(int flags) {
+ if (flags == 0)
+ remove(PdfName.F);
+ else
+ put(PdfName.F, new PdfNumber(flags));
+ }
+
+ public void setBorder(PdfBorderArray border) {
+ putDel(PdfName.BORDER, border);
+ }
+
+ public void setBorderStyle(PdfBorderDictionary border) {
+ putDel(PdfName.BS, border);
+ }
+
+ /**
+ * Sets the annotation's highlighting mode. The values can be
+ * HIGHLIGHT_NONE
, HIGHLIGHT_INVERT
,
+ * HIGHLIGHT_OUTLINE
and HIGHLIGHT_PUSH
;
+ * @param highlight the annotation's highlighting mode
+ */
+ public void setHighlighting(PdfName highlight) {
+ if (highlight.equals(HIGHLIGHT_INVERT))
+ remove(PdfName.H);
+ else
+ put(PdfName.H, highlight);
+ }
+
+ public void setAppearance(PdfName ap, PdfTemplate template) {
+ PdfDictionary dic = (PdfDictionary)get(PdfName.AP);
+ if (dic == null)
+ dic = new PdfDictionary();
+ dic.put(ap, template.getIndirectReference());
+ put(PdfName.AP, dic);
+ if (!form)
+ return;
+ if (templates == null)
+ templates = new HashMap();
+ templates.put(template, null);
+ }
+
+ public void setAppearance(PdfName ap, String state, PdfTemplate template) {
+ PdfDictionary dicAp = (PdfDictionary)get(PdfName.AP);
+ if (dicAp == null)
+ dicAp = new PdfDictionary();
+
+ PdfDictionary dic;
+ PdfObject obj = dicAp.get(ap);
+ if (obj != null && obj.isDictionary())
+ dic = (PdfDictionary)obj;
+ else
+ dic = new PdfDictionary();
+ dic.put(new PdfName(state), template.getIndirectReference());
+ dicAp.put(ap, dic);
+ put(PdfName.AP, dicAp);
+ if (!form)
+ return;
+ if (templates == null)
+ templates = new HashMap();
+ templates.put(template, null);
+ }
+
+ public void setAppearanceState(String state) {
+ if (state == null) {
+ remove(PdfName.AS);
+ return;
+ }
+ put(PdfName.AS, new PdfName(state));
+ }
+
+ public void setColor(Color color) {
+ putDel(PdfName.C, new PdfColor(color));
+ }
+
+ public void setTitle(String title) {
+ if (title == null) {
+ remove(PdfName.T);
+ return;
+ }
+ put(PdfName.T, new PdfString(title, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setPopup(PdfAnnotation popup) {
+ put(PdfName.POPUP, popup.getIndirectReference());
+ popup.put(PdfName.PARENT, getIndirectReference());
+ }
+
+ public void setAction(PdfAction action) {
+ putDel(PdfName.A, action);
+ }
+
+ public void setAdditionalActions(PdfName key, PdfAction action) {
+ PdfDictionary dic;
+ PdfObject obj = get(PdfName.AA);
+ if (obj != null && obj.isDictionary())
+ dic = (PdfDictionary)obj;
+ else
+ dic = new PdfDictionary();
+ dic.put(key, action);
+ put(PdfName.AA, dic);
+ }
+
+ /** Getter for property used.
+ * @return Value of property used.
+ */
+ public boolean isUsed() {
+ return used;
+ }
+
+ /** Setter for property used.
+ */
+ void setUsed() {
+ used = true;
+ }
+
+ HashMap getTemplates() {
+ return templates;
+ }
+
+ /** Getter for property form.
+ * @return Value of property form.
+ */
+ public boolean isForm() {
+ return form;
+ }
+
+ /** Getter for property annotation.
+ * @return Value of property annotation.
+ */
+ public boolean isAnnotation() {
+ return annotation;
+ }
+
+ public void setPage(int page) {
+ put(PdfName.P, writer.getPageReference(page));
+ }
+
+ public void setPage() {
+ put(PdfName.P, writer.getCurrentPage());
+ }
+
+ /** Getter for property placeInPage.
+ * @return Value of property placeInPage.
+ */
+ public int getPlaceInPage() {
+ return placeInPage;
+ }
+
+ /** Places the annotation in a specified page that must be greater
+ * or equal to the current one. With PdfStamper
the page
+ * can be any. The first page is 1.
+ * @param placeInPage New value of property placeInPage.
+ */
+ public void setPlaceInPage(int placeInPage) {
+ this.placeInPage = placeInPage;
+ }
+
+ public void setRotate(int v) {
+ put(PdfName.ROTATE, new PdfNumber(v));
+ }
+
+ PdfDictionary getMK() {
+ PdfDictionary mk = (PdfDictionary)get(PdfName.MK);
+ if (mk == null) {
+ mk = new PdfDictionary();
+ put(PdfName.MK, mk);
+ }
+ return mk;
+ }
+
+ public void setMKRotation(int rotation) {
+ getMK().put(PdfName.R, new PdfNumber(rotation));
+ }
+
+ public static PdfArray getMKColor(Color color) {
+ PdfArray array = new PdfArray();
+ int type = ExtendedColor.getType(color);
+ switch (type) {
+ case ExtendedColor.TYPE_GRAY: {
+ array.add(new PdfNumber(((GrayColor)color).getGray()));
+ break;
+ }
+ case ExtendedColor.TYPE_CMYK: {
+ CMYKColor cmyk = (CMYKColor)color;
+ array.add(new PdfNumber(cmyk.getCyan()));
+ array.add(new PdfNumber(cmyk.getMagenta()));
+ array.add(new PdfNumber(cmyk.getYellow()));
+ array.add(new PdfNumber(cmyk.getBlack()));
+ break;
+ }
+ case ExtendedColor.TYPE_SEPARATION:
+ case ExtendedColor.TYPE_PATTERN:
+ case ExtendedColor.TYPE_SHADING:
+ throw new RuntimeException("Separations, patterns and shadings are not allowed in MK dictionary.");
+ default:
+ array.add(new PdfNumber(color.getRed() / 255f));
+ array.add(new PdfNumber(color.getGreen() / 255f));
+ array.add(new PdfNumber(color.getBlue() / 255f));
+ }
+ return array;
+ }
+
+ public void setMKBorderColor(Color color) {
+ if (color == null)
+ getMK().remove(PdfName.BC);
+ else
+ getMK().put(PdfName.BC, getMKColor(color));
+ }
+
+ public void setMKBackgroundColor(Color color) {
+ if (color == null)
+ getMK().remove(PdfName.BG);
+ else
+ getMK().put(PdfName.BG, getMKColor(color));
+ }
+
+ public void setMKNormalCaption(String caption) {
+ getMK().put(PdfName.CA, new PdfString(caption, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setMKRolloverCaption(String caption) {
+ getMK().put(PdfName.RC, new PdfString(caption, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setMKAlternateCaption(String caption) {
+ getMK().put(PdfName.AC, new PdfString(caption, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setMKNormalIcon(PdfTemplate template) {
+ getMK().put(PdfName.I, template.getIndirectReference());
+ }
+
+ public void setMKRolloverIcon(PdfTemplate template) {
+ getMK().put(PdfName.RI, template.getIndirectReference());
+ }
+
+ public void setMKAlternateIcon(PdfTemplate template) {
+ getMK().put(PdfName.IX, template.getIndirectReference());
+ }
+
+ public void setMKIconFit(PdfName scale, PdfName scalingType, float leftoverLeft, float leftoverBottom, boolean fitInBounds) {
+ PdfDictionary dic = new PdfDictionary();
+ if (!scale.equals(PdfName.A))
+ dic.put(PdfName.SW, scale);
+ if (!scalingType.equals(PdfName.P))
+ dic.put(PdfName.S, scalingType);
+ if (leftoverLeft != 0.5f || leftoverBottom != 0.5f) {
+ PdfArray array = new PdfArray(new PdfNumber(leftoverLeft));
+ array.add(new PdfNumber(leftoverBottom));
+ dic.put(PdfName.A, array);
+ }
+ if (fitInBounds)
+ dic.put(PdfName.FB, PdfBoolean.PDFTRUE);
+ getMK().put(PdfName.IF, dic);
+ }
+
+ public void setMKTextPosition(int tp) {
+ getMK().put(PdfName.TP, new PdfNumber(tp));
+ }
+
+ /**
+ * Sets the layer this annotation belongs to.
+ * @param layer the layer this annotation belongs to
+ */
+ public void setLayer(PdfOCG layer) {
+ put(PdfName.OC, layer.getRef());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfAppearance.java b/src/main/java/com/lowagie/text/pdf/PdfAppearance.java
new file mode 100644
index 0000000..2189762
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfAppearance.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import com.lowagie.text.Rectangle;
+import java.util.HashMap;
+
+/**
+ * Implements the appearance stream to be used with form fields..
+ */
+
+public class PdfAppearance extends PdfTemplate {
+
+ public static final HashMap stdFieldFontNames = new HashMap();
+ static {
+ stdFieldFontNames.put("Courier-BoldOblique", new PdfName("CoBO"));
+ stdFieldFontNames.put("Courier-Bold", new PdfName("CoBo"));
+ stdFieldFontNames.put("Courier-Oblique", new PdfName("CoOb"));
+ stdFieldFontNames.put("Courier", new PdfName("Cour"));
+ stdFieldFontNames.put("Helvetica-BoldOblique", new PdfName("HeBO"));
+ stdFieldFontNames.put("Helvetica-Bold", new PdfName("HeBo"));
+ stdFieldFontNames.put("Helvetica-Oblique", new PdfName("HeOb"));
+ stdFieldFontNames.put("Helvetica", new PdfName("Helv"));
+ stdFieldFontNames.put("Symbol", new PdfName("Symb"));
+ stdFieldFontNames.put("Times-BoldItalic", new PdfName("TiBI"));
+ stdFieldFontNames.put("Times-Bold", new PdfName("TiBo"));
+ stdFieldFontNames.put("Times-Italic", new PdfName("TiIt"));
+ stdFieldFontNames.put("Times-Roman", new PdfName("TiRo"));
+ stdFieldFontNames.put("ZapfDingbats", new PdfName("ZaDb"));
+ stdFieldFontNames.put("HYSMyeongJo-Medium", new PdfName("HySm"));
+ stdFieldFontNames.put("HYGoThic-Medium", new PdfName("HyGo"));
+ stdFieldFontNames.put("HeiseiKakuGo-W5", new PdfName("KaGo"));
+ stdFieldFontNames.put("HeiseiMin-W3", new PdfName("KaMi"));
+ stdFieldFontNames.put("MHei-Medium", new PdfName("MHei"));
+ stdFieldFontNames.put("MSung-Light", new PdfName("MSun"));
+ stdFieldFontNames.put("STSong-Light", new PdfName("STSo"));
+ stdFieldFontNames.put("MSungStd-Light", new PdfName("MSun"));
+ stdFieldFontNames.put("STSongStd-Light", new PdfName("STSo"));
+ stdFieldFontNames.put("HYSMyeongJoStd-Medium", new PdfName("HySm"));
+ stdFieldFontNames.put("KozMinPro-Regular", new PdfName("KaMi"));
+ }
+
+ /**
+ *Creates a PdfAppearance
.
+ */
+
+ PdfAppearance() {
+ super();
+ separator = ' ';
+ }
+
+ PdfAppearance(PdfIndirectReference iref) {
+ thisReference = iref;
+ }
+
+ /**
+ * Creates new PdfTemplate
+ *
+ * @param wr the PdfWriter
+ */
+
+ PdfAppearance(PdfWriter wr) {
+ super(wr);
+ separator = ' ';
+ }
+
+ /**
+ * Set the font and the size for the subsequent text writing.
+ *
+ * @param bf the font
+ * @param size the font size in points
+ */
+ public void setFontAndSize(BaseFont bf, float size) {
+ checkWriter();
+ state.size = size;
+ if (bf.getFontType() == BaseFont.FONT_TYPE_DOCUMENT) {
+ state.fontDetails = new FontDetails(null, ((DocumentFont)bf).getIndirectReference(), bf);
+ }
+ else
+ state.fontDetails = writer.addSimple(bf);
+ PdfName psn = (PdfName)stdFieldFontNames.get(bf.getPostscriptFontName());
+ if (psn == null) {
+ if (bf.isSubset() && bf.getFontType() == BaseFont.FONT_TYPE_TTUNI)
+ psn = state.fontDetails.getFontName();
+ else {
+ psn = new PdfName(bf.getPostscriptFontName());
+ state.fontDetails.setSubset(false);
+ }
+ }
+ PageResources prs = getPageResources();
+// PdfName name = state.fontDetails.getFontName();
+ prs.addFont(psn, state.fontDetails.getIndirectReference());
+ content.append(psn.getBytes()).append(' ').append(size).append(" Tf").append_i(separator);
+ }
+
+ public PdfContentByte getDuplicate() {
+ PdfAppearance tpl = new PdfAppearance();
+ tpl.writer = writer;
+ tpl.pdf = pdf;
+ tpl.thisReference = thisReference;
+ tpl.pageResources = pageResources;
+ tpl.bBox = new Rectangle(bBox);
+ tpl.group = group;
+ tpl.layer = layer;
+ if (matrix != null) {
+ tpl.matrix = new PdfArray(matrix);
+ }
+ tpl.separator = separator;
+ return tpl;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfArray.java b/src/main/java/com/lowagie/text/pdf/PdfArray.java
new file mode 100644
index 0000000..30be4ef
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfArray.java
@@ -0,0 +1,227 @@
+/*
+ * $Id: PdfArray.java,v 1.62 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.ListIterator;
+
+/**
+ * PdfArray
is the PDF Array object.
+ *
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 4.6 (page 40).
+ *
+ * @see PdfObject
+ */
+
+public class PdfArray extends PdfObject {
+
+ // membervariables
+
+/** this is the actual array of PdfObjects */
+ protected ArrayList arrayList;
+
+ // constructors
+
+/**
+ * Constructs an empty PdfArray
-object.
+ */
+
+ public PdfArray() {
+ super(ARRAY);
+ arrayList = new ArrayList();
+ }
+
+/**
+ * Constructs an PdfArray
-object, containing 1 PdfObject
.
+ *
+ * @param object a PdfObject
that has to be added to the array
+ */
+
+ public PdfArray(PdfObject object) {
+ super(ARRAY);
+ arrayList = new ArrayList();
+ arrayList.add(object);
+ }
+
+ public PdfArray(float values[]) {
+ super(ARRAY);
+ arrayList = new ArrayList();
+ add(values);
+ }
+
+ public PdfArray(int values[]) {
+ super(ARRAY);
+ arrayList = new ArrayList();
+ add(values);
+ }
+
+/**
+ * Constructs an PdfArray
-object, containing all the PdfObject
s in a given PdfArray
.
+ *
+ * @param array a PdfArray
that has to be added to the array
+ */
+
+ public PdfArray(PdfArray array) {
+ super(ARRAY);
+ arrayList = new ArrayList(array.getArrayList());
+ }
+
+ // methods overriding some methods in PdfObject
+
+/**
+ * Returns the PDF representation of this PdfArray
.
+ *
+ * @return an array of byte
s
+ */
+
+ public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
+ os.write('[');
+
+ Iterator i = arrayList.iterator();
+ PdfObject object;
+ int type = 0;
+ if (i.hasNext()) {
+ object = (PdfObject) i.next();
+ object.toPdf(writer, os);
+ }
+ while (i.hasNext()) {
+ object = (PdfObject) i.next();
+ type = object.type();
+ if (type != PdfObject.ARRAY && type != PdfObject.DICTIONARY && type != PdfObject.NAME && type != PdfObject.STRING)
+ os.write(' ');
+ object.toPdf(writer, os);
+ }
+ os.write(']');
+ }
+
+ // methods concerning the ArrayList-membervalue
+
+/**
+ * Returns an ArrayList containing PdfObject
s.
+ *
+ * @return an ArrayList
+ */
+
+ public ArrayList getArrayList() {
+ return arrayList;
+ }
+
+/**
+ * Returns the number of entries in the array.
+ *
+ * @return the size of the ArrayList
+ */
+
+ public int size() {
+ return arrayList.size();
+ }
+
+/**
+ * Adds a PdfObject
to the PdfArray
.
+ *
+ * @param object PdfObject
to add
+ * @return true
+ */
+
+ public boolean add(PdfObject object) {
+ return arrayList.add(object);
+ }
+
+ public boolean add(float values[]) {
+ for (int k = 0; k < values.length; ++k)
+ arrayList.add(new PdfNumber(values[k]));
+ return true;
+ }
+
+ public boolean add(int values[]) {
+ for (int k = 0; k < values.length; ++k)
+ arrayList.add(new PdfNumber(values[k]));
+ return true;
+ }
+
+/**
+ * Adds a PdfObject
to the PdfArray
.
+ * ArrayList
.
+ *
+ * @param object PdfObject
to add
+ */
+
+ public void addFirst(PdfObject object) {
+ arrayList.add(0, object);
+ }
+
+/**
+ * Checks if the PdfArray
already contains a certain PdfObject
.
+ *
+ * @param object PdfObject
to check
+ * @return true
+ */
+
+ public boolean contains(PdfObject object) {
+ return arrayList.contains(object);
+ }
+
+ public ListIterator listIterator() {
+ return arrayList.listIterator();
+ }
+
+ public String toString() {
+ return arrayList.toString();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfBoolean.java b/src/main/java/com/lowagie/text/pdf/PdfBoolean.java
new file mode 100644
index 0000000..1bd2fc8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfBoolean.java
@@ -0,0 +1,134 @@
+/*
+ * $Id: PdfBoolean.java,v 1.55 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * PdfBoolean
is the boolean object represented by the keywords true or false.
+ * PdfBoolean
*/
+ public static final String TRUE = "true";
+
+/** A possible value of PdfBoolean
*/
+ public static final String FALSE = "false";
+
+ // membervariables
+
+/** the boolean value of this object */
+ private boolean value;
+
+ // constructors
+
+/**
+ * Constructs a PdfBoolean
-object.
+ *
+ * @param value the value of the new PdfObject
+ */
+
+ public PdfBoolean(boolean value) {
+ super(BOOLEAN);
+ if (value) {
+ setContent(TRUE);
+ }
+ else {
+ setContent(FALSE);
+ }
+ this.value = value;
+ }
+
+/**
+ * Constructs a PdfBoolean
-object.
+ *
+ * @param value the value of the new PdfObject
, represented as a String
+ *
+ * @throws BadPdfFormatException thrown if the value isn't 'true
' or 'false
'
+ */
+
+ public PdfBoolean(String value) throws BadPdfFormatException {
+ super(BOOLEAN, value);
+ if (value.equals(TRUE)) {
+ this.value = true;
+ }
+ else if (value.equals(FALSE)) {
+ this.value = false;
+ }
+ else {
+ throw new BadPdfFormatException("The value has to be 'true' of 'false', instead of '" + value + "'.");
+ }
+ }
+
+ // methods returning the value of this object
+
+/**
+ * Returns the primitive value of the PdfBoolean
-object.
+ *
+ * @return the actual value of the object.
+ */
+
+ public boolean booleanValue() {
+ return value;
+ }
+
+ public String toString() {
+ return String.valueOf(value);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfBorderArray.java b/src/main/java/com/lowagie/text/pdf/PdfBorderArray.java
new file mode 100644
index 0000000..170bb4c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfBorderArray.java
@@ -0,0 +1,82 @@
+/*
+ * $Id: PdfBorderArray.java,v 1.55 2005/05/04 14:32:24 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * A PdfBorderArray
defines the border of a PdfAnnotation
.
+ *
+ * @see PdfArray
+ */
+
+public class PdfBorderArray extends PdfArray {
+
+ // constructors
+
+/**
+ * Constructs a new PdfBorderArray
.
+ */
+
+ public PdfBorderArray(float hRadius, float vRadius, float width) {
+ this(hRadius, vRadius, width, null);
+ }
+
+/**
+ * Constructs a new PdfBorderArray
.
+ */
+
+ public PdfBorderArray(float hRadius, float vRadius, float width, PdfDashPattern dash) {
+ super(new PdfNumber(hRadius));
+ add(new PdfNumber(vRadius));
+ add(new PdfNumber(width));
+ if (dash != null)
+ add(dash);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfBorderDictionary.java b/src/main/java/com/lowagie/text/pdf/PdfBorderDictionary.java
new file mode 100644
index 0000000..687b9b0
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfBorderDictionary.java
@@ -0,0 +1,100 @@
+/*
+ * $Id: PdfBorderDictionary.java,v 1.54 2005/05/04 14:31:49 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * A PdfBorderDictionary
define the appearance of a Border (Annotations).
+ *
+ * @see PdfDictionary
+ */
+
+public class PdfBorderDictionary extends PdfDictionary {
+
+ public static final int STYLE_SOLID = 0;
+ public static final int STYLE_DASHED = 1;
+ public static final int STYLE_BEVELED = 2;
+ public static final int STYLE_INSET = 3;
+ public static final int STYLE_UNDERLINE = 4;
+ // constructors
+
+/**
+ * Constructs a PdfBorderDictionary
.
+ */
+
+ public PdfBorderDictionary(float borderWidth, int borderStyle, PdfDashPattern dashes) {
+ put(PdfName.W, new PdfNumber(borderWidth));
+ switch (borderStyle) {
+ case STYLE_SOLID:
+ put(PdfName.S, PdfName.S);
+ break;
+ case STYLE_DASHED:
+ if (dashes != null)
+ put(PdfName.D, dashes);
+ put(PdfName.S, PdfName.D);
+ break;
+ case STYLE_BEVELED:
+ put(PdfName.S, PdfName.B);
+ break;
+ case STYLE_INSET:
+ put(PdfName.S, PdfName.I);
+ break;
+ case STYLE_UNDERLINE:
+ put(PdfName.S, PdfName.U);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid border style.");
+ }
+ }
+
+ public PdfBorderDictionary(float borderWidth, int borderStyle) {
+ this(borderWidth, borderStyle, null);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfCell.java b/src/main/java/com/lowagie/text/pdf/PdfCell.java
new file mode 100644
index 0000000..909b884
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfCell.java
@@ -0,0 +1,890 @@
+/*
+ * $Id: PdfCell.java,v 1.98 2005/11/01 12:27:04 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import com.lowagie.text.*;
+
+/**
+ * A PdfCell
is the PDF translation of a Cell
.
+ * PdfCell
is an ArrayList
of PdfLine
s.
+ * PdfTable
+ */
+ private boolean header = false;
+
+ /**
+ * This is the total height of the content of the cell. Note that the actual cell
+ * height may be larger due to another cell on the row *
+ */
+ private float contentHeight = 0.0f;
+
+ /**
+ * Indicates that the largest ascender height should be used to
+ * determine the height of the first line. Setting this to true can help
+ * with vertical alignment problems. */
+ private boolean useAscender;
+
+ /**
+ * Indicates that the largest descender height should be added to the height of
+ * the last line (so characters like y don't dip into the border). */
+ private boolean useDescender;
+
+ /**
+ * Adjusts the cell contents to compensate for border widths.
+ */
+ private boolean useBorderPadding;
+
+ private int verticalAlignment;
+
+ private PdfLine firstLine;
+ private PdfLine lastLine;
+
+ // constructors
+
+ /**
+ * Constructs a PdfCell
-object.
+ *
+ * @param cell the original Cell
+ * @param rownumber the number of the Row
the Cell
was in.
+ * @param left the left border of the PdfCell
+ * @param right the right border of the PdfCell
+ * @param top the top border of the PdfCell
+ * @param cellspacing the cellspacing of the Table
+ * @param cellpadding the cellpadding of the Table
+ */
+
+ public PdfCell(Cell cell, int rownumber, float left, float right, float top, float cellspacing, float cellpadding) {
+ // constructs a Rectangle (the bottomvalue will be changed afterwards)
+ super(left, top, right, top);
+ // copying the other Rectangle attributes from class Cell
+ cloneNonPositionParameters(cell);
+ this.cellpadding = cellpadding;
+ this.cellspacing = cellspacing;
+ this.verticalAlignment = cell.verticalAlignment();
+ this.useAscender = cell.isUseAscender();
+ this.useDescender = cell.isUseDescender();
+ this.useBorderPadding = cell.isUseBorderPadding();
+
+ // initialisation of some parameters
+ PdfChunk chunk;
+ Element element;
+ PdfChunk overflow;
+ lines = new ArrayList();
+ images = new ArrayList();
+ leading = cell.leading();
+ int alignment = cell.horizontalAlignment();
+ left += cellspacing + cellpadding;
+ right -= cellspacing + cellpadding;
+
+ left += getBorderWidthInside(LEFT);
+ right -= getBorderWidthInside(RIGHT);
+
+
+ contentHeight = 0;
+
+ rowspan = cell.rowspan();
+
+ ArrayList allActions;
+ int aCounter;
+ // we loop over all the elements of the cell
+ for (Iterator i = cell.getElements(); i.hasNext();) {
+ element = (Element) i.next();
+ switch (element.type()) {
+ case Element.JPEG:
+ case Element.IMGRAW:
+ case Element.IMGTEMPLATE:
+ addImage((Image) element, left, right, 0.4f * leading, alignment); //
+ break;
+ // if the element is a list
+ case Element.LIST:
+ if (line != null && line.size() > 0) {
+ line.resetAlignment();
+ addLine(line);
+ }
+ allActions = new ArrayList();
+ processActions(element, null, allActions);
+ aCounter = 0;
+ ListItem item;
+ // we loop over all the listitems
+ for (Iterator items = ((List) element).getItems().iterator(); items.hasNext();) {
+ item = (ListItem) items.next();
+ line = new PdfLine(left + item.indentationLeft(), right, alignment, item.leading());
+ line.setListItem(item);
+ for (Iterator j = item.getChunks().iterator(); j.hasNext();) {
+ chunk = new PdfChunk((Chunk) j.next(), (PdfAction) (allActions.get(aCounter++)));
+ while ((overflow = line.add(chunk)) != null) {
+ addLine(line);
+ line = new PdfLine(left + item.indentationLeft(), right, alignment, item.leading());
+ chunk = overflow;
+ }
+ line.resetAlignment();
+ addLine(line);
+ line = new PdfLine(left + item.indentationLeft(), right, alignment, leading);
+ }
+ }
+ line = new PdfLine(left, right, alignment, leading);
+ break;
+ // if the element is something else
+ default:
+ allActions = new ArrayList();
+ processActions(element, null, allActions);
+ aCounter = 0;
+
+ float currentLineLeading = leading;
+ float currentLeft = left;
+ float currentRight = right;
+ if (element instanceof Phrase) {
+ currentLineLeading = ((Phrase) element).leading();
+ }
+ if (element instanceof Paragraph) {
+ Paragraph p = (Paragraph) element;
+ currentLeft += p.indentationLeft();
+ currentRight -= p.indentationRight();
+ }
+ if (line == null) {
+ line = new PdfLine(currentLeft, currentRight, alignment, currentLineLeading);
+ }
+ // we loop over the chunks
+ ArrayList chunks = element.getChunks();
+ if (chunks.isEmpty()) {
+ addLine(line); // add empty line - all cells need some lines even if they are empty
+ line = new PdfLine(currentLeft, currentRight, alignment, currentLineLeading);
+ }
+ else {
+ for (Iterator j = chunks.iterator(); j.hasNext();) {
+ Chunk c = (Chunk) j.next();
+ chunk = new PdfChunk(c, (PdfAction) (allActions.get(aCounter++)));
+ while ((overflow = line.add(chunk)) != null) {
+ addLine(line);
+ line = new PdfLine(currentLeft, currentRight, alignment, currentLineLeading);
+ chunk = overflow;
+ }
+ }
+ }
+ // if the element is a paragraph, section or chapter, we reset the alignment and add the line
+ switch (element.type()) {
+ case Element.PARAGRAPH:
+ case Element.SECTION:
+ case Element.CHAPTER:
+ line.resetAlignment();
+ flushCurrentLine();
+ }
+ }
+ }
+ flushCurrentLine();
+ if (lines.size() > cell.getMaxLines()) {
+ while (lines.size() > cell.getMaxLines()) {
+ removeLine(lines.size() - 1);
+ }
+ if (cell.getMaxLines() > 0) {
+ String more = cell.getShowTruncation();
+ if (more != null && more.length() > 0) {
+ // Denote that the content has been truncated
+ lastLine = (PdfLine) lines.get(lines.size() - 1);
+ if (lastLine.size() >= 0) {
+ PdfChunk lastChunk = lastLine.getChunk(lastLine.size() - 1);
+ float moreWidth = new PdfChunk(more, lastChunk).width();
+ while (lastChunk.toString().length() > 0 && lastChunk.width() + moreWidth > right - left) {
+ // Remove characters to leave room for the 'more' indicator
+ lastChunk.setValue(lastChunk.toString().substring(0, lastChunk.length() - 1));
+ }
+ lastChunk.setValue(lastChunk.toString() + more);
+ } else {
+ lastLine.add(new PdfChunk(new Chunk(more), null));
+ }
+ }
+ }
+ }
+ // we set some additional parameters
+ if (useDescender && lastLine != null) {
+ contentHeight -= lastLine.getDescender();
+ }
+
+ // adjust first line height so that it touches the top
+ if (lines.size() > 0) {
+ firstLine = (PdfLine) lines.get(0);
+ float firstLineRealHeight = firstLineRealHeight();
+ contentHeight -= firstLine.height();
+ firstLine.height = firstLineRealHeight;
+ contentHeight += firstLineRealHeight;
+ }
+
+ float newBottom = top - contentHeight - (2f * cellpadding()) - (2f * cellspacing());
+ newBottom -= getBorderWidthInside(TOP) + getBorderWidthInside(BOTTOM);
+ setBottom(newBottom);
+
+ this.rownumber = rownumber;
+ }
+
+
+
+
+ // overriding of the Rectangle methods
+
+
+ /**
+ * Sets the bottom of the Rectangle and determines the proper {link #verticalOffset}
+ * to appropriately align the contents vertically.
+ * @param value
+ */
+ public void setBottom(float value) {
+ super.setBottom(value);
+ float firstLineRealHeight = firstLineRealHeight();
+
+ float totalHeight = ury - value; // can't use top (already compensates for cellspacing)
+ float nonContentHeight = (cellpadding() * 2f) + (cellspacing() * 2f);
+ nonContentHeight += getBorderWidthInside(TOP) + getBorderWidthInside(BOTTOM);
+
+ float interiorHeight = totalHeight - nonContentHeight;
+ float extraHeight = 0.0f;
+
+ switch (verticalAlignment) {
+ case Element.ALIGN_BOTTOM:
+ extraHeight = interiorHeight - contentHeight;
+ break;
+ case Element.ALIGN_MIDDLE:
+ extraHeight = (interiorHeight - contentHeight) / 2.0f;
+ break;
+ default: // ALIGN_TOP
+ extraHeight = 0f;
+ }
+
+ extraHeight += cellpadding() + cellspacing();
+ extraHeight += getBorderWidthInside(TOP);
+ if (firstLine != null) {
+ firstLine.height = firstLineRealHeight + extraHeight;
+ }
+ }
+
+ /**
+ * Returns the lower left x-coordinaat.
+ *
+ * @return the lower left x-coordinaat
+ */
+
+ public float left() {
+ return super.left(cellspacing);
+ }
+
+ /**
+ * Returns the upper right x-coordinate.
+ *
+ * @return the upper right x-coordinate
+ */
+
+ public float right() {
+ return super.right(cellspacing);
+ }
+
+ /**
+ * Returns the upper right y-coordinate.
+ *
+ * @return the upper right y-coordinate
+ */
+
+ public float top() {
+ return super.top(cellspacing);
+ }
+
+ /**
+ * Returns the lower left y-coordinate.
+ *
+ * @return the lower left y-coordinate
+ */
+
+ public float bottom() {
+ return super.bottom(cellspacing);
+ }
+
+ // methods
+
+ private void addLine(PdfLine line) {
+ lines.add(line);
+ contentHeight += line.height();
+ lastLine = line;
+ this.line = null;
+ }
+
+ private PdfLine removeLine(int index) {
+ PdfLine oldLine = (PdfLine) lines.remove(index);
+ contentHeight -= oldLine.height();
+ if (index == 0) {
+ if (lines.size() > 0) {
+ firstLine = (PdfLine) lines.get(0);
+ float firstLineRealHeight = firstLineRealHeight();
+ contentHeight -= firstLine.height();
+ firstLine.height = firstLineRealHeight;
+ contentHeight += firstLineRealHeight;
+ }
+ }
+ return oldLine;
+ }
+
+ private void flushCurrentLine() {
+ if (line != null && line.size() > 0) {
+ addLine(line);
+ }
+ }
+
+ /**
+ * Calculates what the height of the first line should be so that the content will be
+ * flush with the top. For text, this is the height of the ascender. For an image,
+ * it is the actual height of the image.
+ * @return the real height of the first line
+ */
+ private float firstLineRealHeight() {
+ float firstLineRealHeight = 0f;
+ if (firstLine != null) {
+ PdfChunk chunk = firstLine.getChunk(0);
+ if (chunk != null) {
+ Image image = chunk.getImage();
+ if (image != null) {
+ firstLineRealHeight = firstLine.getChunk(0).getImage().scaledHeight();
+ } else {
+ firstLineRealHeight = useAscender ? firstLine.getAscender() : leading;
+ }
+ }
+ }
+ return firstLineRealHeight;
+ }
+
+ /**
+ * Gets the amount of the border for the specified side that is inside the Rectangle.
+ * For non-variable width borders this is only 1/2 the border width on that side. This
+ * always returns 0 if {@link #useBorderPadding} is false;
+ * @param side the side to check. One of the side constants in {@link com.lowagie.text.Rectangle}
+ * @return the borderwidth inside the cell
+ */
+ private float getBorderWidthInside(int side) {
+ float width = 0f;
+ if (useBorderPadding) {
+ switch (side) {
+ case Rectangle.LEFT:
+ width = getBorderWidthLeft();
+ break;
+
+ case Rectangle.RIGHT:
+ width = getBorderWidthRight();
+ break;
+
+ case Rectangle.TOP:
+ width = getBorderWidthTop();
+ break;
+
+ default: // default and BOTTOM
+ width = getBorderWidthBottom();
+ break;
+ }
+ // non-variable (original style) borders overlap the rectangle (only 1/2 counts)
+ if (!isUseVariableBorders()) {
+ width = width / 2f;
+ }
+ }
+ return width;
+ }
+
+
+ /**
+ * Adds an image to this Cell.
+ *
+ * @param i the image to add
+ * @param left the left border
+ * @param right the right border
+ * @param extraHeight extra height to add above image
+ * @param alignment horizontal alignment (constant from Element class)
+ * @return the height of the image
+ */
+
+ private float addImage(Image i, float left, float right, float extraHeight, int alignment) {
+ Image image = Image.getInstance(i);
+ if (image.scaledWidth() > right - left) {
+ image.scaleToFit(right - left, Float.MAX_VALUE);
+ }
+ flushCurrentLine();
+ if (line == null) {
+ line = new PdfLine(left, right, alignment, leading);
+ }
+ PdfLine imageLine = line;
+
+ // left and right in chunk is relative to the start of the line
+ right = right - left;
+ left = 0f;
+
+ if ((image.alignment() & Image.RIGHT) == Image.RIGHT) { // fix Uwe Zimmerman
+ left = right - image.scaledWidth();
+ } else if ((image.alignment() & Image.MIDDLE) == Image.MIDDLE) {
+ left = left + ((right - left - image.scaledWidth()) / 2f);
+ }
+ Chunk imageChunk = new Chunk(image, left, 0);
+ imageLine.add(new PdfChunk(imageChunk, null));
+ addLine(imageLine);
+ return imageLine.height();
+ }
+
+ /**
+ * Gets the lines of a cell that can be drawn between certain limits.
+ * ArrayList
of PdfLine
s
+ */
+
+ public ArrayList getLines(float top, float bottom) {
+ float lineHeight;
+ float currentPosition = Math.min(top(), top);
+ setTop(currentPosition + cellspacing);
+ ArrayList result = new ArrayList();
+
+ // if the bottom of the page is higher than the top of the cell: do nothing
+ if (top() < bottom) {
+ return result;
+ }
+
+ // we loop over the lines
+ int size = lines.size();
+ boolean aboveBottom = true;
+ for (int i = 0; i < size && aboveBottom; i++) {
+ line = (PdfLine) lines.get(i);
+ lineHeight = line.height();
+ currentPosition -= lineHeight;
+ // if the currentPosition is higher than the bottom, we add the line to the result
+ if (currentPosition > (bottom + cellpadding + getBorderWidthInside(BOTTOM))) { // bugfix by Tom Ring and Veerendra Namineni
+ result.add(line);
+ } else {
+ aboveBottom = false;
+ }
+ }
+ // if the bottom of the cell is higher than the bottom of the page, the cell is written, so we can remove all lines
+ float difference = 0f;
+ if (!header) {
+ if (aboveBottom) {
+ lines = new ArrayList();
+ contentHeight = 0f;
+ } else {
+ size = result.size();
+ for (int i = 0; i < size; i++) {
+ line = removeLine(0);
+ difference += line.height();
+ }
+ }
+ }
+ if (difference > 0) {
+ Image image;
+ for (Iterator i = images.iterator(); i.hasNext();) {
+ image = (Image) i.next();
+ image.setAbsolutePosition(image.absoluteX(), image.absoluteY() - difference - leading);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Gets the images of a cell that can be drawn between certain limits.
+ * ArrayList
of Image
s
+ */
+
+ public ArrayList getImages(float top, float bottom) {
+
+ // if the bottom of the page is higher than the top of the cell: do nothing
+ if (top() < bottom) {
+ return new ArrayList();
+ }
+ top = Math.min(top(), top);
+ // initialisations
+ Image image;
+ float height;
+ ArrayList result = new ArrayList();
+ // we loop over the images
+ for (Iterator i = images.iterator(); i.hasNext() && !header;) {
+ image = (Image) i.next();
+ height = image.absoluteY();
+ // if the currentPosition is higher than the bottom, we add the line to the result
+ if (top - height > (bottom + cellpadding)) {
+ image.setAbsolutePosition(image.absoluteX(), top - height);
+ result.add(image);
+ i.remove();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if this cell belongs to the header of a PdfTable
.
+ *
+ * @return void
+ */
+
+ boolean isHeader() {
+ return header;
+ }
+
+ /**
+ * Indicates that this cell belongs to the header of a PdfTable
.
+ */
+
+ void setHeader() {
+ header = true;
+ }
+
+ /**
+ * Checks if the cell may be removed.
+ * true
if all the lines are allready drawn; false
otherwise.
+ */
+
+ boolean mayBeRemoved() {
+ return (header || (lines.size() == 0 && images.size() == 0));
+ }
+
+ /**
+ * Returns the number of lines in the cell.
+ *
+ * @return a value
+ */
+
+ public int size() {
+ return lines.size();
+ }
+
+ /**
+ * Returns the number of lines in the cell that are not empty.
+ *
+ * @return a value
+ */
+
+ public int remainingLines() {
+ if (lines.size() == 0) return 0;
+ int result = 0;
+ int size = lines.size();
+ PdfLine line;
+ for (int i = 0; i < size; i++) {
+ line = (PdfLine) lines.get(i);
+ if (line.size() > 0) result++;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the height needed to draw the remaining text.
+ *
+ * @return a height
+ */
+
+ public float remainingHeight() {
+ float result = 0f;
+ for (Iterator i = images.iterator(); i.hasNext();) {
+ Image image = (Image) i.next();
+ result += image.scaledHeight();
+ }
+ return remainingLines() * leading + 2 * cellpadding + cellspacing + result + leading / 2.5f;
+ }
+
+ // methods to retrieve membervariables
+
+ /**
+ * Gets the leading of a cell.
+ *
+ * @return the leading of the lines is the cell.
+ */
+
+ public float leading() {
+ return leading;
+ }
+
+ /**
+ * Gets the number of the row this cell is in..
+ *
+ * @return a number
+ */
+
+ public int rownumber() {
+ return rownumber;
+ }
+
+ /**
+ * Gets the rowspan of a cell.
+ *
+ * @return the rowspan of the cell
+ */
+
+ public int rowspan() {
+ return rowspan;
+ }
+
+ /**
+ * Gets the cellspacing of a cell.
+ *
+ * @return a value
+ */
+
+ public float cellspacing() {
+ return cellspacing;
+ }
+
+ /**
+ * Gets the cellpadding of a cell..
+ *
+ * @return a value
+ */
+
+ public float cellpadding() {
+ return cellpadding;
+ }
+
+ /**
+ * Processes all actions contained in the cell.
+ * @param element an element in the cell
+ * @param action an action that should be coupled to the cell
+ * @param allActions
+ */
+
+ protected void processActions(Element element, PdfAction action, ArrayList allActions) {
+ if (element.type() == Element.ANCHOR) {
+ String url = ((Anchor) element).reference();
+ if (url != null) {
+ action = new PdfAction(url);
+ }
+ }
+ Iterator i;
+ switch (element.type()) {
+ case Element.PHRASE:
+ case Element.SECTION:
+ case Element.ANCHOR:
+ case Element.CHAPTER:
+ case Element.LISTITEM:
+ case Element.PARAGRAPH:
+ for (i = ((ArrayList) element).iterator(); i.hasNext();) {
+ processActions((Element) i.next(), action, allActions);
+ }
+ break;
+ case Element.CHUNK:
+ allActions.add(action);
+ break;
+ case Element.LIST:
+ for (i = ((List) element).getItems().iterator(); i.hasNext();) {
+ processActions((Element) i.next(), action, allActions);
+ }
+ break;
+ default:
+ int n = element.getChunks().size();
+ while (n-- > 0)
+ allActions.add(action);
+ break;
+ }
+ }
+
+ /**
+ * This is the number of the group the cell is in.
+ */
+ private int groupNumber;
+
+ /**
+ * Gets the number of the group this cell is in..
+ *
+ * @return a number
+ */
+
+ public int getGroupNumber() {
+ return groupNumber;
+ }
+
+ /**
+ * Sets the group number.
+ * @param number
+ */
+
+ void setGroupNumber(int number) {
+ groupNumber = number;
+ }
+
+ /**
+ * Gets a Rectangle that is altered to fit on the page.
+ *
+ * @param top the top position
+ * @param bottom the bottom position
+ * @return a Rectangle
+ */
+
+ public Rectangle rectangle(float top, float bottom) {
+ Rectangle tmp = new Rectangle(left(), bottom(), right(), top());
+ tmp.cloneNonPositionParameters(this);
+ if (top() > top) {
+ tmp.setTop(top);
+ tmp.setBorder(border - (border & TOP));
+ }
+ if (bottom() < bottom) {
+ tmp.setBottom(bottom);
+ tmp.setBorder(border - (border & BOTTOM));
+ }
+ return tmp;
+ }
+
+ /**
+ * Sets the value of {@link #useAscender}.
+ * @param use use ascender height if true
+ */
+ public void setUseAscender(boolean use) {
+ useAscender = use;
+ }
+
+ /**
+ * Gets the value of {@link #useAscender}
+ * @return useAscender
+ */
+ public boolean isUseAscender() {
+ return useAscender;
+ }
+
+ /**
+ * Sets the value of {@link #useDescender}.
+ * @param use use descender height if true
+ */
+ public void setUseDescender(boolean use) {
+ useDescender = use;
+ }
+
+ /**
+ * gets the value of {@link #useDescender }
+ * @return useDescender
+ */
+ public boolean isUseDescender() {
+ return useDescender;
+ }
+
+ /**
+ * Sets the value of {@link #useBorderPadding}.
+ * @param use adjust layour for borders if true
+ */
+ public void setUseBorderPadding(boolean use) {
+ useBorderPadding = use;
+ }
+
+ /**
+ * Gets the value of {@link #useBorderPadding}.
+ * @return useBorderPadding
+ */
+ public boolean isUseBorderPadding() {
+ return useBorderPadding;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfChunk.java b/src/main/java/com/lowagie/text/pdf/PdfChunk.java
new file mode 100644
index 0000000..2906552
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfChunk.java
@@ -0,0 +1,781 @@
+/*
+ * $Id: PdfChunk.java,v 1.71 2006/02/16 16:17:52 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.awt.Color;
+
+import com.lowagie.text.Chunk;
+import com.lowagie.text.Font;
+import com.lowagie.text.Image;
+import com.lowagie.text.SplitCharacter;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * A PdfChunk
is the PDF translation of a Chunk
.
+ * PdfChunk
is a PdfString
in a certain
+ * PdfFont
and Color
.
+ *
+ * @see PdfString
+ * @see com.lowagie.text.Chunk
+ * @see com.lowagie.text.Font
+ */
+
+public class PdfChunk implements SplitCharacter{
+
+ private static final char singleSpace[] = {' '};
+ private static final PdfChunk thisChunk[] = new PdfChunk[1];
+ private static final float ITALIC_ANGLE = 0.21256f;
+/** The allowed attributes in variable attributes
. */
+ private static final HashMap keysAttributes = new HashMap();
+
+/** The allowed attributes in variable noStroke
. */
+ private static final HashMap keysNoStroke = new HashMap();
+
+ static {
+ keysAttributes.put(Chunk.ACTION, null);
+ keysAttributes.put(Chunk.UNDERLINE, null);
+ keysAttributes.put(Chunk.REMOTEGOTO, null);
+ keysAttributes.put(Chunk.LOCALGOTO, null);
+ keysAttributes.put(Chunk.LOCALDESTINATION, null);
+ keysAttributes.put(Chunk.GENERICTAG, null);
+ keysAttributes.put(Chunk.NEWPAGE, null);
+ keysAttributes.put(Chunk.IMAGE, null);
+ keysAttributes.put(Chunk.BACKGROUND, null);
+ keysAttributes.put(Chunk.PDFANNOTATION, null);
+ keysAttributes.put(Chunk.SKEW, null);
+ keysAttributes.put(Chunk.HSCALE, null);
+ keysNoStroke.put(Chunk.SUBSUPSCRIPT, null);
+ keysNoStroke.put(Chunk.SPLITCHARACTER, null);
+ keysNoStroke.put(Chunk.HYPHENATION, null);
+ keysNoStroke.put(Chunk.TEXTRENDERMODE, null);
+ }
+
+ // membervariables
+
+ /** The value of this object. */
+ protected String value = PdfObject.NOTHING;
+
+ /** The encoding. */
+ protected String encoding = BaseFont.WINANSI;
+
+
+/** The font for this PdfChunk
. */
+ protected PdfFont font;
+
+ protected BaseFont baseFont;
+
+ protected SplitCharacter splitCharacter;
+/**
+ * Metric attributes.
+ * true
if the chunk split was cause by a newline. */
+ protected boolean newlineSplit;
+
+/** The image in this PdfChunk
, if it has one */
+ protected Image image;
+
+/** The offset in the x direction for the image */
+ protected float offsetX;
+
+/** The offset in the y direction for the image */
+ protected float offsetY;
+
+/** Indicates if the height and offset of the Image has to be taken into account */
+ protected boolean changeLeading = false;
+
+ // constructors
+
+/**
+ * Constructs a PdfChunk
-object.
+ *
+ * @param string the content of the PdfChunk
-object
+ * @param other Chunk with the same style you want for the new Chunk
+ */
+
+ PdfChunk(String string, PdfChunk other) {
+ thisChunk[0] = this;
+ value = string;
+ this.font = other.font;
+ this.attributes = other.attributes;
+ this.noStroke = other.noStroke;
+ this.baseFont = other.baseFont;
+ Object obj[] = (Object[])attributes.get(Chunk.IMAGE);
+ if (obj == null)
+ image = null;
+ else {
+ image = (Image)obj[0];
+ offsetX = ((Float)obj[1]).floatValue();
+ offsetY = ((Float)obj[2]).floatValue();
+ changeLeading = ((Boolean)obj[3]).booleanValue();
+ }
+ encoding = font.getFont().getEncoding();
+ splitCharacter = (SplitCharacter)noStroke.get(Chunk.SPLITCHARACTER);
+ if (splitCharacter == null)
+ splitCharacter = this;
+ }
+
+/**
+ * Constructs a PdfChunk
-object.
+ *
+ * @param chunk the original Chunk
-object
+ * @param action the PdfAction
if the Chunk
comes from an Anchor
+ */
+
+ PdfChunk(Chunk chunk, PdfAction action) {
+ thisChunk[0] = this;
+ value = chunk.content();
+
+ Font f = chunk.font();
+ float size = f.size();
+ if (size == Font.UNDEFINED)
+ size = 12;
+ baseFont = f.getBaseFont();
+ int style = f.style();
+ if (style == Font.UNDEFINED) {
+ style = Font.NORMAL;
+ }
+ if (baseFont == null) {
+ // translation of the font-family to a PDF font-family
+ baseFont = f.getCalculatedBaseFont(false);
+ }
+ else {
+ // bold simulation
+ if ((style & Font.BOLD) != 0)
+ attributes.put(Chunk.TEXTRENDERMODE, new Object[]{new Integer(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE), new Float(size / 30f), null});
+ // italic simulation
+ if ((style & Font.ITALIC) != 0)
+ attributes.put(Chunk.SKEW, new float[]{0, ITALIC_ANGLE});
+ }
+ font = new PdfFont(baseFont, size);
+ // other style possibilities
+ HashMap attr = chunk.getAttributes();
+ if (attr != null) {
+ for (Iterator i = attr.keySet().iterator(); i.hasNext();) {
+ Object name = i.next();
+ if (keysAttributes.containsKey(name)) {
+ attributes.put(name, attr.get(name));
+ }
+ else if (keysNoStroke.containsKey(name)) {
+ noStroke.put(name, attr.get(name));
+ }
+ }
+ if ("".equals(attr.get(Chunk.GENERICTAG))) {
+ attributes.put(Chunk.GENERICTAG, chunk.content());
+ }
+ }
+ if (f.isUnderlined()) {
+ Object obj[] = {null, new float[]{0, 1f / 15, 0, -1f / 3, 0}};
+ Object unders[][] = Chunk.addToArray((Object[][])attributes.get(Chunk.UNDERLINE), obj);
+ attributes.put(Chunk.UNDERLINE, unders);
+ }
+ if (f.isStrikethru()) {
+ Object obj[] = {null, new float[]{0, 1f / 15, 0, 1f / 3, 0}};
+ Object unders[][] = Chunk.addToArray((Object[][])attributes.get(Chunk.UNDERLINE), obj);
+ attributes.put(Chunk.UNDERLINE, unders);
+ }
+ if (action != null)
+ attributes.put(Chunk.ACTION, action);
+ // the color can't be stored in a PdfFont
+ noStroke.put(Chunk.COLOR, f.color());
+ noStroke.put(Chunk.ENCODING, font.getFont().getEncoding());
+ Object obj[] = (Object[])attributes.get(Chunk.IMAGE);
+ if (obj == null) {
+ image = null;
+ }
+ else {
+ attributes.remove(Chunk.HSCALE); // images are scaled in other ways
+ image = (Image)obj[0];
+ offsetX = ((Float)obj[1]).floatValue();
+ offsetY = ((Float)obj[2]).floatValue();
+ changeLeading = ((Boolean)obj[3]).booleanValue();
+ }
+ font.setImage(image);
+ Float hs = (Float)attributes.get(Chunk.HSCALE);
+ if (hs != null)
+ font.setHorizontalScaling(hs.floatValue());
+ encoding = font.getFont().getEncoding();
+ splitCharacter = (SplitCharacter)noStroke.get(Chunk.SPLITCHARACTER);
+ if (splitCharacter == null)
+ splitCharacter = this;
+ }
+
+ // methods
+
+ /** Gets the Unicode equivalent to a CID.
+ * The (inexistent) CID PdfChunk
if it's too long for the given width.
+ * PdfChunk
wasn't truncated.
+ *
+ * @param width a given width
+ * @return the PdfChunk
that doesn't fit into the width.
+ */
+
+ PdfChunk split(float width) {
+ newlineSplit = false;
+ if (image != null) {
+ if (image.scaledWidth() > width) {
+ PdfChunk pc = new PdfChunk(Chunk.OBJECT_REPLACEMENT_CHARACTER, this);
+ value = "";
+ attributes = new HashMap();
+ image = null;
+ font = PdfFont.getDefaultFont();
+ return pc;
+ }
+ else
+ return null;
+ }
+ HyphenationEvent hyphenationEvent = (HyphenationEvent)noStroke.get(Chunk.HYPHENATION);
+ int currentPosition = 0;
+ int splitPosition = -1;
+ float currentWidth = 0;
+
+ // loop over all the characters of a string
+ // or until the totalWidth is reached
+ int lastSpace = -1;
+ float lastSpaceWidth = 0;
+ int length = value.length();
+ char valueArray[] = value.toCharArray();
+ char character = 0;
+ BaseFont ft = font.getFont();
+ if (ft.getFontType() == BaseFont.FONT_TYPE_CJK && ft.getUnicodeEquivalent(' ') != ' ') {
+ while (currentPosition < length) {
+ // the width of every character is added to the currentWidth
+ char cidChar = valueArray[currentPosition];
+ character = ft.getUnicodeEquivalent(cidChar);
+ // if a newLine or carriageReturn is encountered
+ if (character == '\n') {
+ newlineSplit = true;
+ String returnValue = value.substring(currentPosition + 1);
+ value = value.substring(0, currentPosition);
+ if (value.length() < 1) {
+ value = "\u0001";
+ }
+ PdfChunk pc = new PdfChunk(returnValue, this);
+ return pc;
+ }
+ currentWidth += font.width(cidChar);
+ if (character == ' ') {
+ lastSpace = currentPosition + 1;
+ lastSpaceWidth = currentWidth;
+ }
+ if (currentWidth > width)
+ break;
+ // if a split-character is encountered, the splitPosition is altered
+ if (splitCharacter.isSplitCharacter(0, currentPosition, length, valueArray, thisChunk))
+ splitPosition = currentPosition + 1;
+ currentPosition++;
+ }
+ }
+ else {
+ while (currentPosition < length) {
+ // the width of every character is added to the currentWidth
+ character = valueArray[currentPosition];
+ // if a newLine or carriageReturn is encountered
+ if (character == '\r' || character == '\n') {
+ newlineSplit = true;
+ int inc = 1;
+ if (character == '\r' && currentPosition + 1 < length && valueArray[currentPosition + 1] == '\n')
+ inc = 2;
+ String returnValue = value.substring(currentPosition + inc);
+ value = value.substring(0, currentPosition);
+ if (value.length() < 1) {
+ value = " ";
+ }
+ PdfChunk pc = new PdfChunk(returnValue, this);
+ return pc;
+ }
+ currentWidth += font.width(character);
+ if (character == ' ') {
+ lastSpace = currentPosition + 1;
+ lastSpaceWidth = currentWidth;
+ }
+ if (currentWidth > width)
+ break;
+ // if a split-character is encountered, the splitPosition is altered
+ if (splitCharacter.isSplitCharacter(0, currentPosition, length, valueArray, null))
+ splitPosition = currentPosition + 1;
+ currentPosition++;
+ }
+ }
+
+ // if all the characters fit in the total width, null is returned (there is no overflow)
+ if (currentPosition == length) {
+ return null;
+ }
+ // otherwise, the string has to be truncated
+ if (splitPosition < 0) {
+ String returnValue = value;
+ value = "";
+ PdfChunk pc = new PdfChunk(returnValue, this);
+ return pc;
+ }
+ if (lastSpace > splitPosition && splitCharacter.isSplitCharacter(0, 0, 1, singleSpace, null))
+ splitPosition = lastSpace;
+ if (hyphenationEvent != null && lastSpace < currentPosition) {
+ int wordIdx = getWord(value, lastSpace);
+ if (wordIdx > lastSpace) {
+ String pre = hyphenationEvent.getHyphenatedWordPre(value.substring(lastSpace, wordIdx), font.getFont(), font.size(), width - lastSpaceWidth);
+ String post = hyphenationEvent.getHyphenatedWordPost();
+ if (pre.length() > 0) {
+ String returnValue = post + value.substring(wordIdx);
+ value = trim(value.substring(0, lastSpace) + pre);
+ PdfChunk pc = new PdfChunk(returnValue, this);
+ return pc;
+ }
+ }
+ }
+ String returnValue = value.substring(splitPosition);
+ value = trim(value.substring(0, splitPosition));
+ PdfChunk pc = new PdfChunk(returnValue, this);
+ return pc;
+ }
+
+/**
+ * Truncates this PdfChunk
if it's too long for the given width.
+ * PdfChunk
wasn't truncated.
+ *
+ * @param width a given width
+ * @return the PdfChunk
that doesn't fit into the width.
+ */
+
+ PdfChunk truncate(float width) {
+ if (image != null) {
+ if (image.scaledWidth() > width) {
+ PdfChunk pc = new PdfChunk("", this);
+ value = "";
+ attributes.remove(Chunk.IMAGE);
+ image = null;
+ font = PdfFont.getDefaultFont();
+ return pc;
+ }
+ else
+ return null;
+ }
+
+ int currentPosition = 0;
+ float currentWidth = 0;
+
+ // it's no use trying to split if there isn't even enough place for a space
+ if (width < font.width()) {
+ String returnValue = value.substring(1);
+ value = value.substring(0, 1);
+ PdfChunk pc = new PdfChunk(returnValue, this);
+ return pc;
+ }
+
+ // loop over all the characters of a string
+ // or until the totalWidth is reached
+ int length = value.length();
+ char character;
+ while (currentPosition < length) {
+ // the width of every character is added to the currentWidth
+ character = value.charAt(currentPosition);
+ currentWidth += font.width(character);
+ if (currentWidth > width)
+ break;
+ currentPosition++;
+ }
+
+ // if all the characters fit in the total width, null is returned (there is no overflow)
+ if (currentPosition == length) {
+ return null;
+ }
+
+ // otherwise, the string has to be truncated
+ //currentPosition -= 2;
+ // we have to chop off minimum 1 character from the chunk
+ if (currentPosition == 0) {
+ currentPosition = 1;
+ }
+ String returnValue = value.substring(currentPosition);
+ value = value.substring(0, currentPosition);
+ PdfChunk pc = new PdfChunk(returnValue, this);
+ return pc;
+ }
+
+ // methods to retrieve the membervariables
+
+/**
+ * Returns the font of this Chunk
.
+ *
+ * @return a PdfFont
+ */
+
+ PdfFont font() {
+ return font;
+ }
+
+/**
+ * Returns the color of this Chunk
.
+ *
+ * @return a Color
+ */
+
+ Color color() {
+ return (Color)noStroke.get(Chunk.COLOR);
+ }
+
+/**
+ * Returns the width of this PdfChunk
.
+ *
+ * @return a width
+ */
+
+ float width() {
+ return font.width(value);
+ }
+
+/**
+ * Checks if the PdfChunk
split was caused by a newline.
+ * @return true
if the PdfChunk
split was caused by a newline.
+ */
+
+ public boolean isNewlineSplit()
+ {
+ return newlineSplit;
+ }
+
+/**
+ * Gets the width of the PdfChunk
taking into account the
+ * extra character and word spacing.
+ * @param charSpacing the extra character spacing
+ * @param wordSpacing the extra word spacing
+ * @return the calculated width
+ */
+
+ public float getWidthCorrected(float charSpacing, float wordSpacing)
+ {
+ if (image != null) {
+ return image.scaledWidth() + charSpacing;
+ }
+ int numberOfSpaces = 0;
+ int idx = -1;
+ while ((idx = value.indexOf(' ', idx + 1)) >= 0)
+ ++numberOfSpaces;
+ return width() + (value.length() * charSpacing + numberOfSpaces * wordSpacing);
+ }
+
+ /**
+ * Gets the text displacement relatiev to the baseline.
+ * @return a displacement in points
+ */
+ public float getTextRise() {
+ Float f = (Float) getAttribute(Chunk.SUBSUPSCRIPT);
+ if (f != null) {
+ return f.floatValue();
+ }
+ return 0.0f;
+ }
+
+/**
+ * Trims the last space.
+ * @return the width of the space trimmed, otherwise 0
+ */
+
+ public float trimLastSpace()
+ {
+ BaseFont ft = font.getFont();
+ if (ft.getFontType() == BaseFont.FONT_TYPE_CJK && ft.getUnicodeEquivalent(' ') != ' ') {
+ if (value.length() > 1 && value.endsWith("\u0001")) {
+ value = value.substring(0, value.length() - 1);
+ return font.width('\u0001');
+ }
+ }
+ else {
+ if (value.length() > 1 && value.endsWith(" ")) {
+ value = value.substring(0, value.length() - 1);
+ return font.width(' ');
+ }
+ }
+ return 0;
+ }
+
+/**
+ * Gets an attribute. The search is made in attributes
+ * and noStroke
.
+ * @param name the attribute key
+ * @return the attribute value or null if not found
+ */
+
+ Object getAttribute(String name)
+ {
+ if (attributes.containsKey(name))
+ return attributes.get(name);
+ return noStroke.get(name);
+ }
+
+/**
+ *Checks if the attribute exists.
+ * @param name the attribute key
+ * @return true
if the attribute exists
+ */
+
+ boolean isAttribute(String name)
+ {
+ if (attributes.containsKey(name))
+ return true;
+ return noStroke.containsKey(name);
+ }
+
+/**
+ * Checks if this PdfChunk
needs some special metrics handling.
+ * @return true
if this PdfChunk
needs some special metrics handling.
+ */
+
+ boolean isStroked()
+ {
+ return (attributes.size() > 0);
+ }
+
+/**
+ * Checks if there is an image in the PdfChunk
.
+ * @return true
if an image is present
+ */
+
+ boolean isImage()
+ {
+ return image != null;
+ }
+
+/**
+ * Gets the image in the PdfChunk
.
+ * @return the image or null
+ */
+
+ Image getImage()
+ {
+ return image;
+ }
+
+/**
+ * Sets the image offset in the x direction
+ * @param offsetX the image offset in the x direction
+ */
+
+ void setImageOffsetX(float offsetX)
+ {
+ this.offsetX = offsetX;
+ }
+
+/**
+ * Gets the image offset in the x direction
+ * @return the image offset in the x direction
+ */
+
+ float getImageOffsetX()
+ {
+ return offsetX;
+ }
+
+/**
+ * Sets the image offset in the y direction
+ * @param offsetY the image offset in the y direction
+ */
+
+ void setImageOffsetY(float offsetY)
+ {
+ this.offsetY = offsetY;
+ }
+
+/**
+ * Gets the image offset in the y direction
+ * @return Gets the image offset in the y direction
+ */
+
+ float getImageOffsetY()
+ {
+ return offsetY;
+ }
+
+/**
+ * sets the value.
+ * @param value content of the Chunk
+ */
+
+ void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return value;
+ }
+
+ /**
+ * Tells you if this string is in Chinese, Japanese, Korean or Identity-H.
+ * @return true if the Chunk has a special encoding
+ */
+
+ boolean isSpecialEncoding() {
+ return encoding.equals(CJKFont.CJK_ENCODING) || encoding.equals(BaseFont.IDENTITY_H);
+ }
+
+ /**
+ * Gets the encoding of this string.
+ *
+ * @return a String
+ */
+
+ String getEncoding() {
+ return encoding;
+ }
+
+ int length() {
+ return value.length();
+ }
+/**
+ * Checks if a character can be used to split a PdfString
.
+ * true
if the character can be used to split a string, false
otherwise
+ */
+ public boolean isSplitCharacter(int start, int current, int end, char[] cc, PdfChunk[] ck) {
+ char c;
+ if (ck == null)
+ c = cc[current];
+ else
+ c = ck[Math.min(current, ck.length - 1)].getUnicodeEquivalent(cc[current]);
+ if (c <= ' ' || c == '-') {
+ return true;
+ }
+ if (c < 0x2e80)
+ return false;
+ return ((c >= 0x2e80 && c < 0xd7a0)
+ || (c >= 0xf900 && c < 0xfb00)
+ || (c >= 0xfe30 && c < 0xfe50)
+ || (c >= 0xff61 && c < 0xffa0));
+ }
+
+ boolean isExtSplitCharacter(int start, int current, int end, char[] cc, PdfChunk[] ck) {
+ return splitCharacter.isSplitCharacter(start, current, end, cc, ck);
+ }
+
+/**
+ * Removes all the ' ' and '-'-characters on the right of a String
.
+ * String
corresponding with the key
+ */
+
+ public void put(PdfName key, PdfObject value) {
+ hashMap.put(key, value);
+ list.add(key);
+ }
+
+/**
+ * Adds a that has to be trimmed.
+ * @return the trimmed
PdfObjectString
+ */
+ String trim(String string) {
+ BaseFont ft = font.getFont();
+ if (ft.getFontType() == BaseFont.FONT_TYPE_CJK && ft.getUnicodeEquivalent(' ') != ' ') {
+ while (string.endsWith("\u0001")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ }
+ else {
+ while (string.endsWith(" ") || string.endsWith("\t")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ }
+ return string;
+ }
+
+ public boolean changeLeading() {
+ return changeLeading;
+ }
+
+ float getCharWidth(char c) {
+ if (noPrint(c))
+ return 0;
+ return font.width(c);
+ }
+
+ public static boolean noPrint(char c) {
+ return ((c >= 0x200b && c <= 0x200f) || (c >= 0x202a && c <= 0x202e));
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfColor.java b/src/main/java/com/lowagie/text/pdf/PdfColor.java
new file mode 100644
index 0000000..b07348d
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfColor.java
@@ -0,0 +1,80 @@
+/*
+ * $Id: PdfColor.java,v 1.54 2005/05/04 14:31:42 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import java.awt.Color;
+/**
+ * A PdfColor
defines a Color (it's a PdfArray
containing 3 values).
+ *
+ * @see PdfDictionary
+ */
+
+class PdfColor extends PdfArray {
+
+ // constructors
+
+/**
+ * Constructs a new PdfColor
.
+ *
+ * @param red a value between 0 and 255
+ * @param green a value between 0 and 255
+ * @param blue a value between 0 and 255
+ */
+
+ PdfColor(int red, int green, int blue) {
+ super(new PdfNumber((double)(red & 0xFF) / 0xFF));
+ add(new PdfNumber((double)(green & 0xFF) / 0xFF));
+ add(new PdfNumber((double)(blue & 0xFF) / 0xFF));
+ }
+
+ PdfColor(Color color) {
+ this(color.getRed(), color.getGreen(), color.getBlue());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfContentByte.java b/src/main/java/com/lowagie/text/pdf/PdfContentByte.java
new file mode 100644
index 0000000..07669cd
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfContentByte.java
@@ -0,0 +1,3053 @@
+/*
+ * $Id: PdfContentByte.java,v 1.100 2006/05/18 09:39:28 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.awt.geom.AffineTransform;
+import java.awt.print.PrinterJob;
+
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.Image;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Annotation;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.pdf.ExtendedColor;
+
+/**
+ * PdfContentByte
is an object containing the user positioned
+ * text and graphic contents of a page. It knows how to apply the proper
+ * font encoding.
+ */
+
+public class PdfContentByte {
+
+ /**
+ * This class keeps the graphic state of the current page
+ */
+
+ static class GraphicState {
+
+ /** This is the font in use */
+ FontDetails fontDetails;
+
+ /** This is the color in use */
+ ColorDetails colorDetails;
+
+ /** This is the font size in use */
+ float size;
+
+ /** The x position of the text line matrix. */
+ protected float xTLM = 0;
+ /** The y position of the text line matrix. */
+ protected float yTLM = 0;
+
+ /** The current text leading. */
+ protected float leading = 0;
+
+ /** The current horizontal scaling */
+ protected float scale = 100;
+
+ /** The current character spacing */
+ protected float charSpace = 0;
+
+ /** The current word spacing */
+ protected float wordSpace = 0;
+
+ GraphicState() {
+ }
+
+ GraphicState(GraphicState cp) {
+ fontDetails = cp.fontDetails;
+ colorDetails = cp.colorDetails;
+ size = cp.size;
+ xTLM = cp.xTLM;
+ yTLM = cp.yTLM;
+ leading = cp.leading;
+ scale = cp.scale;
+ charSpace = cp.charSpace;
+ wordSpace = cp.wordSpace;
+ }
+ }
+
+ /** The alignement is center */
+ public static final int ALIGN_CENTER = Element.ALIGN_CENTER;
+
+ /** The alignement is left */
+ public static final int ALIGN_LEFT = Element.ALIGN_LEFT;
+
+ /** The alignement is right */
+ public static final int ALIGN_RIGHT = Element.ALIGN_RIGHT;
+
+ /** A possible line cap value */
+ public static final int LINE_CAP_BUTT = 0;
+ /** A possible line cap value */
+ public static final int LINE_CAP_ROUND = 1;
+ /** A possible line cap value */
+ public static final int LINE_CAP_PROJECTING_SQUARE = 2;
+
+ /** A possible line join value */
+ public static final int LINE_JOIN_MITER = 0;
+ /** A possible line join value */
+ public static final int LINE_JOIN_ROUND = 1;
+ /** A possible line join value */
+ public static final int LINE_JOIN_BEVEL = 2;
+
+ /** A possible text rendering value */
+ public static final int TEXT_RENDER_MODE_FILL = 0;
+ /** A possible text rendering value */
+ public static final int TEXT_RENDER_MODE_STROKE = 1;
+ /** A possible text rendering value */
+ public static final int TEXT_RENDER_MODE_FILL_STROKE = 2;
+ /** A possible text rendering value */
+ public static final int TEXT_RENDER_MODE_INVISIBLE = 3;
+ /** A possible text rendering value */
+ public static final int TEXT_RENDER_MODE_FILL_CLIP = 4;
+ /** A possible text rendering value */
+ public static final int TEXT_RENDER_MODE_STROKE_CLIP = 5;
+ /** A possible text rendering value */
+ public static final int TEXT_RENDER_MODE_FILL_STROKE_CLIP = 6;
+ /** A possible text rendering value */
+ public static final int TEXT_RENDER_MODE_CLIP = 7;
+
+ private static final float[] unitRect = {0, 0, 0, 1, 1, 0, 1, 1};
+ // membervariables
+
+ /** This is the actual content */
+ protected ByteBuffer content = new ByteBuffer();
+
+ /** This is the writer */
+ protected PdfWriter writer;
+
+ /** This is the PdfDocument */
+ protected PdfDocument pdf;
+
+ /** This is the GraphicState in use */
+ protected GraphicState state = new GraphicState();
+
+ /** The list were we save/restore the state */
+ protected ArrayList stateList = new ArrayList();
+
+ /** The list were we save/restore the layer depth */
+ protected ArrayList layerDepth;
+
+ /** The separator between commands.
+ */
+ protected int separator = '\n';
+
+ private static HashMap abrev = new HashMap();
+
+ static {
+ abrev.put(PdfName.BITSPERCOMPONENT, "/BPC ");
+ abrev.put(PdfName.COLORSPACE, "/CS ");
+ abrev.put(PdfName.DECODE, "/D ");
+ abrev.put(PdfName.DECODEPARMS, "/DP ");
+ abrev.put(PdfName.FILTER, "/F ");
+ abrev.put(PdfName.HEIGHT, "/H ");
+ abrev.put(PdfName.IMAGEMASK, "/IM ");
+ abrev.put(PdfName.INTENT, "/Intent ");
+ abrev.put(PdfName.INTERPOLATE, "/I ");
+ abrev.put(PdfName.WIDTH, "/W ");
+ }
+
+ // constructors
+
+ /**
+ * Constructs a new PdfContentByte
-object.
+ *
+ * @param wr the writer associated to this content
+ */
+
+ public PdfContentByte(PdfWriter wr) {
+ if (wr != null) {
+ writer = wr;
+ pdf = writer.getPdfDocument();
+ }
+ }
+
+ // methods to get the content of this object
+
+ /**
+ * Returns the String
representation of this PdfContentByte
-object.
+ *
+ * @return a String
+ */
+
+ public String toString() {
+ return content.toString();
+ }
+
+ /**
+ * Gets the internal buffer.
+ * @return the internal buffer
+ */
+ public ByteBuffer getInternalBuffer() {
+ return content;
+ }
+
+ /** Returns the PDF representation of this PdfContentByte
-object.
+ *
+ * @param writer the PdfWriter
+ * @return a byte
array with the representation
+ */
+
+ public byte[] toPdf(PdfWriter writer) {
+ return content.toByteArray();
+ }
+
+ // methods to add graphical content
+
+ /**
+ * Adds the content of another PdfContent
-object to this object.
+ *
+ * @param other another PdfByteContent
-object
+ */
+
+ public void add(PdfContentByte other) {
+ if (other.writer != null && writer != other.writer)
+ throw new RuntimeException("Inconsistent writers. Are you mixing two documents?");
+ content.append(other.content);
+ }
+
+ /**
+ * Gets the x position of the text line matrix.
+ *
+ * @return the x position of the text line matrix
+ */
+ public float getXTLM() {
+ return state.xTLM;
+ }
+
+ /**
+ * Gets the y position of the text line matrix.
+ *
+ * @return the y position of the text line matrix
+ */
+ public float getYTLM() {
+ return state.yTLM;
+ }
+
+ /**
+ * Gets the current text leading.
+ *
+ * @return the current text leading
+ */
+ public float getLeading() {
+ return state.leading;
+ }
+
+ /**
+ * Gets the current character spacing.
+ *
+ * @return the current character spacing
+ */
+ public float getCharacterSpacing() {
+ return state.charSpace;
+ }
+
+ /**
+ * Gets the current word spacing.
+ *
+ * @return the current word spacing
+ */
+ public float getWordSpacing() {
+ return state.wordSpace;
+ }
+
+ /**
+ * Gets the current character spacing.
+ *
+ * @return the current character spacing
+ */
+ public float getHorizontalScaling() {
+ return state.scale;
+ }
+
+ /**
+ * Changes the Flatness.
+ *
+ *
+ * @param flatness a value
+ */
+
+ public void setFlatness(float flatness) {
+ if (flatness >= 0 && flatness <= 100) {
+ content.append(flatness).append(" i").append_i(separator);
+ }
+ }
+
+ /**
+ * Changes the Line cap style.
+ *
+ * Allowed values are LINE_CAP_BUTT, LINE_CAP_ROUND and LINE_CAP_PROJECTING_SQUARE.
+ *
+ * @param style a value
+ */
+
+ public void setLineCap(int style) {
+ if (style >= 0 && style <= 2) {
+ content.append(style).append(" J").append_i(separator);
+ }
+ }
+
+ /**
+ * Changes the value of the line dash pattern.
+ *
+ *
+ * @param phase the value of the phase
+ */
+
+ public void setLineDash(float phase) {
+ content.append("[] ").append(phase).append(" d").append_i(separator);
+ }
+
+ /**
+ * Changes the value of the line dash pattern.
+ *
+ *
+ * @param phase the value of the phase
+ * @param unitsOn the number of units that must be 'on' (equals the number of units that must be 'off').
+ */
+
+ public void setLineDash(float unitsOn, float phase) {
+ content.append("[").append(unitsOn).append("] ").append(phase).append(" d").append_i(separator);
+ }
+
+ /**
+ * Changes the value of the line dash pattern.
+ *
+ *
+ * @param phase the value of the phase
+ * @param unitsOn the number of units that must be 'on'
+ * @param unitsOff the number of units that must be 'off'
+ */
+
+ public void setLineDash(float unitsOn, float unitsOff, float phase) {
+ content.append("[").append(unitsOn).append(' ').append(unitsOff).append("] ").append(phase).append(" d").append_i(separator);
+ }
+
+ /**
+ * Changes the value of the line dash pattern.
+ *
+ *
+ * @param array length of the alternating dashes and gaps
+ * @param phase the value of the phase
+ */
+
+ public final void setLineDash(float[] array, float phase) {
+ content.append("[");
+ for (int i = 0; i < array.length; i++) {
+ content.append(array[i]);
+ if (i < array.length - 1) content.append(' ');
+ }
+ content.append("] ").append(phase).append(" d").append_i(separator);
+ }
+
+ /**
+ * Changes the Line join style.
+ *
+ * Allowed values are LINE_JOIN_MITER (Miter joins), LINE_JOIN_ROUND (Round joins) and LINE_JOIN_BEVEL (Bevel joins).
+ *
+ * @param style a value
+ */
+
+ public void setLineJoin(int style) {
+ if (style >= 0 && style <= 2) {
+ content.append(style).append(" j").append_i(separator);
+ }
+ }
+
+ /**
+ * Changes the line width.
+ *
+ *
+ * @param w a width
+ */
+
+ public void setLineWidth(float w) {
+ content.append(w).append(" w").append_i(separator);
+ }
+
+ /**
+ * Changes the Miter limit.
+ *
+ *
+ * @param miterLimit a miter limit
+ */
+
+ public void setMiterLimit(float miterLimit) {
+ if (miterLimit > 1) {
+ content.append(miterLimit).append(" M").append_i(separator);
+ }
+ }
+
+ /**
+ * Modify the current clipping path by intersecting it with the current path, using the
+ * nonzero winding number rule to determine which regions lie inside the clipping
+ * path.
+ */
+
+ public void clip() {
+ content.append("W").append_i(separator);
+ }
+
+ /**
+ * Modify the current clipping path by intersecting it with the current path, using the
+ * even-odd rule to determine which regions lie inside the clipping path.
+ */
+
+ public void eoClip() {
+ content.append("W*").append_i(separator);
+ }
+
+ /**
+ * Changes the currentgray tint for filling paths (device dependent colors!).
+ * Rectangle
+ */
+ public void variableRectangle(Rectangle rect) {
+ float t = rect.top();
+ float b = rect.bottom();
+ float r = rect.right();
+ float l = rect.left();
+ float wt = rect.getBorderWidthTop();
+ float wb = rect.getBorderWidthBottom();
+ float wr = rect.getBorderWidthRight();
+ float wl = rect.getBorderWidthLeft();
+ Color ct = rect.getBorderColorTop();
+ Color cb = rect.getBorderColorBottom();
+ Color cr = rect.getBorderColorRight();
+ Color cl = rect.getBorderColorLeft();
+ saveState();
+ setLineCap(PdfContentByte.LINE_CAP_BUTT);
+ setLineJoin(PdfContentByte.LINE_JOIN_MITER);
+ float clw = 0;
+ boolean cdef = false;
+ Color ccol = null;
+ boolean cdefi = false;
+ Color cfil = null;
+ // draw top
+ if (wt > 0) {
+ setLineWidth(clw = wt);
+ cdef = true;
+ if (ct == null)
+ resetRGBColorStroke();
+ else
+ setColorStroke(ct);
+ ccol = ct;
+ moveTo(l, t - wt / 2f);
+ lineTo(r, t - wt / 2f);
+ stroke();
+ }
+
+ // Draw bottom
+ if (wb > 0) {
+ if (wb != clw)
+ setLineWidth(clw = wb);
+ if (!cdef || !compareColors(ccol, cb)) {
+ cdef = true;
+ if (cb == null)
+ resetRGBColorStroke();
+ else
+ setColorStroke(cb);
+ ccol = cb;
+ }
+ moveTo(r, b + wb / 2f);
+ lineTo(l, b + wb / 2f);
+ stroke();
+ }
+
+ // Draw right
+ if (wr > 0) {
+ if (wr != clw)
+ setLineWidth(clw = wr);
+ if (!cdef || !compareColors(ccol, cr)) {
+ cdef = true;
+ if (cr == null)
+ resetRGBColorStroke();
+ else
+ setColorStroke(cr);
+ ccol = cr;
+ }
+ boolean bt = compareColors(ct, cr);
+ boolean bb = compareColors(cb, cr);
+ moveTo(r - wr / 2f, bt ? t : t - wt);
+ lineTo(r - wr / 2f, bb ? b : b + wb);
+ stroke();
+ if (!bt || !bb) {
+ cdefi = true;
+ if (cr == null)
+ resetRGBColorFill();
+ else
+ setColorFill(cr);
+ cfil = cr;
+ if (!bt) {
+ moveTo(r, t);
+ lineTo(r, t - wt);
+ lineTo(r - wr, t - wt);
+ fill();
+ }
+ if (!bb) {
+ moveTo(r, b);
+ lineTo(r, b + wb);
+ lineTo(r - wr, b + wb);
+ fill();
+ }
+ }
+ }
+
+ // Draw Left
+ if (wl > 0) {
+ if (wl != clw)
+ setLineWidth(wl);
+ if (!cdef || !compareColors(ccol, cl)) {
+ if (cl == null)
+ resetRGBColorStroke();
+ else
+ setColorStroke(cl);
+ }
+ boolean bt = compareColors(ct, cl);
+ boolean bb = compareColors(cb, cl);
+ moveTo(l + wl / 2f, bt ? t : t - wt);
+ lineTo(l + wl / 2f, bb ? b : b + wb);
+ stroke();
+ if (!bt || !bb) {
+ if (!cdefi || !compareColors(cfil, cl)) {
+ if (cl == null)
+ resetRGBColorFill();
+ else
+ setColorFill(cl);
+ }
+ if (!bt) {
+ moveTo(l, t);
+ lineTo(l, t - wt);
+ lineTo(l + wl, t - wt);
+ fill();
+ }
+ if (!bb) {
+ moveTo(l, b);
+ lineTo(l, b + wb);
+ lineTo(l + wl, b + wb);
+ fill();
+ }
+ }
+ }
+ restoreState();
+ }
+
+ /**
+ * Adds a border (complete or partially) to the current path..
+ *
+ * @param rectangle a Rectangle
+ */
+
+ public void rectangle(Rectangle rectangle) {
+ // the coordinates of the border are retrieved
+ float x1 = rectangle.left();
+ float y1 = rectangle.bottom();
+ float x2 = rectangle.right();
+ float y2 = rectangle.top();
+
+ // the backgroundcolor is set
+ Color background = rectangle.backgroundColor();
+ if (background != null) {
+ setColorFill(background);
+ rectangle(x1, y1, x2 - x1, y2 - y1);
+ fill();
+ resetRGBColorFill();
+ }
+
+ // if the element hasn't got any borders, nothing is added
+ if (! rectangle.hasBorders()) {
+ return;
+ }
+
+ // if any of the individual border colors are set
+ // we draw the borders all around using the
+ // different colors
+ if (rectangle.isUseVariableBorders()) {
+ variableRectangle(rectangle);
+ }
+ else {
+ // the width is set to the width of the element
+ if (rectangle.borderWidth() != Rectangle.UNDEFINED) {
+ setLineWidth(rectangle.borderWidth());
+ }
+
+ // the color is set to the color of the element
+ Color color = rectangle.borderColor();
+ if (color != null) {
+ setColorStroke(color);
+ }
+
+ // if the box is a rectangle, it is added as a rectangle
+ if (rectangle.hasBorder(Rectangle.BOX)) {
+ rectangle(x1, y1, x2 - x1, y2 - y1);
+ }
+ // if the border isn't a rectangle, the different sides are added apart
+ else {
+ if (rectangle.hasBorder(Rectangle.RIGHT)) {
+ moveTo(x2, y1);
+ lineTo(x2, y2);
+ }
+ if (rectangle.hasBorder(Rectangle.LEFT)) {
+ moveTo(x1, y1);
+ lineTo(x1, y2);
+ }
+ if (rectangle.hasBorder(Rectangle.BOTTOM)) {
+ moveTo(x1, y1);
+ lineTo(x2, y1);
+ }
+ if (rectangle.hasBorder(Rectangle.TOP)) {
+ moveTo(x1, y2);
+ lineTo(x2, y2);
+ }
+ }
+
+ stroke();
+
+ if (color != null) {
+ resetRGBColorStroke();
+ }
+ }
+ }
+
+ /**
+ * Closes the current subpath by appending a straight line segment from the current point
+ * to the starting point of the subpath.
+ */
+
+ public void closePath() {
+ content.append("h").append_i(separator);
+ }
+
+ /**
+ * Ends the path without filling or stroking it.
+ */
+
+ public void newPath() {
+ content.append("n").append_i(separator);
+ }
+
+ /**
+ * Strokes the path.
+ */
+
+ public void stroke() {
+ content.append("S").append_i(separator);
+ }
+
+ /**
+ * Closes the path and strokes it.
+ */
+
+ public void closePathStroke() {
+ content.append("s").append_i(separator);
+ }
+
+ /**
+ * Fills the path, using the non-zero winding number rule to determine the region to fill.
+ */
+
+ public void fill() {
+ content.append("f").append_i(separator);
+ }
+
+ /**
+ * Fills the path, using the even-odd rule to determine the region to fill.
+ */
+
+ public void eoFill() {
+ content.append("f*").append_i(separator);
+ }
+
+ /**
+ * Fills the path using the non-zero winding number rule to determine the region to fill and strokes it.
+ */
+
+ public void fillStroke() {
+ content.append("B").append_i(separator);
+ }
+
+ /**
+ * Closes the path, fills it using the non-zero winding number rule to determine the region to fill and strokes it.
+ */
+
+ public void closePathFillStroke() {
+ content.append("b").append_i(separator);
+ }
+
+ /**
+ * Fills the path, using the even-odd rule to determine the region to fill and strokes it.
+ */
+
+ public void eoFillStroke() {
+ content.append("B*").append_i(separator);
+ }
+
+ /**
+ * Closes the path, fills it using the even-odd rule to determine the region to fill and strokes it.
+ */
+
+ public void closePathEoFillStroke() {
+ content.append("b*").append_i(separator);
+ }
+
+ /**
+ * Adds an Image
to the page. The Image
must have
+ * absolute positioning.
+ * @param image the Image
object
+ * @throws DocumentException if the Image
does not have absolute positioning
+ */
+ public void addImage(Image image) throws DocumentException {
+ addImage(image, false);
+ }
+
+ /**
+ * Adds an Image
to the page. The Image
must have
+ * absolute positioning. The image can be placed inline.
+ * @param image the Image
object
+ * @param inlineImage true
to place this image inline, false
otherwise
+ * @throws DocumentException if the Image
does not have absolute positioning
+ */
+ public void addImage(Image image, boolean inlineImage) throws DocumentException {
+ if (!image.hasAbsolutePosition())
+ throw new DocumentException("The image must have absolute positioning.");
+ float matrix[] = image.matrix();
+ matrix[Image.CX] = image.absoluteX() - matrix[Image.CX];
+ matrix[Image.CY] = image.absoluteY() - matrix[Image.CY];
+ addImage(image, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], inlineImage);
+ }
+
+ /**
+ * Adds an Image
to the page. The positioning of the Image
+ * is done with the transformation matrix. To position an image
at (x,y)
+ * use addImage(image, image_width, 0, 0, image_height, x, y).
+ * @param image the Image
object
+ * @param a an element of the transformation matrix
+ * @param b an element of the transformation matrix
+ * @param c an element of the transformation matrix
+ * @param d an element of the transformation matrix
+ * @param e an element of the transformation matrix
+ * @param f an element of the transformation matrix
+ * @throws DocumentException on error
+ */
+ public void addImage(Image image, float a, float b, float c, float d, float e, float f) throws DocumentException {
+ addImage(image, a, b, c, d, e, f, false);
+ }
+
+ /**
+ * Adds an Image
to the page. The positioning of the Image
+ * is done with the transformation matrix. To position an image
at (x,y)
+ * use addImage(image, image_width, 0, 0, image_height, x, y). The image can be placed inline.
+ * @param image the Image
object
+ * @param a an element of the transformation matrix
+ * @param b an element of the transformation matrix
+ * @param c an element of the transformation matrix
+ * @param d an element of the transformation matrix
+ * @param e an element of the transformation matrix
+ * @param f an element of the transformation matrix
+ * @param inlineImage true
to place this image inline, false
otherwise
+ * @throws DocumentException on error
+ */
+ public void addImage(Image image, float a, float b, float c, float d, float e, float f, boolean inlineImage) throws DocumentException {
+ try {
+ if (image.getLayer() != null)
+ beginLayer(image.getLayer());
+ if (image.isImgTemplate()) {
+ writer.addDirectImageSimple(image);
+ PdfTemplate template = image.templateData();
+ float w = template.getWidth();
+ float h = template.getHeight();
+ addTemplate(template, a / w, b / w, c / h, d / h, e, f);
+ }
+ else {
+ content.append("q ");
+ content.append(a).append(' ');
+ content.append(b).append(' ');
+ content.append(c).append(' ');
+ content.append(d).append(' ');
+ content.append(e).append(' ');
+ content.append(f).append(" cm");
+ if (inlineImage) {
+ content.append("\nBI\n");
+ PdfImage pimage = new PdfImage(image, "", null);
+ for (Iterator it = pimage.getKeys().iterator(); it.hasNext();) {
+ PdfName key = (PdfName)it.next();
+ PdfObject value = pimage.get(key);
+ String s = (String)abrev.get(key);
+ if (s == null)
+ continue;
+ content.append(s);
+ boolean check = true;
+ if (key.equals(PdfName.COLORSPACE) && value.isArray()) {
+ ArrayList ar = ((PdfArray)value).getArrayList();
+ if (ar.size() == 4
+ && PdfName.INDEXED.equals(ar.get(0))
+ && ((PdfObject)ar.get(1)).isName()
+ && ((PdfObject)ar.get(2)).isNumber()
+ && ((PdfObject)ar.get(3)).isString()
+ ) {
+ check = false;
+ }
+
+ }
+ if (check && key.equals(PdfName.COLORSPACE) && !value.isName()) {
+ PdfName cs = writer.getColorspaceName();
+ PageResources prs = getPageResources();
+ prs.addColor(cs, writer.addToBody(value).getIndirectReference());
+ value = cs;
+ }
+ value.toPdf(null, content);
+ content.append('\n');
+ }
+ content.append("ID\n");
+ pimage.writeContent(content);
+ content.append("\nEI\nQ").append_i(separator);
+ }
+ else {
+ PdfName name;
+ PageResources prs = getPageResources();
+ Image maskImage = image.getImageMask();
+ if (maskImage != null) {
+ name = writer.addDirectImageSimple(maskImage);
+ prs.addXObject(name, writer.getImageReference(name));
+ }
+ name = writer.addDirectImageSimple(image);
+ name = prs.addXObject(name, writer.getImageReference(name));
+ content.append(' ').append(name.getBytes()).append(" Do Q").append_i(separator);
+ }
+ }
+ if (image.hasBorders()) {
+ saveState();
+ float w = image.width();
+ float h = image.height();
+ concatCTM(a / w, b / w, c / h, d / h, e, f);
+ rectangle(image);
+ restoreState();
+ }
+ if (image.getLayer() != null)
+ endLayer();
+ Annotation annot = image.annotation();
+ if (annot == null)
+ return;
+ float[] r = new float[unitRect.length];
+ for (int k = 0; k < unitRect.length; k += 2) {
+ r[k] = a * unitRect[k] + c * unitRect[k + 1] + e;
+ r[k + 1] = b * unitRect[k] + d * unitRect[k + 1] + f;
+ }
+ float llx = r[0];
+ float lly = r[1];
+ float urx = llx;
+ float ury = lly;
+ for (int k = 2; k < r.length; k += 2) {
+ llx = Math.min(llx, r[k]);
+ lly = Math.min(lly, r[k + 1]);
+ urx = Math.max(urx, r[k]);
+ ury = Math.max(ury, r[k + 1]);
+ }
+ annot = new Annotation(annot);
+ annot.setDimensions(llx, lly, urx, ury);
+ PdfAnnotation an = PdfDocument.convertAnnotation(writer, annot);
+ if (an == null)
+ return;
+ addAnnotation(an);
+ }
+ catch (Exception ee) {
+ throw new DocumentException(ee);
+ }
+ }
+
+ /**
+ * Makes this PdfContentByte
empty.
+ */
+ public void reset() {
+ content.reset();
+ stateList.clear();
+ state = new GraphicState();
+ }
+
+ /**
+ * Starts the writing of text.
+ */
+ public void beginText() {
+ state.xTLM = 0;
+ state.yTLM = 0;
+ content.append("BT").append_i(separator);
+ }
+
+ /**
+ * Ends the writing of text and makes the current font invalid.
+ */
+ public void endText() {
+ content.append("ET").append_i(separator);
+ }
+
+ /**
+ * Saves the graphic state. saveState
and
+ * restoreState
must be balanced.
+ */
+ public void saveState() {
+ content.append("q").append_i(separator);
+ stateList.add(new GraphicState(state));
+ }
+
+ /**
+ * Restores the graphic state. saveState
and
+ * restoreState
must be balanced.
+ */
+ public void restoreState() {
+ content.append("Q").append_i(separator);
+ int idx = stateList.size() - 1;
+ if (idx < 0)
+ throw new RuntimeException("Unbalanced save/restore state operators.");
+ state = (GraphicState)stateList.get(idx);
+ stateList.remove(idx);
+ }
+
+ /**
+ * Sets the character spacing parameter.
+ *
+ * @param charSpace a parameter
+ */
+ public void setCharacterSpacing(float charSpace) {
+ state.charSpace = charSpace;
+ content.append(charSpace).append(" Tc").append_i(separator);
+ }
+
+ /**
+ * Sets the word spacing parameter.
+ *
+ * @param wordSpace a parameter
+ */
+ public void setWordSpacing(float wordSpace) {
+ state.wordSpace = wordSpace;
+ content.append(wordSpace).append(" Tw").append_i(separator);
+ }
+
+ /**
+ * Sets the horizontal scaling parameter.
+ *
+ * @param scale a parameter
+ */
+ public void setHorizontalScaling(float scale) {
+ state.scale = scale;
+ content.append(scale).append(" Tz").append_i(separator);
+ }
+
+ /**
+ * Sets the text leading parameter.
+ * text
+ * converted to bytes according to the font's encoding.
+ *
+ * @param text the text to write
+ */
+ private void showText2(String text) {
+ if (state.fontDetails == null)
+ throw new NullPointerException("Font and size must be set before writing any text");
+ byte b[] = state.fontDetails.convertToBytes(text);
+ escapeString(b, content);
+ }
+
+ /**
+ * Shows the text
.
+ *
+ * @param text the text to write
+ */
+ public void showText(String text) {
+ showText2(text);
+ content.append("Tj").append_i(separator);
+ }
+
+ /**
+ * Constructs a kern array for a text in a certain font
+ * @param text the text
+ * @param font the font
+ * @return a PdfTextArray
+ */
+ public static PdfTextArray getKernArray(String text, BaseFont font) {
+ PdfTextArray pa = new PdfTextArray();
+ StringBuffer acc = new StringBuffer();
+ int len = text.length() - 1;
+ char c[] = text.toCharArray();
+ if (len >= 0)
+ acc.append(c, 0, 1);
+ for (int k = 0; k < len; ++k) {
+ char c2 = c[k + 1];
+ int kern = font.getKerning(c[k], c2);
+ if (kern == 0) {
+ acc.append(c2);
+ }
+ else {
+ pa.add(acc.toString());
+ acc.setLength(0);
+ acc.append(c, k + 1, 1);
+ pa.add(-kern);
+ }
+ }
+ pa.add(acc.toString());
+ return pa;
+ }
+
+ /**
+ * Shows the text
kerned.
+ *
+ * @param text the text to write
+ */
+ public void showTextKerned(String text) {
+ if (state.fontDetails == null)
+ throw new NullPointerException("Font and size must be set before writing any text");
+ BaseFont bf = state.fontDetails.getBaseFont();
+ if (bf.hasKernPairs())
+ showText(getKernArray(text, bf));
+ else
+ showText(text);
+ }
+
+ /**
+ * Moves to the next line and shows text
.
+ *
+ * @param text the text to write
+ */
+ public void newlineShowText(String text) {
+ state.yTLM -= state.leading;
+ showText2(text);
+ content.append("'").append_i(separator);
+ }
+
+ /**
+ * Moves to the next line and shows text string, using the given values of the character and word spacing parameters.
+ *
+ * @param wordSpacing a parameter
+ * @param charSpacing a parameter
+ * @param text the text to write
+ */
+ public void newlineShowText(float wordSpacing, float charSpacing, String text) {
+ state.yTLM -= state.leading;
+ content.append(wordSpacing).append(' ').append(charSpacing);
+ showText2(text);
+ content.append("\"").append_i(separator);
+
+ // The " operator sets charSpace and wordSpace into graphics state
+ // (cfr PDF reference v1.6, table 5.6)
+ state.charSpace = charSpacing;
+ state.wordSpace = wordSpacing;
+ }
+
+ /**
+ * Changes the text matrix.
+ * byte
array according to the PDF conventions.
+ *
+ * @param b the byte
array to escape
+ * @return an escaped byte
array
+ */
+ static byte[] escapeString(byte b[]) {
+ ByteBuffer content = new ByteBuffer();
+ escapeString(b, content);
+ return content.toByteArray();
+ }
+
+ /**
+ * Escapes a byte
array according to the PDF conventions.
+ *
+ * @param b the byte
array to escape
+ * @param content the content
+ */
+ static void escapeString(byte b[], ByteBuffer content) {
+ content.append_i('(');
+ for (int k = 0; k < b.length; ++k) {
+ byte c = b[k];
+ switch ((int)c) {
+ case '\r':
+ content.append("\\r");
+ break;
+ case '\n':
+ content.append("\\n");
+ break;
+ case '\t':
+ content.append("\\t");
+ break;
+ case '\b':
+ content.append("\\b");
+ break;
+ case '\f':
+ content.append("\\f");
+ break;
+ case '(':
+ case ')':
+ case '\\':
+ content.append_i('\\').append_i(c);
+ break;
+ default:
+ content.append_i(c);
+ }
+ }
+ content.append(")");
+ }
+
+ /**
+ * Adds an outline to the document.
+ *
+ * @param outline the outline
+ * @deprecated not needed anymore. The outlines are extracted
+ * from the root outline
+ */
+ public void addOutline(PdfOutline outline) {
+ // for compatibility
+ }
+ /**
+ * Adds a named outline to the document.
+ *
+ * @param outline the outline
+ * @param name the name for the local destination
+ */
+ public void addOutline(PdfOutline outline, String name) {
+ checkWriter();
+ pdf.addOutline(outline, name);
+ }
+ /**
+ * Gets the root outline.
+ *
+ * @return the root outline
+ */
+ public PdfOutline getRootOutline() {
+ checkWriter();
+ return pdf.getRootOutline();
+ }
+
+ /**
+ * Computes the width of the given string taking in account
+ * the current values of "Character spacing", "Word Spacing"
+ * and "Horizontal Scaling".
+ * The additional spacing is not computed for the last character
+ * of the string.
+ * @param text the string to get width of
+ * @param kerned the kerning option
+ * @return the width
+ */
+
+ public float getEffectiveStringWidth(String text, boolean kerned) {
+ BaseFont bf = state.fontDetails.getBaseFont();
+
+ float w;
+ if (kerned)
+ w = bf.getWidthPointKerned(text, state.size);
+ else
+ w = bf.getWidthPoint(text, state.size);
+
+ if (state.charSpace != 0.0f && text.length() > 1) {
+ w += state.charSpace * (text.length() -1);
+ }
+
+ int ft = bf.getFontType();
+ if (state.wordSpace != 0.0f && (ft == BaseFont.FONT_TYPE_T1 || ft == BaseFont.FONT_TYPE_TT || ft == BaseFont.FONT_TYPE_T3)) {
+ for (int i = 0; i < (text.length() -1); i++) {
+ if (text.charAt(i) == ' ')
+ w += state.wordSpace;
+ }
+ }
+ if (state.scale != 100.0)
+ w = (w * state.scale) / 100.0f;
+
+ //System.out.println("String width = " + Float.toString(w));
+ return w;
+ }
+
+ /**
+ * Shows text right, left or center aligned with rotation.
+ * @param alignment the alignment can be ALIGN_CENTER, ALIGN_RIGHT or ALIGN_LEFT
+ * @param text the text to show
+ * @param x the x pivot position
+ * @param y the y pivot position
+ * @param rotation the rotation to be applied in degrees counterclockwise
+ */
+ public void showTextAligned(int alignment, String text, float x, float y, float rotation) {
+ showTextAligned(alignment, text, x, y, rotation, false);
+ }
+
+ private void showTextAligned(int alignment, String text, float x, float y, float rotation, boolean kerned) {
+ if (state.fontDetails == null)
+ throw new NullPointerException("Font and size must be set before writing any text");
+ if (rotation == 0) {
+ switch (alignment) {
+ case ALIGN_CENTER:
+ x -= getEffectiveStringWidth(text, kerned) / 2;
+ break;
+ case ALIGN_RIGHT:
+ x -= getEffectiveStringWidth(text, kerned);
+ break;
+ }
+ setTextMatrix(x, y);
+ if (kerned)
+ showTextKerned(text);
+ else
+ showText(text);
+ }
+ else {
+ double alpha = rotation * Math.PI / 180.0;
+ float cos = (float)Math.cos(alpha);
+ float sin = (float)Math.sin(alpha);
+ float len;
+ switch (alignment) {
+ case ALIGN_CENTER:
+ len = getEffectiveStringWidth(text, kerned) / 2;
+ x -= len * cos;
+ y -= len * sin;
+ break;
+ case ALIGN_RIGHT:
+ len = getEffectiveStringWidth(text, kerned);
+ x -= len * cos;
+ y -= len * sin;
+ break;
+ }
+ setTextMatrix(cos, sin, -sin, cos, x, y);
+ if (kerned)
+ showTextKerned(text);
+ else
+ showText(text);
+ setTextMatrix(0f, 0f);
+ }
+ }
+
+ /**
+ * Shows text kerned right, left or center aligned with rotation.
+ * @param alignment the alignment can be ALIGN_CENTER, ALIGN_RIGHT or ALIGN_LEFT
+ * @param text the text to show
+ * @param x the x pivot position
+ * @param y the y pivot position
+ * @param rotation the rotation to be applied in degrees counterclockwise
+ */
+ public void showTextAlignedKerned(int alignment, String text, float x, float y, float rotation) {
+ showTextAligned(alignment, text, x, y, rotation, true);
+ }
+
+ /**
+ * Concatenate a matrix to the current transformation matrix.
+ * @param a an element of the transformation matrix
+ * @param b an element of the transformation matrix
+ * @param c an element of the transformation matrix
+ * @param d an element of the transformation matrix
+ * @param e an element of the transformation matrix
+ * @param f an element of the transformation matrix
+ **/
+ public void concatCTM(float a, float b, float c, float d, float e, float f) {
+ content.append(a).append(' ').append(b).append(' ').append(c).append(' ');
+ content.append(d).append(' ').append(e).append(' ').append(f).append(" cm").append_i(separator);
+ }
+
+ /**
+ * Generates an array of bezier curves to draw an arc.
+ * PdfPatternPainter
where the pattern will be created
+ */
+ public PdfPatternPainter createPattern(float width, float height, float xstep, float ystep) {
+ checkWriter();
+ if ( xstep == 0.0f || ystep == 0.0f )
+ throw new RuntimeException("XStep or YStep can not be ZERO.");
+ PdfPatternPainter painter = new PdfPatternPainter(writer);
+ painter.setWidth(width);
+ painter.setHeight(height);
+ painter.setXStep(xstep);
+ painter.setYStep(ystep);
+ writer.addSimplePattern(painter);
+ return painter;
+ }
+
+ /**
+ * Create a new colored tiling pattern. Variables xstep and ystep are set to the same values
+ * of width and height.
+ * @param width the width of the pattern
+ * @param height the height of the pattern
+ * @return the PdfPatternPainter
where the pattern will be created
+ */
+ public PdfPatternPainter createPattern(float width, float height) {
+ return createPattern(width, height, width, height);
+ }
+
+ /**
+ * Create a new uncolored tiling pattern.
+ *
+ * @param width the width of the pattern
+ * @param height the height of the pattern
+ * @param xstep the desired horizontal spacing between pattern cells.
+ * May be either positive or negative, but not zero.
+ * @param ystep the desired vertical spacing between pattern cells.
+ * May be either positive or negative, but not zero.
+ * @param color the default color. Can be null
+ * @return the PdfPatternPainter
where the pattern will be created
+ */
+ public PdfPatternPainter createPattern(float width, float height, float xstep, float ystep, Color color) {
+ checkWriter();
+ if ( xstep == 0.0f || ystep == 0.0f )
+ throw new RuntimeException("XStep or YStep can not be ZERO.");
+ PdfPatternPainter painter = new PdfPatternPainter(writer, color);
+ painter.setWidth(width);
+ painter.setHeight(height);
+ painter.setXStep(xstep);
+ painter.setYStep(ystep);
+ writer.addSimplePattern(painter);
+ return painter;
+ }
+
+ /**
+ * Create a new uncolored tiling pattern.
+ * Variables xstep and ystep are set to the same values
+ * of width and height.
+ * @param width the width of the pattern
+ * @param height the height of the pattern
+ * @param color the default color. Can be null
+ * @return the PdfPatternPainter
where the pattern will be created
+ */
+ public PdfPatternPainter createPattern(float width, float height, Color color) {
+ return createPattern(width, height, width, height, color);
+ }
+
+ /**
+ * Creates a new template.
+ * PdfContentByte
or in another template. Templates are only written
+ * to the output when the document is closed permitting things like showing text in the first page
+ * that is only defined in the last page.
+ *
+ * @param width the bounding box width
+ * @param height the bounding box height
+ * @return the templated created
+ */
+ public PdfTemplate createTemplate(float width, float height) {
+ return createTemplate(width, height, null);
+ }
+
+ PdfTemplate createTemplate(float width, float height, PdfName forcedName) {
+ checkWriter();
+ PdfTemplate template = new PdfTemplate(writer);
+ template.setWidth(width);
+ template.setHeight(height);
+ writer.addDirectTemplateSimple(template, forcedName);
+ return template;
+ }
+
+ /**
+ * Creates a new appearance to be used with form fields.
+ *
+ * @param width the bounding box width
+ * @param height the bounding box height
+ * @return the appearance created
+ */
+ public PdfAppearance createAppearance(float width, float height) {
+ return createAppearance(width, height, null);
+ }
+
+ PdfAppearance createAppearance(float width, float height, PdfName forcedName) {
+ checkWriter();
+ PdfAppearance template = new PdfAppearance(writer);
+ template.setWidth(width);
+ template.setHeight(height);
+ writer.addDirectTemplateSimple(template, forcedName);
+ return template;
+ }
+
+ /**
+ * Adds a PostScript XObject to this content.
+ *
+ * @param psobject the object
+ */
+ public void addPSXObject(PdfPSXObject psobject) {
+ checkWriter();
+ PdfName name = writer.addDirectTemplateSimple(psobject, null);
+ PageResources prs = getPageResources();
+ name = prs.addXObject(name, psobject.getIndirectReference());
+ content.append(name.getBytes()).append(" Do").append_i(separator);
+ }
+
+ /**
+ * Adds a template to this content.
+ *
+ * @param template the template
+ * @param a an element of the transformation matrix
+ * @param b an element of the transformation matrix
+ * @param c an element of the transformation matrix
+ * @param d an element of the transformation matrix
+ * @param e an element of the transformation matrix
+ * @param f an element of the transformation matrix
+ */
+ public void addTemplate(PdfTemplate template, float a, float b, float c, float d, float e, float f) {
+ checkWriter();
+ checkNoPattern(template);
+ PdfName name = writer.addDirectTemplateSimple(template, null);
+ PageResources prs = getPageResources();
+ name = prs.addXObject(name, template.getIndirectReference());
+ content.append("q ");
+ content.append(a).append(' ');
+ content.append(b).append(' ');
+ content.append(c).append(' ');
+ content.append(d).append(' ');
+ content.append(e).append(' ');
+ content.append(f).append(" cm ");
+ content.append(name.getBytes()).append(" Do Q").append_i(separator);
+ }
+
+ /**
+ * Adds a template to this content.
+ *
+ * @param template the template
+ * @param x the x location of this template
+ * @param y the y location of this template
+ */
+ public void addTemplate(PdfTemplate template, float x, float y) {
+ addTemplate(template, 1, 0, 0, 1, x, y);
+ }
+
+ /**
+ * Changes the current color for filling paths (device dependent colors!).
+ * color
can be an
+ * ExtendedColor
.
+ * @param color the color
+ */
+ public void setColorStroke(Color color) {
+ PdfWriter.checkPDFXConformance(writer, PdfWriter.PDFXKEY_COLOR, color);
+ int type = ExtendedColor.getType(color);
+ switch (type) {
+ case ExtendedColor.TYPE_GRAY: {
+ setGrayStroke(((GrayColor)color).getGray());
+ break;
+ }
+ case ExtendedColor.TYPE_CMYK: {
+ CMYKColor cmyk = (CMYKColor)color;
+ setCMYKColorStrokeF(cmyk.getCyan(), cmyk.getMagenta(), cmyk.getYellow(), cmyk.getBlack());
+ break;
+ }
+ case ExtendedColor.TYPE_SEPARATION: {
+ SpotColor spot = (SpotColor)color;
+ setColorStroke(spot.getPdfSpotColor(), spot.getTint());
+ break;
+ }
+ case ExtendedColor.TYPE_PATTERN: {
+ PatternColor pat = (PatternColor) color;
+ setPatternStroke(pat.getPainter());
+ break;
+ }
+ case ExtendedColor.TYPE_SHADING: {
+ ShadingColor shading = (ShadingColor) color;
+ setShadingStroke(shading.getPdfShadingPattern());
+ break;
+ }
+ default:
+ setRGBColorStroke(color.getRed(), color.getGreen(), color.getBlue());
+ }
+ }
+
+ /** Sets the fill color. color
can be an
+ * ExtendedColor
.
+ * @param color the color
+ */
+ public void setColorFill(Color color) {
+ PdfWriter.checkPDFXConformance(writer, PdfWriter.PDFXKEY_COLOR, color);
+ int type = ExtendedColor.getType(color);
+ switch (type) {
+ case ExtendedColor.TYPE_GRAY: {
+ setGrayFill(((GrayColor)color).getGray());
+ break;
+ }
+ case ExtendedColor.TYPE_CMYK: {
+ CMYKColor cmyk = (CMYKColor)color;
+ setCMYKColorFillF(cmyk.getCyan(), cmyk.getMagenta(), cmyk.getYellow(), cmyk.getBlack());
+ break;
+ }
+ case ExtendedColor.TYPE_SEPARATION: {
+ SpotColor spot = (SpotColor)color;
+ setColorFill(spot.getPdfSpotColor(), spot.getTint());
+ break;
+ }
+ case ExtendedColor.TYPE_PATTERN: {
+ PatternColor pat = (PatternColor) color;
+ setPatternFill(pat.getPainter());
+ break;
+ }
+ case ExtendedColor.TYPE_SHADING: {
+ ShadingColor shading = (ShadingColor) color;
+ setShadingFill(shading.getPdfShadingPattern());
+ break;
+ }
+ default:
+ setRGBColorFill(color.getRed(), color.getGreen(), color.getBlue());
+ }
+ }
+
+ /** Sets the fill color to a spot color.
+ * @param sp the spot color
+ * @param tint the tint for the spot color. 0 is no color and 1
+ * is 100% color
+ */
+ public void setColorFill(PdfSpotColor sp, float tint) {
+ checkWriter();
+ state.colorDetails = writer.addSimple(sp);
+ PageResources prs = getPageResources();
+ PdfName name = state.colorDetails.getColorName();
+ name = prs.addColor(name, state.colorDetails.getIndirectReference());
+ content.append(name.getBytes()).append(" cs ").append(tint).append(" scn").append_i(separator);
+ }
+
+ /** Sets the stroke color to a spot color.
+ * @param sp the spot color
+ * @param tint the tint for the spot color. 0 is no color and 1
+ * is 100% color
+ */
+ public void setColorStroke(PdfSpotColor sp, float tint) {
+ checkWriter();
+ state.colorDetails = writer.addSimple(sp);
+ PageResources prs = getPageResources();
+ PdfName name = state.colorDetails.getColorName();
+ name = prs.addColor(name, state.colorDetails.getIndirectReference());
+ content.append(name.getBytes()).append(" CS ").append(tint).append(" SCN").append_i(separator);
+ }
+
+ /** Sets the fill color to a pattern. The pattern can be
+ * colored or uncolored.
+ * @param p the pattern
+ */
+ public void setPatternFill(PdfPatternPainter p) {
+ if (p.isStencil()) {
+ setPatternFill(p, p.getDefaultColor());
+ return;
+ }
+ checkWriter();
+ PageResources prs = getPageResources();
+ PdfName name = writer.addSimplePattern(p);
+ name = prs.addPattern(name, p.getIndirectReference());
+ content.append(PdfName.PATTERN.getBytes()).append(" cs ").append(name.getBytes()).append(" scn").append_i(separator);
+ }
+
+ /** Outputs the color values to the content.
+ * @param color The color
+ * @param tint the tint if it is a spot color, ignored otherwise
+ */
+ void outputColorNumbers(Color color, float tint) {
+ PdfWriter.checkPDFXConformance(writer, PdfWriter.PDFXKEY_COLOR, color);
+ int type = ExtendedColor.getType(color);
+ switch (type) {
+ case ExtendedColor.TYPE_RGB:
+ content.append((float)(color.getRed()) / 0xFF);
+ content.append(' ');
+ content.append((float)(color.getGreen()) / 0xFF);
+ content.append(' ');
+ content.append((float)(color.getBlue()) / 0xFF);
+ break;
+ case ExtendedColor.TYPE_GRAY:
+ content.append(((GrayColor)color).getGray());
+ break;
+ case ExtendedColor.TYPE_CMYK: {
+ CMYKColor cmyk = (CMYKColor)color;
+ content.append(cmyk.getCyan()).append(' ').append(cmyk.getMagenta());
+ content.append(' ').append(cmyk.getYellow()).append(' ').append(cmyk.getBlack());
+ break;
+ }
+ case ExtendedColor.TYPE_SEPARATION:
+ content.append(tint);
+ break;
+ default:
+ throw new RuntimeException("Invalid color type.");
+ }
+ }
+
+ /** Sets the fill color to an uncolored pattern.
+ * @param p the pattern
+ * @param color the color of the pattern
+ */
+ public void setPatternFill(PdfPatternPainter p, Color color) {
+ if (ExtendedColor.getType(color) == ExtendedColor.TYPE_SEPARATION)
+ setPatternFill(p, color, ((SpotColor)color).getTint());
+ else
+ setPatternFill(p, color, 0);
+ }
+
+ /** Sets the fill color to an uncolored pattern.
+ * @param p the pattern
+ * @param color the color of the pattern
+ * @param tint the tint if the color is a spot color, ignored otherwise
+ */
+ public void setPatternFill(PdfPatternPainter p, Color color, float tint) {
+ checkWriter();
+ if (!p.isStencil())
+ throw new RuntimeException("An uncolored pattern was expected.");
+ PageResources prs = getPageResources();
+ PdfName name = writer.addSimplePattern(p);
+ name = prs.addPattern(name, p.getIndirectReference());
+ ColorDetails csDetail = writer.addSimplePatternColorspace(color);
+ PdfName cName = prs.addColor(csDetail.getColorName(), csDetail.getIndirectReference());
+ content.append(cName.getBytes()).append(" cs").append_i(separator);
+ outputColorNumbers(color, tint);
+ content.append(' ').append(name.getBytes()).append(" scn").append_i(separator);
+ }
+
+ /** Sets the stroke color to an uncolored pattern.
+ * @param p the pattern
+ * @param color the color of the pattern
+ */
+ public void setPatternStroke(PdfPatternPainter p, Color color) {
+ if (ExtendedColor.getType(color) == ExtendedColor.TYPE_SEPARATION)
+ setPatternStroke(p, color, ((SpotColor)color).getTint());
+ else
+ setPatternStroke(p, color, 0);
+ }
+
+ /** Sets the stroke color to an uncolored pattern.
+ * @param p the pattern
+ * @param color the color of the pattern
+ * @param tint the tint if the color is a spot color, ignored otherwise
+ */
+ public void setPatternStroke(PdfPatternPainter p, Color color, float tint) {
+ checkWriter();
+ if (!p.isStencil())
+ throw new RuntimeException("An uncolored pattern was expected.");
+ PageResources prs = getPageResources();
+ PdfName name = writer.addSimplePattern(p);
+ name = prs.addPattern(name, p.getIndirectReference());
+ ColorDetails csDetail = writer.addSimplePatternColorspace(color);
+ PdfName cName = prs.addColor(csDetail.getColorName(), csDetail.getIndirectReference());
+ content.append(cName.getBytes()).append(" CS").append_i(separator);
+ outputColorNumbers(color, tint);
+ content.append(' ').append(name.getBytes()).append(" SCN").append_i(separator);
+ }
+
+ /** Sets the stroke color to a pattern. The pattern can be
+ * colored or uncolored.
+ * @param p the pattern
+ */
+ public void setPatternStroke(PdfPatternPainter p) {
+ if (p.isStencil()) {
+ setPatternStroke(p, p.getDefaultColor());
+ return;
+ }
+ checkWriter();
+ PageResources prs = getPageResources();
+ PdfName name = writer.addSimplePattern(p);
+ name = prs.addPattern(name, p.getIndirectReference());
+ content.append(PdfName.PATTERN.getBytes()).append(" CS ").append(name.getBytes()).append(" SCN").append_i(separator);
+ }
+
+ /**
+ * Paints using a shading object.
+ * @param shading the shading object
+ */
+ public void paintShading(PdfShading shading) {
+ writer.addSimpleShading(shading);
+ PageResources prs = getPageResources();
+ PdfName name = prs.addShading(shading.getShadingName(), shading.getShadingReference());
+ content.append(name.getBytes()).append(" sh").append_i(separator);
+ ColorDetails details = shading.getColorDetails();
+ if (details != null)
+ prs.addColor(details.getColorName(), details.getIndirectReference());
+ }
+
+ /**
+ * Paints using a shading pattern.
+ * @param shading the shading pattern
+ */
+ public void paintShading(PdfShadingPattern shading) {
+ paintShading(shading.getShading());
+ }
+
+ /**
+ * Sets the shading fill pattern.
+ * @param shading the shading pattern
+ */
+ public void setShadingFill(PdfShadingPattern shading) {
+ writer.addSimpleShadingPattern(shading);
+ PageResources prs = getPageResources();
+ PdfName name = prs.addPattern(shading.getPatternName(), shading.getPatternReference());
+ content.append(PdfName.PATTERN.getBytes()).append(" cs ").append(name.getBytes()).append(" scn").append_i(separator);
+ ColorDetails details = shading.getColorDetails();
+ if (details != null)
+ prs.addColor(details.getColorName(), details.getIndirectReference());
+ }
+
+ /**
+ * Sets the shading stroke pattern
+ * @param shading the shading pattern
+ */
+ public void setShadingStroke(PdfShadingPattern shading) {
+ writer.addSimpleShadingPattern(shading);
+ PageResources prs = getPageResources();
+ PdfName name = prs.addPattern(shading.getPatternName(), shading.getPatternReference());
+ content.append(PdfName.PATTERN.getBytes()).append(" CS ").append(name.getBytes()).append(" SCN").append_i(separator);
+ ColorDetails details = shading.getColorDetails();
+ if (details != null)
+ prs.addColor(details.getColorName(), details.getIndirectReference());
+ }
+
+ /** Check if we have a valid PdfWriter.
+ *
+ */
+ protected void checkWriter() {
+ if (writer == null)
+ throw new NullPointerException("The writer in PdfContentByte is null.");
+ }
+
+ /**
+ * Show an array of text.
+ * @param text array of text
+ */
+ public void showText(PdfTextArray text) {
+ if (state.fontDetails == null)
+ throw new NullPointerException("Font and size must be set before writing any text");
+ content.append("[");
+ ArrayList arrayList = text.getArrayList();
+ boolean lastWasNumber = false;
+ for (int k = 0; k < arrayList.size(); ++k) {
+ Object obj = arrayList.get(k);
+ if (obj instanceof String) {
+ showText2((String)obj);
+ lastWasNumber = false;
+ }
+ else {
+ if (lastWasNumber)
+ content.append(' ');
+ else
+ lastWasNumber = true;
+ content.append(((Float)obj).floatValue());
+ }
+ }
+ content.append("]TJ").append_i(separator);
+ }
+
+ /**
+ * Gets the PdfWriter
in use by this object.
+ * @return the PdfWriter
in use by this object
+ */
+ public PdfWriter getPdfWriter() {
+ return writer;
+ }
+
+ /**
+ * Gets the PdfDocument
in use by this object.
+ * @return the PdfDocument
in use by this object
+ */
+ public PdfDocument getPdfDocument() {
+ return pdf;
+ }
+
+ /**
+ * Implements a link to other part of the document. The jump will
+ * be made to a local destination with the same name, that must exist.
+ * @param name the name for this link
+ * @param llx the lower left x corner of the activation area
+ * @param lly the lower left y corner of the activation area
+ * @param urx the upper right x corner of the activation area
+ * @param ury the upper right y corner of the activation area
+ */
+ public void localGoto(String name, float llx, float lly, float urx, float ury) {
+ pdf.localGoto(name, llx, lly, urx, ury);
+ }
+
+ /**
+ * The local destination to where a local goto with the same
+ * name will jump.
+ * @param name the name of this local destination
+ * @param destination the PdfDestination
with the jump coordinates
+ * @return true
if the local destination was added,
+ * false
if a local destination with the same name
+ * already exists
+ */
+ public boolean localDestination(String name, PdfDestination destination) {
+ return pdf.localDestination(name, destination);
+ }
+
+ /**
+ * Gets a duplicate of this PdfContentByte
. All
+ * the members are copied by reference but the buffer stays different.
+ *
+ * @return a copy of this PdfContentByte
+ */
+ public PdfContentByte getDuplicate() {
+ return new PdfContentByte(writer);
+ }
+
+ /**
+ * Implements a link to another document.
+ * @param filename the filename for the remote document
+ * @param name the name to jump to
+ * @param llx the lower left x corner of the activation area
+ * @param lly the lower left y corner of the activation area
+ * @param urx the upper right x corner of the activation area
+ * @param ury the upper right y corner of the activation area
+ */
+ public void remoteGoto(String filename, String name, float llx, float lly, float urx, float ury) {
+ pdf.remoteGoto(filename, name, llx, lly, urx, ury);
+ }
+
+ /**
+ * Implements a link to another document.
+ * @param filename the filename for the remote document
+ * @param page the page to jump to
+ * @param llx the lower left x corner of the activation area
+ * @param lly the lower left y corner of the activation area
+ * @param urx the upper right x corner of the activation area
+ * @param ury the upper right y corner of the activation area
+ */
+ public void remoteGoto(String filename, int page, float llx, float lly, float urx, float ury) {
+ pdf.remoteGoto(filename, page, llx, lly, urx, ury);
+ }
+ /**
+ * Adds a round rectangle to the current path.
+ *
+ * @param x x-coordinate of the starting point
+ * @param y y-coordinate of the starting point
+ * @param w width
+ * @param h height
+ * @param r radius of the arc corner
+ */
+ public void roundRectangle(float x, float y, float w, float h, float r) {
+ if (w < 0) {
+ x += w;
+ w = -w;
+ }
+ if (h < 0) {
+ y += h;
+ h = -h;
+ }
+ if (r < 0)
+ r = -r;
+ float b = 0.4477f;
+ moveTo(x + r, y);
+ lineTo(x + w - r, y);
+ curveTo(x + w - r * b, y, x + w, y + r * b, x + w, y + r);
+ lineTo(x + w, y + h - r);
+ curveTo(x + w, y + h - r * b, x + w - r * b, y + h, x + w - r, y + h);
+ lineTo(x + r, y + h);
+ curveTo(x + r * b, y + h, x, y + h - r * b, x, y + h - r);
+ lineTo(x, y + r);
+ curveTo(x, y + r * b, x + r * b, y, x + r, y);
+ }
+
+ /** Implements an action in an area.
+ * @param action the PdfAction
+ * @param llx the lower left x corner of the activation area
+ * @param lly the lower left y corner of the activation area
+ * @param urx the upper right x corner of the activation area
+ * @param ury the upper right y corner of the activation area
+ */
+ public void setAction(PdfAction action, float llx, float lly, float urx, float ury) {
+ pdf.setAction(action, llx, lly, urx, ury);
+ }
+
+ /** Outputs a String
directly to the content.
+ * @param s the String
+ */
+ public void setLiteral(String s) {
+ content.append(s);
+ }
+
+ /** Outputs a char
directly to the content.
+ * @param c the char
+ */
+ public void setLiteral(char c) {
+ content.append(c);
+ }
+
+ /** Outputs a float
directly to the content.
+ * @param n the float
+ */
+ public void setLiteral(float n) {
+ content.append(n);
+ }
+
+ /** Throws an error if it is a pattern.
+ * @param t the object to check
+ */
+ void checkNoPattern(PdfTemplate t) {
+ if (t.getType() == PdfTemplate.TYPE_PATTERN)
+ throw new RuntimeException("Invalid use of a pattern. A template was expected.");
+ }
+
+ /**
+ * Draws a TextField.
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @param on
+ */
+ public void drawRadioField(float llx, float lly, float urx, float ury, boolean on) {
+ if (llx > urx) { float x = llx; llx = urx; urx = x; }
+ if (lly > ury) { float y = lly; lly = ury; ury = y; }
+ // silver circle
+ setLineWidth(1);
+ setLineCap(1);
+ setColorStroke(new Color(0xC0, 0xC0, 0xC0));
+ arc(llx + 1f, lly + 1f, urx - 1f, ury - 1f, 0f, 360f);
+ stroke();
+ // gray circle-segment
+ setLineWidth(1);
+ setLineCap(1);
+ setColorStroke(new Color(0xA0, 0xA0, 0xA0));
+ arc(llx + 0.5f, lly + 0.5f, urx - 0.5f, ury - 0.5f, 45, 180);
+ stroke();
+ // black circle-segment
+ setLineWidth(1);
+ setLineCap(1);
+ setColorStroke(new Color(0x00, 0x00, 0x00));
+ arc(llx + 1.5f, lly + 1.5f, urx - 1.5f, ury - 1.5f, 45, 180);
+ stroke();
+ if (on) {
+ // gray circle
+ setLineWidth(1);
+ setLineCap(1);
+ setColorFill(new Color(0x00, 0x00, 0x00));
+ arc(llx + 4f, lly + 4f, urx - 4f, ury - 4f, 0, 360);
+ fill();
+ }
+ }
+
+ /**
+ * Draws a TextField.
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ */
+ public void drawTextField(float llx, float lly, float urx, float ury) {
+ if (llx > urx) { float x = llx; llx = urx; urx = x; }
+ if (lly > ury) { float y = lly; lly = ury; ury = y; }
+ // silver rectangle not filled
+ setColorStroke(new Color(0xC0, 0xC0, 0xC0));
+ setLineWidth(1);
+ setLineCap(0);
+ rectangle(llx, lly, urx - llx, ury - lly);
+ stroke();
+ // white rectangle filled
+ setLineWidth(1);
+ setLineCap(0);
+ setColorFill(new Color(0xFF, 0xFF, 0xFF));
+ rectangle(llx + 0.5f, lly + 0.5f, urx - llx - 1f, ury -lly - 1f);
+ fill();
+ // silver lines
+ setColorStroke(new Color(0xC0, 0xC0, 0xC0));
+ setLineWidth(1);
+ setLineCap(0);
+ moveTo(llx + 1f, lly + 1.5f);
+ lineTo(urx - 1.5f, lly + 1.5f);
+ lineTo(urx - 1.5f, ury - 1f);
+ stroke();
+ // gray lines
+ setColorStroke(new Color(0xA0, 0xA0, 0xA0));
+ setLineWidth(1);
+ setLineCap(0);
+ moveTo(llx + 1f, lly + 1);
+ lineTo(llx + 1f, ury - 1f);
+ lineTo(urx - 1f, ury - 1f);
+ stroke();
+ // black lines
+ setColorStroke(new Color(0x00, 0x00, 0x00));
+ setLineWidth(1);
+ setLineCap(0);
+ moveTo(llx + 2f, lly + 2f);
+ lineTo(llx + 2f, ury - 2f);
+ lineTo(urx - 2f, ury - 2f);
+ stroke();
+ }
+
+ /**
+ * Draws a button.
+ * @param llx
+ * @param lly
+ * @param urx
+ * @param ury
+ * @param text
+ * @param bf
+ * @param size
+ */
+ public void drawButton(float llx, float lly, float urx, float ury, String text, BaseFont bf, float size) {
+ if (llx > urx) { float x = llx; llx = urx; urx = x; }
+ if (lly > ury) { float y = lly; lly = ury; ury = y; }
+ // black rectangle not filled
+ setColorStroke(new Color(0x00, 0x00, 0x00));
+ setLineWidth(1);
+ setLineCap(0);
+ rectangle(llx, lly, urx - llx, ury - lly);
+ stroke();
+ // silver rectangle filled
+ setLineWidth(1);
+ setLineCap(0);
+ setColorFill(new Color(0xC0, 0xC0, 0xC0));
+ rectangle(llx + 0.5f, lly + 0.5f, urx - llx - 1f, ury -lly - 1f);
+ fill();
+ // white lines
+ setColorStroke(new Color(0xFF, 0xFF, 0xFF));
+ setLineWidth(1);
+ setLineCap(0);
+ moveTo(llx + 1f, lly + 1f);
+ lineTo(llx + 1f, ury - 1f);
+ lineTo(urx - 1f, ury - 1f);
+ stroke();
+ // dark grey lines
+ setColorStroke(new Color(0xA0, 0xA0, 0xA0));
+ setLineWidth(1);
+ setLineCap(0);
+ moveTo(llx + 1f, lly + 1f);
+ lineTo(urx - 1f, lly + 1f);
+ lineTo(urx - 1f, ury - 1f);
+ stroke();
+ // text
+ resetRGBColorFill();
+ beginText();
+ setFontAndSize(bf, size);
+ showTextAligned(PdfContentByte.ALIGN_CENTER, text, llx + (urx - llx) / 2, lly + (ury - lly - size) / 2, 0);
+ endText();
+ }
+
+ /** Gets a Graphics2D
to write on. The graphics
+ * are translated to PDF commands as shapes. No PDF fonts will appear.
+ * @param width the width of the panel
+ * @param height the height of the panel
+ * @return a Graphics2D
+ */
+ public java.awt.Graphics2D createGraphicsShapes(float width, float height) {
+ return new PdfGraphics2D(this, width, height, null, true, false, 0);
+ }
+
+ /** Gets a Graphics2D
to print on. The graphics
+ * are translated to PDF commands as shapes. No PDF fonts will appear.
+ * @param width the width of the panel
+ * @param height the height of the panel
+ * @param printerJob a printer job
+ * @return a Graphics2D
+ */
+ public java.awt.Graphics2D createPrinterGraphicsShapes(float width, float height, PrinterJob printerJob) {
+ return new PdfPrinterGraphics2D(this, width, height, null, true, false, 0, printerJob);
+ }
+
+ /** Gets a Graphics2D
to write on. The graphics
+ * are translated to PDF commands.
+ * @param width the width of the panel
+ * @param height the height of the panel
+ * @return a Graphics2D
+ */
+ public java.awt.Graphics2D createGraphics(float width, float height) {
+ return new PdfGraphics2D(this, width, height, null, false, false, 0);
+ }
+
+ /** Gets a Graphics2D
to print on. The graphics
+ * are translated to PDF commands.
+ * @param width the width of the panel
+ * @param height the height of the panel
+ * @param printerJob
+ * @return a Graphics2D
+ */
+ public java.awt.Graphics2D createPrinterGraphics(float width, float height, PrinterJob printerJob) {
+ return new PdfPrinterGraphics2D(this, width, height, null, false, false, 0, printerJob);
+ }
+
+ /** Gets a Graphics2D
to write on. The graphics
+ * are translated to PDF commands.
+ * @param width the width of the panel
+ * @param height the height of the panel
+ * @param convertImagesToJPEG
+ * @param quality
+ * @return a Graphics2D
+ */
+ public java.awt.Graphics2D createGraphics(float width, float height, boolean convertImagesToJPEG, float quality) {
+ return new PdfGraphics2D(this, width, height, null, false, convertImagesToJPEG, quality);
+ }
+
+ /** Gets a Graphics2D
to print on. The graphics
+ * are translated to PDF commands.
+ * @param width the width of the panel
+ * @param height the height of the panel
+ * @param convertImagesToJPEG
+ * @param quality
+ * @param printerJob
+ * @return a Graphics2D
+ */
+ public java.awt.Graphics2D createPrinterGraphics(float width, float height, boolean convertImagesToJPEG, float quality, PrinterJob printerJob) {
+ return new PdfPrinterGraphics2D(this, width, height, null, false, convertImagesToJPEG, quality, printerJob);
+ }
+
+ /** Gets a Graphics2D
to print on. The graphics
+ * are translated to PDF commands.
+ * @param width
+ * @param height
+ * @param convertImagesToJPEG
+ * @param quality
+ * @return A Graphics2D object
+ */
+ public java.awt.Graphics2D createGraphicsShapes(float width, float height, boolean convertImagesToJPEG, float quality) {
+ return new PdfGraphics2D(this, width, height, null, true, convertImagesToJPEG, quality);
+ }
+
+ /** Gets a Graphics2D
to print on. The graphics
+ * are translated to PDF commands.
+ * @param width
+ * @param height
+ * @param convertImagesToJPEG
+ * @param quality
+ * @param printerJob
+ * @return a Graphics2D object
+ */
+ public java.awt.Graphics2D createPrinterGraphicsShapes(float width, float height, boolean convertImagesToJPEG, float quality, PrinterJob printerJob) {
+ return new PdfPrinterGraphics2D(this, width, height, null, true, convertImagesToJPEG, quality, printerJob);
+ }
+
+ /** Gets a Graphics2D
to write on. The graphics
+ * are translated to PDF commands.
+ * @param width the width of the panel
+ * @param height the height of the panel
+ * @param fontMapper the mapping from awt fonts to BaseFont
+ * @return a Graphics2D
+ */
+ public java.awt.Graphics2D createGraphics(float width, float height, FontMapper fontMapper) {
+ return new PdfGraphics2D(this, width, height, fontMapper, false, false, 0);
+ }
+
+ /** Gets a Graphics2D
to print on. The graphics
+ * are translated to PDF commands.
+ * @param width the width of the panel
+ * @param height the height of the panel
+ * @param fontMapper the mapping from awt fonts to BaseFont
+ * @param printerJob a printer job
+ * @return a Graphics2D
+ */
+ public java.awt.Graphics2D createPrinterGraphics(float width, float height, FontMapper fontMapper, PrinterJob printerJob) {
+ return new PdfPrinterGraphics2D(this, width, height, fontMapper, false, false, 0, printerJob);
+ }
+
+ /** Gets a Graphics2D
to write on. The graphics
+ * are translated to PDF commands.
+ * @param width the width of the panel
+ * @param height the height of the panel
+ * @param fontMapper the mapping from awt fonts to BaseFont
+ * @param convertImagesToJPEG converts awt images to jpeg before inserting in pdf
+ * @param quality the quality of the jpeg
+ * @return a Graphics2D
+ */
+ public java.awt.Graphics2D createGraphics(float width, float height, FontMapper fontMapper, boolean convertImagesToJPEG, float quality) {
+ return new PdfGraphics2D(this, width, height, fontMapper, false, convertImagesToJPEG, quality);
+ }
+
+ /** Gets a Graphics2D
to print on. The graphics
+ * are translated to PDF commands.
+ * @param width the width of the panel
+ * @param height the height of the panel
+ * @param fontMapper the mapping from awt fonts to BaseFont
+ * @param convertImagesToJPEG converts awt images to jpeg before inserting in pdf
+ * @param quality the quality of the jpeg
+ * @param printerJob a printer job
+ * @return a Graphics2D
+ */
+ public java.awt.Graphics2D createPrinterGraphics(float width, float height, FontMapper fontMapper, boolean convertImagesToJPEG, float quality, PrinterJob printerJob) {
+ return new PdfPrinterGraphics2D(this, width, height, fontMapper, false, convertImagesToJPEG, quality, printerJob);
+ }
+
+ PageResources getPageResources() {
+ return pdf.getPageResources();
+ }
+
+ /** Sets the graphic state
+ * @param gstate the graphic state
+ */
+ public void setGState(PdfGState gstate) {
+ PdfObject obj[] = writer.addSimpleExtGState(gstate);
+ PageResources prs = getPageResources();
+ PdfName name = prs.addExtGState((PdfName)obj[0], (PdfIndirectReference)obj[1]);
+ content.append(name.getBytes()).append(" gs").append_i(separator);
+ }
+
+ /**
+ * Begins a graphic block whose visibility is controled by the layer
.
+ * Blocks can be nested. Each block must be terminated by an {@link #endLayer()}.PdfName.DEFAULTGRAY
, PdfName.DEFAULTRGB
+ * or PdfName.DEFAULTCMYK
+ * @param obj the colorspace. A null
or PdfNull
removes any colorspace with the same name
+ */
+ public void setDefaultColorspace(PdfName name, PdfObject obj) {
+ PageResources prs = getPageResources();
+ prs.addDefaultColor(name, obj);
+ }
+
+ /**
+ * Begins a marked content sequence. This sequence will be tagged with the structure struc
.
+ * The same structure can be used several times to connect text that belongs to the same logical segment
+ * but is in a different location, like the same paragraph crossing to another page, for example.
+ * @param struc the tagging structure
+ */
+ public void beginMarkedContentSequence(PdfStructureElement struc) {
+ PdfObject obj = struc.get(PdfName.K);
+ int mark = pdf.getMarkPoint();
+ if (obj != null) {
+ PdfArray ar = null;
+ if (obj.isNumber()) {
+ ar = new PdfArray();
+ ar.add(obj);
+ struc.put(PdfName.K, ar);
+ }
+ else if (obj.isArray()) {
+ ar = (PdfArray)obj;
+ if (!((PdfObject)ar.getArrayList().get(0)).isNumber())
+ throw new IllegalArgumentException("The structure has kids.");
+ }
+ else
+ throw new IllegalArgumentException("Unknown object at /K " + obj.getClass().toString());
+ PdfDictionary dic = new PdfDictionary(PdfName.MCR);
+ dic.put(PdfName.PG, writer.getCurrentPage());
+ dic.put(PdfName.MCID, new PdfNumber(mark));
+ ar.add(dic);
+ struc.setPageMark(writer.getPageNumber() - 1, -1);
+ }
+ else {
+ struc.setPageMark(writer.getPageNumber() - 1, mark);
+ struc.put(PdfName.PG, writer.getCurrentPage());
+ }
+ pdf.incMarkPoint();
+ content.append(struc.get(PdfName.S).getBytes()).append(" <> BDC").append_i(separator);
+ }
+
+ /**
+ * Ends a marked content sequence
+ */
+ public void endMarkedContentSequence() {
+ content.append("EMC").append_i(separator);
+ }
+
+ /**
+ * Begins a marked content sequence. If property is null
the mark will be of the type
+ * BMC
otherwise it will be BDC
.
+ * @param tag the tag
+ * @param property the property
+ * @param inline true
to include the property in the content or false
+ * to include the property in the resource dictionary with the possibility of reusing
+ */
+ public void beginMarkedContentSequence(PdfName tag, PdfDictionary property, boolean inline) {
+ if (property == null) {
+ content.append(tag.getBytes()).append(" BMC").append_i(separator);
+ return;
+ }
+ content.append(tag.getBytes()).append(' ');
+ if (inline)
+ try {
+ property.toPdf(writer, content);
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ else {
+ PdfObject[] objs;
+ if (writer.propertyExists(property))
+ objs = writer.addSimpleProperty(property, null);
+ else
+ objs = writer.addSimpleProperty(property, writer.getPdfIndirectReference());
+ PdfName name = (PdfName)objs[0];
+ PageResources prs = getPageResources();
+ name = prs.addProperty(name, (PdfIndirectReference)objs[1]);
+ content.append(name.getBytes());
+ }
+ content.append(" BDC").append_i(separator);
+ }
+
+ /**
+ * This is just a shorthand to beginMarkedContentSequence(tag, null, false)
.
+ * @param tag the tag
+ */
+ public void beginMarkedContentSequence(PdfName tag) {
+ beginMarkedContentSequence(tag, null, false);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfContentParser.java b/src/main/java/com/lowagie/text/pdf/PdfContentParser.java
new file mode 100644
index 0000000..af731bc
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfContentParser.java
@@ -0,0 +1,204 @@
+/*
+ * $Id: PdfContentParser.java,v 1.4 2006/05/27 11:11:54 psoares33 Exp $
+ *
+ * Copyright 2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+import java.util.ArrayList;
+/**
+ * Parses the page or template content.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfContentParser {
+
+ /**
+ * Commands have this type.
+ */
+ public static final int COMMAND_TYPE = 200;
+ /**
+ * Holds value of property tokeniser.
+ */
+ private PRTokeniser tokeniser;
+
+ /**
+ * Creates a new instance of PdfContentParser
+ * @param tokeniser the tokeniser with the content
+ */
+ public PdfContentParser(PRTokeniser tokeniser) {
+ this.tokeniser = tokeniser;
+ }
+
+ /**
+ * Parses a single command from the content. Each command is output as an array of arguments
+ * having the command itself as the last element. The returned array will be empty if the
+ * end of content was reached.
+ * @param ls an ArrayList
to use. It will be cleared before using. If it's
+ * null
will create a new ArrayList
+ * @return the same ArrayList
given as argument or a new one
+ * @throws IOException on error
+ */
+ public ArrayList parse(ArrayList ls) throws IOException {
+ if (ls == null)
+ ls = new ArrayList();
+ else
+ ls.clear();
+ PdfObject ob = null;
+ while ((ob = readPRObject()) != null) {
+ ls.add(ob);
+ if (ob.type() == COMMAND_TYPE)
+ break;
+ }
+ return ls;
+ }
+
+ /**
+ * Gets the tokeniser.
+ * @return the tokeniser.
+ */
+ public PRTokeniser getTokeniser() {
+ return this.tokeniser;
+ }
+
+ /**
+ * Sets the tokeniser.
+ * @param tokeniser the tokeniser
+ */
+ public void setTokeniser(PRTokeniser tokeniser) {
+ this.tokeniser = tokeniser;
+ }
+
+ /**
+ * Reads a dictionary. The tokeniser must be positioned past the "<<" token.
+ * @return the dictionary
+ * @throws IOException on error
+ */
+ public PdfDictionary readDictionary() throws IOException {
+ PdfDictionary dic = new PdfDictionary();
+ while (true) {
+ if (!nextValidToken())
+ throw new IOException("Unexpected end of file.");;
+ if (tokeniser.getTokenType() == PRTokeniser.TK_END_DIC)
+ break;
+ if (tokeniser.getTokenType() != PRTokeniser.TK_NAME)
+ throw new IOException("Dictionary key is not a name.");
+ PdfName name = new PdfName(tokeniser.getStringValue(), false);
+ PdfObject obj = readPRObject();
+ int type = obj.type();
+ if (-type == PRTokeniser.TK_END_DIC)
+ throw new IOException("Unexpected '>>'");
+ if (-type == PRTokeniser.TK_END_ARRAY)
+ throw new IOException("Unexpected ']'");
+ dic.put(name, obj);
+ }
+ return dic;
+ }
+
+ /**
+ * Reads an array. The tokeniser must be positioned past the "[" token.
+ * @return an array
+ * @throws IOException on error
+ */
+ public PdfArray readArray() throws IOException {
+ PdfArray array = new PdfArray();
+ while (true) {
+ PdfObject obj = readPRObject();
+ int type = obj.type();
+ if (-type == PRTokeniser.TK_END_ARRAY)
+ break;
+ if (-type == PRTokeniser.TK_END_DIC)
+ throw new IOException("Unexpected '>>'");
+ array.add(obj);
+ }
+ return array;
+ }
+
+ /**
+ * Reads a pdf object.
+ * @return the pdf object
+ * @throws IOException on error
+ */
+ public PdfObject readPRObject() throws IOException {
+ if (!nextValidToken())
+ return null;
+ int type = tokeniser.getTokenType();
+ switch (type) {
+ case PRTokeniser.TK_START_DIC: {
+ PdfDictionary dic = readDictionary();
+ return dic;
+ }
+ case PRTokeniser.TK_START_ARRAY:
+ return readArray();
+ case PRTokeniser.TK_STRING:
+ PdfString str = new PdfString(tokeniser.getStringValue(), null).setHexWriting(tokeniser.isHexString());
+ return str;
+ case PRTokeniser.TK_NAME:
+ return new PdfName(tokeniser.getStringValue(), false);
+ case PRTokeniser.TK_NUMBER:
+ return new PdfNumber(tokeniser.getStringValue());
+ case PRTokeniser.TK_OTHER:
+ return new PdfLiteral(COMMAND_TYPE, tokeniser.getStringValue());
+ default:
+ return new PdfLiteral(-type, tokeniser.getStringValue());
+ }
+ }
+
+ /**
+ * Reads the next token skipping over the comments.
+ * @return true
if a token was read, false
if the end of content was reached
+ * @throws IOException on error
+ */
+ public boolean nextValidToken() throws IOException {
+ while (tokeniser.nextToken()) {
+ if (tokeniser.getTokenType() == PRTokeniser.TK_COMMENT)
+ continue;
+ return true;
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfContents.java b/src/main/java/com/lowagie/text/pdf/PdfContents.java
new file mode 100644
index 0000000..786da2f
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfContents.java
@@ -0,0 +1,147 @@
+/*
+ * $Id: PdfContents.java,v 1.58 2005/05/04 14:31:50 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.DocWriter;
+import com.lowagie.text.Document;
+import com.lowagie.text.Rectangle;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.util.zip.DeflaterOutputStream;
+
+/**
+ * PdfContents
is a PdfStream
containing the contents (text + graphics) of a PdfPage
.
+ */
+
+class PdfContents extends PdfStream {
+
+ static final byte SAVESTATE[] = DocWriter.getISOBytes("q\n");
+ static final byte RESTORESTATE[] = DocWriter.getISOBytes("Q\n");
+ static final byte ROTATE90[] = DocWriter.getISOBytes("0 1 -1 0 ");
+ static final byte ROTATE180[] = DocWriter.getISOBytes("-1 0 0 -1 ");
+ static final byte ROTATE270[] = DocWriter.getISOBytes("0 -1 1 0 ");
+ static final byte ROTATEFINAL[] = DocWriter.getISOBytes(" cm\n");
+ // constructor
+
+/**
+ * Constructs a PdfContents
-object, containing text and general graphics.
+ *
+ * @param under the direct content that is under all others
+ * @param content the graphics in a page
+ * @param text the text in a page
+ * @param secondContent the direct content that is over all others
+ * @throws BadPdfFormatException on error
+ */
+
+ PdfContents(PdfContentByte under, PdfContentByte content, PdfContentByte text, PdfContentByte secondContent, Rectangle page) throws BadPdfFormatException {
+ super();
+ try {
+ OutputStream out = null;
+ streamBytes = new ByteArrayOutputStream();
+ if (Document.compress)
+ {
+ compressed = true;
+ out = new DeflaterOutputStream(streamBytes);
+ }
+ else
+ out = streamBytes;
+ int rotation = page.getRotation();
+ switch (rotation) {
+ case 90:
+ out.write(ROTATE90);
+ out.write(DocWriter.getISOBytes(ByteBuffer.formatDouble(page.top())));
+ out.write(' ');
+ out.write('0');
+ out.write(ROTATEFINAL);
+ break;
+ case 180:
+ out.write(ROTATE180);
+ out.write(DocWriter.getISOBytes(ByteBuffer.formatDouble(page.right())));
+ out.write(' ');
+ out.write(DocWriter.getISOBytes(ByteBuffer.formatDouble(page.top())));
+ out.write(ROTATEFINAL);
+ break;
+ case 270:
+ out.write(ROTATE270);
+ out.write('0');
+ out.write(' ');
+ out.write(DocWriter.getISOBytes(ByteBuffer.formatDouble(page.right())));
+ out.write(ROTATEFINAL);
+ break;
+ }
+ if (under.size() > 0) {
+ out.write(SAVESTATE);
+ under.getInternalBuffer().writeTo(out);
+ out.write(RESTORESTATE);
+ }
+ if (content.size() > 0) {
+ out.write(SAVESTATE);
+ content.getInternalBuffer().writeTo(out);
+ out.write(RESTORESTATE);
+ }
+ if (text != null) {
+ out.write(SAVESTATE);
+ text.getInternalBuffer().writeTo(out);
+ out.write(RESTORESTATE);
+ }
+ if (secondContent.size() > 0) {
+ secondContent.getInternalBuffer().writeTo(out);
+ }
+ out.close();
+ }
+ catch (Exception e) {
+ throw new BadPdfFormatException(e.getMessage());
+ }
+ put(PdfName.LENGTH, new PdfNumber(streamBytes.size()));
+ if (compressed)
+ put(PdfName.FILTER, PdfName.FLATEDECODE);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfCopy.java b/src/main/java/com/lowagie/text/pdf/PdfCopy.java
new file mode 100644
index 0000000..0dcdc70
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfCopy.java
@@ -0,0 +1,474 @@
+/*
+ * $Id: PdfCopy.java,v 1.42 2006/05/06 14:19:51 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * This module by Mark Thompson. Copyright (C) 2002 Mark Thompson
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+import java.io.*;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+
+/**
+ * Make copies of PDF documents. Documents can be edited after reading and
+ * before writing them out.
+ * @author Mark Thompson
+ */
+
+public class PdfCopy extends PdfWriter {
+ /**
+ * This class holds information about indirect references, since they are
+ * renumbered by iText.
+ */
+ static class IndirectReferences {
+ PdfIndirectReference theRef;
+ boolean hasCopied;
+ IndirectReferences(PdfIndirectReference ref) {
+ theRef = ref;
+ hasCopied = false;
+ }
+ void setCopied() { hasCopied = true; }
+ boolean getCopied() { return hasCopied; }
+ PdfIndirectReference getRef() { return theRef; }
+ };
+ protected HashMap indirects;
+ protected HashMap indirectMap;
+ protected int currentObjectNum = 1;
+ protected PdfReader reader;
+ protected PdfIndirectReference acroForm;
+ protected PdfIndirectReference topPageParent;
+ protected ArrayList pageNumbersToRefs = new ArrayList();
+ protected List newBookmarks;
+
+ /**
+ * A key to allow us to hash indirect references
+ */
+ protected static class RefKey {
+ int num;
+ int gen;
+ RefKey(int num, int gen) {
+ this.num = num;
+ this.gen = gen;
+ }
+ RefKey(PdfIndirectReference ref) {
+ num = ref.getNumber();
+ gen = ref.getGeneration();
+ }
+ RefKey(PRIndirectReference ref) {
+ num = ref.getNumber();
+ gen = ref.getGeneration();
+ }
+ public int hashCode() {
+ return (gen<<16)+num;
+ }
+ public boolean equals(Object o) {
+ RefKey other = (RefKey)o;
+ return this.gen == other.gen && this.num == other.num;
+ }
+ public String toString() {
+ return "" + num + " " + gen;
+ }
+ }
+
+ /**
+ * Constructor
+ * @param document
+ * @param os outputstream
+ */
+ public PdfCopy(Document document, OutputStream os) throws DocumentException {
+ super(new PdfDocument(), os);
+ document.addDocListener(pdf);
+ pdf.addWriter(this);
+ indirectMap = new HashMap();
+ }
+ public void open() {
+ super.open();
+ topPageParent = getPdfIndirectReference();
+ root.setLinearMode(topPageParent);
+ }
+
+ /**
+ * Grabs a page from the input document
+ * @param reader the reader of the document
+ * @param pageNumber which page to get
+ * @return the page
+ */
+ public PdfImportedPage getImportedPage(PdfReader reader, int pageNumber) {
+ if (currentPdfReaderInstance != null) {
+ if (currentPdfReaderInstance.getReader() != reader) {
+ try {
+ currentPdfReaderInstance.getReader().close();
+ currentPdfReaderInstance.getReaderFile().close();
+ }
+ catch (IOException ioe) {
+ // empty on purpose
+ }
+ currentPdfReaderInstance = reader.getPdfReaderInstance(this);
+ }
+ }
+ else {
+ currentPdfReaderInstance = reader.getPdfReaderInstance(this);
+ }
+ return currentPdfReaderInstance.getImportedPage(pageNumber);
+ }
+
+
+ /**
+ * Translate a PRIndirectReference to a PdfIndirectReference
+ * In addition, translates the object numbers, and copies the
+ * referenced object to the output file.
+ * NB: PRIndirectReferences (and PRIndirectObjects) really need to know what
+ * file they came from, because each file has its own namespace. The translation
+ * we do from their namespace to ours is *at best* heuristic, and guaranteed to
+ * fail under some circumstances.
+ */
+ protected PdfIndirectReference copyIndirect(PRIndirectReference in) throws IOException, BadPdfFormatException {
+ PdfIndirectReference theRef;
+ RefKey key = new RefKey(in);
+ IndirectReferences iRef = (IndirectReferences)indirects.get(key);
+ if (iRef != null) {
+ theRef = iRef.getRef();
+ if (iRef.getCopied()) {
+ // System.out.println(">>> Value is " + theRef.toString());
+ return theRef;
+ }
+ // System.out.println(">>> Fill in " + theRef.toString());
+ }
+ else {
+ theRef = body.getPdfIndirectReference();
+ iRef = new IndirectReferences(theRef);
+ indirects.put(key, iRef);
+ }
+ iRef.setCopied();
+ PdfObject obj = copyObject(PdfReader.getPdfObjectRelease(in));
+ addToBody(obj, theRef);
+ return theRef;
+ }
+
+ /**
+ * Translate a PRDictionary to a PdfDictionary. Also translate all of the
+ * objects contained in it.
+ */
+ protected PdfDictionary copyDictionary(PdfDictionary in)
+ throws IOException, BadPdfFormatException {
+ PdfDictionary out = new PdfDictionary();
+ PdfName type = (PdfName)in.get(PdfName.TYPE);
+
+ for (Iterator it = in.getKeys().iterator(); it.hasNext();) {
+ PdfName key = (PdfName)it.next();
+ PdfObject value = in.get(key);
+ // System.out.println("Copy " + key);
+ if (type != null && PdfName.PAGE.equals(type)) {
+ if (key.equals(PdfName.PARENT))
+ out.put(PdfName.PARENT, topPageParent);
+ else if (!key.equals(PdfName.B))
+ out.put(key, copyObject(value));
+ }
+ else
+ out.put(key, copyObject(value));
+ }
+ return out;
+ }
+
+ /**
+ * Translate a PRStream to a PdfStream. The data part copies itself.
+ */
+ protected PdfStream copyStream(PRStream in) throws IOException, BadPdfFormatException {
+ PRStream out = new PRStream(in, null);
+
+ for (Iterator it = in.getKeys().iterator(); it.hasNext();) {
+ PdfName key = (PdfName) it.next();
+ PdfObject value = in.get(key);
+ out.put(key, copyObject(value));
+ }
+
+ return out;
+ }
+
+
+ /**
+ * Translate a PRArray to a PdfArray. Also translate all of the objects contained
+ * in it
+ */
+ protected PdfArray copyArray(PdfArray in) throws IOException, BadPdfFormatException {
+ PdfArray out = new PdfArray();
+
+ for (Iterator i = in.getArrayList().iterator(); i.hasNext();) {
+ PdfObject value = (PdfObject)i.next();
+ out.add(copyObject(value));
+ }
+ return out;
+ }
+
+ /**
+ * Translate a PR-object to a Pdf-object
+ */
+ protected PdfObject copyObject(PdfObject in) throws IOException,BadPdfFormatException {
+ switch (in.type) {
+ case PdfObject.DICTIONARY:
+ // System.out.println("Dictionary: " + in.toString());
+ return copyDictionary((PdfDictionary)in);
+ case PdfObject.INDIRECT:
+ return copyIndirect((PRIndirectReference)in);
+ case PdfObject.ARRAY:
+ return copyArray((PdfArray)in);
+ case PdfObject.NUMBER:
+ case PdfObject.NAME:
+ case PdfObject.STRING:
+ case PdfObject.NULL:
+ case PdfObject.BOOLEAN:
+ return in;
+ case PdfObject.STREAM:
+ return copyStream((PRStream)in);
+ // return in;
+ default:
+ if (in.type < 0) {
+ String lit = ((PdfLiteral)in).toString();
+ if (lit.equals("true") || lit.equals("false")) {
+ return new PdfBoolean(lit);
+ }
+ return new PdfLiteral(lit);
+ }
+ System.out.println("CANNOT COPY type " + in.type);
+ return null;
+ }
+ }
+
+ /**
+ * convenience method. Given an importedpage, set our "globals"
+ */
+ protected int setFromIPage(PdfImportedPage iPage) {
+ int pageNum = iPage.getPageNumber();
+ PdfReaderInstance inst = currentPdfReaderInstance = iPage.getPdfReaderInstance();
+ reader = inst.getReader();
+ setFromReader(reader);
+ return pageNum;
+ }
+
+ /**
+ * convenience method. Given a reader, set our "globals"
+ */
+ protected void setFromReader(PdfReader reader) {
+ this.reader = reader;
+ indirects = (HashMap)indirectMap.get(reader);
+ if (indirects == null) {
+ indirects = new HashMap();
+ indirectMap.put(reader,indirects);
+ PdfDictionary catalog = reader.getCatalog();
+ PRIndirectReference ref = (PRIndirectReference)catalog.get(PdfName.PAGES);
+ indirects.put(new RefKey(ref), new IndirectReferences(topPageParent));
+ ref = null;
+ PdfObject o = catalog.get(PdfName.ACROFORM);
+ if (o == null || o.type() != PdfObject.INDIRECT)
+ return;
+ ref = (PRIndirectReference)o;
+ if (acroForm == null) acroForm = body.getPdfIndirectReference();
+ indirects.put(new RefKey(ref), new IndirectReferences(acroForm));
+ }
+ }
+ /**
+ * Add an imported page to our output
+ * @param iPage an imported page
+ * @throws IOException, BadPdfFormatException
+ */
+ public void addPage(PdfImportedPage iPage) throws IOException, BadPdfFormatException {
+ int pageNum = setFromIPage(iPage);
+
+ PdfDictionary thePage = reader.getPageN(pageNum);
+ PRIndirectReference origRef = reader.getPageOrigRef(pageNum);
+ reader.releasePage(pageNum);
+ RefKey key = new RefKey(origRef);
+ PdfIndirectReference pageRef;
+ IndirectReferences iRef = (IndirectReferences)indirects.get(key);
+ // if we already have an iref for the page (we got here by another link)
+ if (iRef != null) {
+ pageRef = iRef.getRef();
+ }
+ else {
+ pageRef = body.getPdfIndirectReference();
+ iRef = new IndirectReferences(pageRef);
+ indirects.put(key, iRef);
+ }
+ pageReferences.add(pageRef);
+ ++currentPageNumber;
+ if (! iRef.getCopied()) {
+ iRef.setCopied();
+ PdfDictionary newPage = copyDictionary(thePage);
+ newPage.put(PdfName.PARENT, topPageParent);
+ addToBody(newPage, pageRef);
+ }
+ root.addPage(pageRef);
+ pageNumbersToRefs.add(pageRef);
+ }
+
+ public PdfIndirectReference getPageReference(int page) {
+ if (page < 0 || page > pageNumbersToRefs.size())
+ throw new IllegalArgumentException("Invalid page number " + page);
+ return (PdfIndirectReference)pageNumbersToRefs.get(page - 1);
+ }
+
+ /**
+ * Copy the acroform for an input document. Note that you can only have one,
+ * we make no effort to merge them.
+ * @param reader The reader of the input file that is being copied
+ * @throws IOException, BadPdfFormatException
+ */
+ public void copyAcroForm(PdfReader reader) throws IOException, BadPdfFormatException {
+ setFromReader(reader);
+
+ PdfDictionary catalog = reader.getCatalog();
+ PRIndirectReference hisRef = null;
+ PdfObject o = catalog.get(PdfName.ACROFORM);
+ if (o != null && o.type() == PdfObject.INDIRECT)
+ hisRef = (PRIndirectReference)o;
+ if (hisRef == null) return; // bugfix by John Englar
+ RefKey key = new RefKey(hisRef);
+ PdfIndirectReference myRef;
+ IndirectReferences iRef = (IndirectReferences)indirects.get(key);
+ if (iRef != null) {
+ acroForm = myRef = iRef.getRef();
+ }
+ else {
+ acroForm = myRef = body.getPdfIndirectReference();
+ iRef = new IndirectReferences(myRef);
+ indirects.put(key, iRef);
+ }
+ if (! iRef.getCopied()) {
+ iRef.setCopied();
+ PdfDictionary theForm = copyDictionary((PdfDictionary)PdfReader.getPdfObject(hisRef));
+ addToBody(theForm, myRef);
+ }
+ }
+
+ /*
+ * the getCatalog method is part of PdfWriter.
+ * we wrap this so that we can extend it
+ */
+ protected PdfDictionary getCatalog(PdfIndirectReference rootObj) {
+ try {
+ PdfDictionary theCat = ((PdfDocument)document).getCatalog(rootObj);
+ if (acroForm != null) theCat.put(PdfName.ACROFORM, acroForm);
+ if (newBookmarks == null || newBookmarks.size() == 0)
+ return theCat;
+ PdfDictionary top = new PdfDictionary();
+ PdfIndirectReference topRef = getPdfIndirectReference();
+ Object kids[] = SimpleBookmark.iterateOutlines(this, topRef, newBookmarks, false);
+ top.put(PdfName.FIRST, (PdfIndirectReference)kids[0]);
+ top.put(PdfName.LAST, (PdfIndirectReference)kids[1]);
+ top.put(PdfName.COUNT, new PdfNumber(((Integer)kids[2]).intValue()));
+ addToBody(top, topRef);
+ theCat.put(PdfName.OUTLINES, topRef);
+ return theCat;
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Sets the bookmarks. The list structure is defined in
+ * SimpleBookmark#
.
+ * @param outlines the bookmarks or null
to remove any
+ */
+ public void setOutlines(List outlines) {
+ newBookmarks = outlines;
+ }
+
+ /**
+ * Signals that the Document
was closed and that no other
+ * Elements
will be added.
+ * List
of Integer
. The page ordering can be changed but
+ * no page repetitions are allowed.
+ * @param reader the PDF document
+ * @param pagesToKeep the pages to keep
+ * @throws DocumentException on error
+ */
+ public void addDocument(PdfReader reader, List pagesToKeep) throws DocumentException {
+ fc.addDocument(reader, pagesToKeep);
+ }
+
+ /**
+ * Concatenates a PDF document selecting the pages to keep. The pages are described as
+ * ranges. The page ordering can be changed but
+ * no page repetitions are allowed.
+ * @param reader the PDF document
+ * @param ranges the comma separated ranges as described in {@link SequenceList}
+ * @throws DocumentException on error
+ */
+ public void addDocument(PdfReader reader, String ranges) throws DocumentException {
+ fc.addDocument(reader, SequenceList.expand(ranges, reader.getNumberOfPages()));
+ }
+
+ /** Sets the encryption options for this document. The userPassword and the
+ * ownerPassword can be null or have zero length. In this case the ownerPassword
+ * is replaced by a random string. The open permissions for the document can be
+ * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
+ * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
+ * The permissions can be combined by ORing them.
+ * @param userPassword the user password. Can be null or empty
+ * @param ownerPassword the owner password. Can be null or empty
+ * @param permissions the user permissions
+ * @param strength128Bits true
for 128 bit key length, false
for 40 bit key length
+ * @throws DocumentException if the document is already open
+ */
+ public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, boolean strength128Bits) throws DocumentException {
+ fc.setEncryption(userPassword, ownerPassword, permissions, strength128Bits);
+ }
+
+ /**
+ * Sets the encryption options for this document. The userPassword and the
+ * ownerPassword can be null or have zero length. In this case the ownerPassword
+ * is replaced by a random string. The open permissions for the document can be
+ * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
+ * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
+ * The permissions can be combined by ORing them.
+ * @param strength true for 128 bit key length. false for 40 bit key length
+ * @param userPassword the user password. Can be null or empty
+ * @param ownerPassword the owner password. Can be null or empty
+ * @param permissions the user permissions
+ * @throws DocumentException if the document is already open
+ */
+ public void setEncryption(boolean strength, String userPassword, String ownerPassword, int permissions) throws DocumentException {
+ setEncryption(DocWriter.getISOBytes(userPassword), DocWriter.getISOBytes(ownerPassword), permissions, strength);
+ }
+
+ /**
+ * Closes the output document.
+ */
+ public void close() {
+ fc.close();
+ }
+
+ /**
+ * Opens the document. This is usually not needed as addDocument() will do it
+ * automatically.
+ */
+ public void open() {
+ fc.openDoc();
+ }
+
+ /**
+ * Adds JavaScript to the global document
+ * @param js the JavaScript
+ */
+ public void addJavaScript(String js) {
+ fc.addJavaScript(js, !PdfEncodings.isPdfDocEncoding(js));
+ }
+
+ /**
+ * Sets the bookmarks. The list structure is defined in
+ * SimpleBookmark#
.
+ * @param outlines the bookmarks or null
to remove any
+ */
+ public void setOutlines(List outlines) {
+ fc.setOutlines(outlines);
+ }
+
+ /** Gets the underlying PdfWriter.
+ * @return the underlying PdfWriter
+ */
+ public PdfWriter getWriter() {
+ return fc;
+ }
+
+ /**
+ * Gets the 1.5 compression status.
+ * @return true
if the 1.5 compression is on
+ */
+ public boolean isFullCompression() {
+ return fc.isFullCompression();
+ }
+
+ /**
+ * Sets the document's compression to the new 1.5 mode with object streams and xref
+ * streams. It can be set at any time but once set it can't be unset.
+ * SimpleBookmark#
.
+ * @param outlines the bookmarks or null
to remove any
+ */
+ public void setOutlines(List outlines) {
+ newBookmarks = outlines;
+ }
+
+ public void openDoc() {
+ if (!nd.isOpen())
+ nd.open();
+ }
+
+ protected static final HashMap widgetKeys = new HashMap();
+ protected static final HashMap fieldKeys = new HashMap();
+ static {
+ Integer one = new Integer(1);
+ widgetKeys.put(PdfName.SUBTYPE, one);
+ widgetKeys.put(PdfName.CONTENTS, one);
+ widgetKeys.put(PdfName.RECT, one);
+ widgetKeys.put(PdfName.NM, one);
+ widgetKeys.put(PdfName.M, one);
+ widgetKeys.put(PdfName.F, one);
+ widgetKeys.put(PdfName.BS, one);
+ widgetKeys.put(PdfName.BORDER, one);
+ widgetKeys.put(PdfName.AP, one);
+ widgetKeys.put(PdfName.AS, one);
+ widgetKeys.put(PdfName.C, one);
+ widgetKeys.put(PdfName.A, one);
+ widgetKeys.put(PdfName.STRUCTPARENT, one);
+ widgetKeys.put(PdfName.OC, one);
+ widgetKeys.put(PdfName.H, one);
+ widgetKeys.put(PdfName.MK, one);
+ widgetKeys.put(PdfName.DA, one);
+ widgetKeys.put(PdfName.Q, one);
+ fieldKeys.put(PdfName.AA, one);
+ fieldKeys.put(PdfName.FT, one);
+ fieldKeys.put(PdfName.TU, one);
+ fieldKeys.put(PdfName.TM, one);
+ fieldKeys.put(PdfName.FF, one);
+ fieldKeys.put(PdfName.V, one);
+ fieldKeys.put(PdfName.DV, one);
+ fieldKeys.put(PdfName.DS, one);
+ fieldKeys.put(PdfName.RV, one);
+ fieldKeys.put(PdfName.OPT, one);
+ fieldKeys.put(PdfName.MAXLEN, one);
+ fieldKeys.put(PdfName.TI, one);
+ fieldKeys.put(PdfName.I, one);
+ fieldKeys.put(PdfName.LOCK, one);
+ fieldKeys.put(PdfName.SV, one);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfDashPattern.java b/src/main/java/com/lowagie/text/pdf/PdfDashPattern.java
new file mode 100644
index 0000000..f5f00f9
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfDashPattern.java
@@ -0,0 +1,144 @@
+/*
+ * $Id: PdfDashPattern.java,v 1.57 2005/05/04 14:33:09 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A PdfDashPattern
defines a dash pattern as described in
+ * the PDF Reference Manual version 1.3 p 325 (section 8.4.3).
+ *
+ * @see PdfArray
+ */
+
+public class PdfDashPattern extends PdfArray {
+
+ // membervariables
+
+/** This is the length of a dash. */
+ private float dash = -1;
+
+/** This is the length of a gap. */
+ private float gap = -1;
+
+/** This is the phase. */
+ private float phase = -1;
+
+ // constructors
+
+/**
+ * Constructs a new PdfDashPattern
.
+ */
+
+ public PdfDashPattern() {
+ super();
+ }
+
+/**
+ * Constructs a new PdfDashPattern
.
+ */
+
+ public PdfDashPattern(float dash) {
+ super(new PdfNumber(dash));
+ this.dash = dash;
+ }
+
+/**
+ * Constructs a new PdfDashPattern
.
+ */
+
+ public PdfDashPattern(float dash, float gap) {
+ super(new PdfNumber(dash));
+ add(new PdfNumber(gap));
+ this.dash = dash;
+ this.gap = gap;
+ }
+
+/**
+ * Constructs a new PdfDashPattern
.
+ */
+
+ public PdfDashPattern(float dash, float gap, float phase) {
+ super(new PdfNumber(dash));
+ add(new PdfNumber(gap));
+ this.dash = dash;
+ this.gap = gap;
+ this.phase = phase;
+ }
+
+ public void add(float n) {
+ add(new PdfNumber(n));
+ }
+
+/**
+ * Returns the PDF representation of this PdfArray
.
+ *
+ * @return an array of byte
s
+ */
+
+ public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
+ os.write('[');
+
+ if (dash >= 0) {
+ new PdfNumber(dash).toPdf(writer, os);
+ if (gap >= 0) {
+ os.write(' ');
+ new PdfNumber(gap).toPdf(writer, os);
+ }
+ }
+ os.write(']');
+ if (phase >=0) {
+ os.write(' ');
+ new PdfNumber(phase).toPdf(writer, os);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfDate.java b/src/main/java/com/lowagie/text/pdf/PdfDate.java
new file mode 100644
index 0000000..5c26880
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfDate.java
@@ -0,0 +1,210 @@
+/*
+ * $Id: PdfDate.java,v 1.63 2005/09/04 16:20:01 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.text.SimpleDateFormat;
+import java.util.GregorianCalendar;
+import java.util.Calendar;
+import java.util.SimpleTimeZone;
+
+/**
+ * PdfDate
is the PDF date object.
+ * PdfString
of the form:
+ *
+ * (D: YYYYMMDDHHmmSSOHH'mm')
+ *
PdfDate
-object.
+ *
+ * @param d the date that has to be turned into a PdfDate
-object
+ */
+
+ public PdfDate(Calendar d) {
+ super();
+ StringBuffer date = new StringBuffer("D:");
+ date.append(setLength(d.get(Calendar.YEAR), 4));
+ date.append(setLength(d.get(Calendar.MONTH) + 1, 2));
+ date.append(setLength(d.get(Calendar.DATE), 2));
+ date.append(setLength(d.get(Calendar.HOUR_OF_DAY), 2));
+ date.append(setLength(d.get(Calendar.MINUTE), 2));
+ date.append(setLength(d.get(Calendar.SECOND), 2));
+ int timezone = (d.get(Calendar.ZONE_OFFSET) + d.get(Calendar.DST_OFFSET)) / (60 * 60 * 1000);
+ if (timezone == 0) {
+ date.append("Z");
+ }
+ else if (timezone < 0) {
+ date.append("-");
+ timezone = -timezone;
+ }
+ else {
+ date.append("+");
+ }
+ if (timezone != 0) {
+ date.append(setLength(timezone, 2)).append("'");
+ int zone = Math.abs((d.get(Calendar.ZONE_OFFSET) + d.get(Calendar.DST_OFFSET)) / (60 * 1000)) - (timezone * 60);
+ date.append(setLength(zone, 2)).append("'");
+ }
+ value = date.toString();
+ }
+
+/**
+ * Constructs a PdfDate
-object, representing the current day and time.
+ */
+
+ public PdfDate() {
+ this(new GregorianCalendar());
+ }
+
+/**
+ * Adds a number of leading zeros to a given String
in order to get a String
+ * of a certain length.
+ *
+ * @param i a given number
+ * @param length the length of the resulting String
+ * @return the resulting String
+ */
+
+ private String setLength(int i, int length) { // 1.3-1.4 problem fixed by Finn Bock
+ StringBuffer tmp = new StringBuffer();
+ tmp.append(i);
+ while (tmp.length() < length) {
+ tmp.insert(0, "0");
+ }
+ tmp.setLength(length);
+ return tmp.toString();
+ }
+
+ /**
+ * Gives the W3C format of the PdfDate.
+ * @return a formatted date
+ */
+ public String getW3CDate() {
+ return getW3CDate(value);
+ }
+
+ /**
+ * Gives the W3C format of the PdfDate.
+ * @param d
+ * @return a formatted date
+ */
+ public static String getW3CDate(String d) {
+ SimpleDateFormat w3c = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+ Calendar c = decode(d);
+ return w3c.format(c.getTime());
+ }
+
+ /**
+ * Converts a PDF string representing a date into a Calendar.
+ * @param s the PDF string representing a date
+ * @return a Calendar
representing the date or null
if the string
+ * was not a date
+ */
+ public static Calendar decode(String s) {
+ try {
+ if (s.startsWith("D:"))
+ s = s.substring(2);
+ GregorianCalendar calendar;
+ int slen = s.length();
+ int idx = s.indexOf('Z');
+ if (idx >= 0) {
+ slen = idx;
+ calendar = new GregorianCalendar(new SimpleTimeZone(0, "ZPDF"));
+ }
+ else {
+ int sign = 1;
+ idx = s.indexOf('+');
+ if (idx < 0) {
+ idx = s.indexOf('-');
+ if (idx >= 0)
+ sign = -1;
+ }
+ if (idx < 0)
+ calendar = new GregorianCalendar();
+ else {
+ int offset = Integer.parseInt(s.substring(idx + 1, idx + 3)) * 60;
+ if (idx + 5 < s.length())
+ offset += Integer.parseInt(s.substring(idx + 4, idx + 6));
+ calendar = new GregorianCalendar(new SimpleTimeZone(offset * sign * 60000, "ZPDF"));
+ slen = idx;
+ }
+ }
+ calendar.clear();
+ idx = 0;
+ for (int k = 0; k < dateSpace.length; k += 3) {
+ if (idx >= slen)
+ break;
+ calendar.set(dateSpace[k], Integer.parseInt(s.substring(idx, idx + dateSpace[k + 1])) + dateSpace[k + 2]);
+ idx += dateSpace[k + 1];
+ }
+ return calendar;
+ }
+ catch (Exception e) {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfDestination.java b/src/main/java/com/lowagie/text/pdf/PdfDestination.java
new file mode 100644
index 0000000..6ab6598
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfDestination.java
@@ -0,0 +1,221 @@
+/*
+ * $Id: PdfDestination.java,v 1.54 2005/05/04 14:33:11 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * A PdfColor
defines a Color (it's a PdfArray
containing 3 values).
+ *
+ * @see PdfDictionary
+ */
+
+public class PdfDestination extends PdfArray {
+
+ // public static final member-variables
+
+/** This is a possible destination type */
+ public static final int XYZ = 0;
+
+/** This is a possible destination type */
+ public static final int FIT = 1;
+
+/** This is a possible destination type */
+ public static final int FITH = 2;
+
+/** This is a possible destination type */
+ public static final int FITV = 3;
+
+/** This is a possible destination type */
+ public static final int FITR = 4;
+
+/** This is a possible destination type */
+ public static final int FITB = 5;
+
+/** This is a possible destination type */
+ public static final int FITBH = 6;
+
+/** This is a possible destination type */
+ public static final int FITBV = 7;
+
+ // member variables
+
+/** Is the indirect reference to a page already added? */
+ private boolean status = false;
+
+ // constructors
+
+/**
+ * Constructs a new PdfDestination
.
+ * PdfDestination
.
+ * PdfDestination
.
+ * PdfDestination
.
+ * true
or false
+ */
+
+ public boolean hasPage() {
+ return status;
+ }
+
+/** Adds the indirect reference of the destination page.
+ *
+ * @param page an indirect reference
+ * @return true if the page reference was added
+ */
+
+ public boolean addPage(PdfIndirectReference page) {
+ if (!status) {
+ addFirst(page);
+ status = true;
+ return true;
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfDictionary.java b/src/main/java/com/lowagie/text/pdf/PdfDictionary.java
new file mode 100644
index 0000000..f7310e4
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfDictionary.java
@@ -0,0 +1,339 @@
+/*
+ * $Id: PdfDictionary.java,v 1.62 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * PdfDictionary
is the Pdf dictionary object.
+ * PdfName
.
+ * A value can be any kind of PdfObject
, including a dictionary. A dictionary is
+ * generally used to collect and tie together the attributes of a complex object, with each
+ * key-value pair specifying the name and value of an attribute.
+ * A dictionary is represented by two left angle brackets (<<), followed by a sequence of
+ * key-value pairs, followed by two right angle brackets (>>).
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 4.7 (page 40-41).
+ * PdfDictionary
-object.
+ */
+
+ public PdfDictionary() {
+ super(DICTIONARY);
+ hashMap = new HashMap();
+ list = new ArrayList();
+ }
+
+/**
+ * Constructs a PdfDictionary
-object of a certain type.
+ *
+ * @param type a PdfName
+ */
+
+ public PdfDictionary(PdfName type) {
+ this();
+ dictionaryType = type;
+ put(PdfName.TYPE, dictionaryType);
+ }
+
+ // methods overriding some methods in PdfObject
+
+/**
+ * Returns the PDF representation of this PdfDictionary
.
+ *
+ * @return an array of byte
+ */
+
+ public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
+ os.write('<');
+ os.write('<');
+
+ // loop over all the object-pairs in the HashMap
+ PdfName key;
+ PdfObject value;
+ int type = 0;
+ for (Iterator i = list.iterator(); //hashMap.keySet().iterator();
+ i.hasNext(); ) {
+ key = (PdfName) i.next();
+ value = (PdfObject) hashMap.get(key);
+ key.toPdf(writer, os);
+ type = value.type();
+ if (type != PdfObject.ARRAY && type != PdfObject.DICTIONARY && type != PdfObject.NAME && type != PdfObject.STRING)
+ os.write(' ');
+ value.toPdf(writer, os);
+ }
+ os.write('>');
+ os.write('>');
+ }
+
+ // methods concerning the HashMap member value
+
+/**
+ * Adds a PdfObject
and its key to the PdfDictionary
.
+ *
+ * @param key key of the entry (a PdfName
)
+ * @param value value of the entry (a PdfObject
)
+ * @return the previous PdfObject
and its key to the PdfDictionary
.
+ * If the value is null it does nothing.
+ *
+ * @param key key of the entry (a PdfName
)
+ * @param value value of the entry (a PdfObject
)
+ * @return the previous PdfObject
and its key to the PdfDictionary
.
+ * If the value is null the key is deleted.
+ *
+ * @param key key of the entry (a PdfName
)
+ * @param value value of the entry (a PdfObject
)
+ * @return the previous PdfObject
and its key from the PdfDictionary
.
+ *
+ * @param key key of the entry (a PdfName
)
+ * @return the previous PdfObject
with a certain key from the PdfDictionary
.
+ *
+ * @param key key of the entry (a PdfName
)
+ * @return the previous PdfDictionary
is of a certain type.
+ *
+ * @param type a type of dictionary
+ * @return true
of false
+ *
+ * @deprecated
+ */
+
+ public boolean isDictionaryType(PdfName type) {
+ return dictionaryType.compareTo(type) == 0;
+ }
+
+/**
+ * Checks if a Dictionary
is of the type FONT.
+ *
+ * @return true
if it is, false
if it isn't.
+ */
+
+ public boolean isFont() {
+ return dictionaryType.compareTo(FONT) == 0;
+ }
+
+/**
+ * Checks if a Dictionary
is of the type PAGE.
+ *
+ * @return true
if it is, false
if it isn't.
+ */
+
+ public boolean isPage() {
+ return dictionaryType.compareTo(PAGE) == 0;
+ }
+
+/**
+ * Checks if a Dictionary
is of the type PAGES.
+ *
+ * @return true
if it is, false
if it isn't.
+ */
+
+ public boolean isPages() {
+ return dictionaryType.compareTo(PAGES) == 0;
+ }
+
+/**
+ * Checks if a Dictionary
is of the type CATALOG.
+ *
+ * @return true
if it is, false
if it isn't.
+ */
+
+ public boolean isCatalog() {
+ return dictionaryType.compareTo(CATALOG) == 0;
+ }
+
+/**
+ * Checks if a Dictionary
is of the type OUTLINES.
+ *
+ * @return true
if it is, false
if it isn't.
+ */
+
+ public boolean isOutlineTree() {
+ return dictionaryType.compareTo(OUTLINES) == 0;
+ }
+
+ public void merge(PdfDictionary other) {
+ hashMap.putAll(other.hashMap);
+ list.addAll(other.list);
+ }
+
+ public void mergeDifferent(PdfDictionary other) {
+ for (Iterator i = other.list.iterator(); //other.hashMap.keySet().iterator();
+ i.hasNext();) {
+ Object key = i.next();
+ if (!hashMap.containsKey(key)) {
+ hashMap.put(key, other.hashMap.get(key));
+ list.add(key);
+ }
+ }
+ }
+
+ public Set getKeys() {
+ return hashMap.keySet();
+ }
+
+ public void putAll(PdfDictionary dic) {
+ hashMap.putAll(dic.hashMap);
+ list.addAll(dic.list);
+ }
+
+ public int size() {
+ return hashMap.size();
+ }
+
+ public boolean contains(PdfName key) {
+ return hashMap.containsKey(key);
+ }
+
+ public String toString() {
+ return "Dictionary of type: " + get(PdfName.TYPE);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfDocument.java b/src/main/java/com/lowagie/text/pdf/PdfDocument.java
new file mode 100644
index 0000000..cafc637
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfDocument.java
@@ -0,0 +1,3279 @@
+/*
+ * $Name: $
+ * $Id: PdfDocument.java,v 1.229 2006/06/04 22:23:34 psoares33 Exp $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.awt.Color;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import com.lowagie.text.Anchor;
+import com.lowagie.text.Annotation;
+import com.lowagie.text.BadElementException;
+import com.lowagie.text.Cell;
+import com.lowagie.text.Chunk;
+import com.lowagie.text.DocListener;
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Graphic;
+import com.lowagie.text.HeaderFooter;
+import com.lowagie.text.Image;
+import com.lowagie.text.List;
+import com.lowagie.text.ListItem;
+import com.lowagie.text.Meta;
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Section;
+import com.lowagie.text.SimpleTable;
+import com.lowagie.text.StringCompare;
+import com.lowagie.text.Table;
+import com.lowagie.text.Watermark;
+import com.lowagie.text.xml.xmp.XmpWriter;
+
+/**
+ * PdfDocument
is the class that is used by PdfWriter
+ * to translate a Document
into a PDF with different pages.
+ * PdfDocument
always listens to a Document
+ * and adds the Pdf representation of every Element
that is
+ * added to the Document
.
+ *
+ * @see com.lowagie.text.Document
+ * @see com.lowagie.text.DocListener
+ * @see PdfWriter
+ */
+
+class PdfDocument extends Document implements DocListener {
+
+ /**
+ * PdfInfo
is the PDF InfoDictionary.
+ *
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 6.10 (page 120-121)
+ */
+
+ public static class PdfInfo extends PdfDictionary {
+
+ // constructors
+
+ /**
+ * Construct a PdfInfo
-object.
+ */
+
+ PdfInfo() {
+ super();
+ addProducer();
+ addCreationDate();
+ }
+
+ /**
+ * Constructs a PdfInfo
-object.
+ *
+ * @param author name of the author of the document
+ * @param title title of the document
+ * @param subject subject of the document
+ */
+
+ PdfInfo(String author, String title, String subject) {
+ this();
+ addTitle(title);
+ addSubject(subject);
+ addAuthor(author);
+ }
+
+ /**
+ * Adds the title of the document.
+ *
+ * @param title the title of the document
+ */
+
+ void addTitle(String title) {
+ put(PdfName.TITLE, new PdfString(title, PdfObject.TEXT_UNICODE));
+ }
+
+ /**
+ * Adds the subject to the document.
+ *
+ * @param subject the subject of the document
+ */
+
+ void addSubject(String subject) {
+ put(PdfName.SUBJECT, new PdfString(subject, PdfObject.TEXT_UNICODE));
+ }
+
+ /**
+ * Adds some keywords to the document.
+ *
+ * @param keywords the keywords of the document
+ */
+
+ void addKeywords(String keywords) {
+ put(PdfName.KEYWORDS, new PdfString(keywords, PdfObject.TEXT_UNICODE));
+ }
+
+ /**
+ * Adds the name of the author to the document.
+ *
+ * @param author the name of the author
+ */
+
+ void addAuthor(String author) {
+ put(PdfName.AUTHOR, new PdfString(author, PdfObject.TEXT_UNICODE));
+ }
+
+ /**
+ * Adds the name of the creator to the document.
+ *
+ * @param creator the name of the creator
+ */
+
+ void addCreator(String creator) {
+ put(PdfName.CREATOR, new PdfString(creator, PdfObject.TEXT_UNICODE));
+ }
+
+ /**
+ * Adds the name of the producer to the document.
+ */
+
+ void addProducer() {
+ // This line may only be changed by Bruno Lowagie or Paulo Soares
+ put(PdfName.PRODUCER, new PdfString(getVersion()));
+ // Do not edit the line above!
+ }
+
+ /**
+ * Adds the date of creation to the document.
+ */
+
+ void addCreationDate() {
+ PdfString date = new PdfDate();
+ put(PdfName.CREATIONDATE, date);
+ put(PdfName.MODDATE, date);
+ }
+
+ void addkey(String key, String value) {
+ if (key.equals("Producer") || key.equals("CreationDate"))
+ return;
+ put(new PdfName(key), new PdfString(value, PdfObject.TEXT_UNICODE));
+ }
+ }
+
+ /**
+ * PdfCatalog
is the PDF Catalog-object.
+ *
+ * In this class however, only the reference to the tree of pages is implemented.
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 6.2 (page 67-71)
+ */
+
+ static class PdfCatalog extends PdfDictionary {
+
+ PdfWriter writer;
+ // constructors
+
+ /**
+ * Constructs a PdfCatalog
.
+ *
+ * @param pages an indirect reference to the root of the document's Pages tree.
+ * @param writer the writer the catalog applies to
+ */
+
+ PdfCatalog(PdfIndirectReference pages, PdfWriter writer) {
+ super(CATALOG);
+ this.writer = writer;
+ put(PdfName.PAGES, pages);
+ }
+
+ /**
+ * Constructs a PdfCatalog
.
+ *
+ * @param pages an indirect reference to the root of the document's Pages tree.
+ * @param outlines an indirect reference to the outline tree.
+ * @param writer the writer the catalog applies to
+ */
+
+ PdfCatalog(PdfIndirectReference pages, PdfIndirectReference outlines, PdfWriter writer) {
+ super(CATALOG);
+ this.writer = writer;
+ put(PdfName.PAGES, pages);
+ put(PdfName.PAGEMODE, PdfName.USEOUTLINES);
+ put(PdfName.OUTLINES, outlines);
+ }
+
+ /**
+ * Adds the names of the named destinations to the catalog.
+ * @param localDestinations the local destinations
+ * @param documentJavaScript the javascript used in the document
+ * @param writer the writer the catalog applies to
+ */
+ void addNames(TreeMap localDestinations, ArrayList documentJavaScript, HashMap documentFileAttachment, PdfWriter writer) {
+ if (localDestinations.size() == 0 && documentJavaScript.size() == 0 && documentFileAttachment.size() == 0)
+ return;
+ try {
+ PdfDictionary names = new PdfDictionary();
+ if (localDestinations.size() > 0) {
+ PdfArray ar = new PdfArray();
+ for (Iterator i = localDestinations.keySet().iterator(); i.hasNext();) {
+ String name = (String)i.next();
+ Object obj[] = (Object[])localDestinations.get(name);
+ PdfIndirectReference ref = (PdfIndirectReference)obj[1];
+ ar.add(new PdfString(name));
+ ar.add(ref);
+ }
+ PdfDictionary dests = new PdfDictionary();
+ dests.put(PdfName.NAMES, ar);
+ names.put(PdfName.DESTS, writer.addToBody(dests).getIndirectReference());
+ }
+ if (documentJavaScript.size() > 0) {
+ String s[] = new String[documentJavaScript.size()];
+ for (int k = 0; k < s.length; ++k)
+ s[k] = Integer.toHexString(k);
+ Arrays.sort(s, new StringCompare());
+ PdfArray ar = new PdfArray();
+ for (int k = 0; k < s.length; ++k) {
+ ar.add(new PdfString(s[k]));
+ ar.add((PdfIndirectReference)documentJavaScript.get(k));
+ }
+ PdfDictionary js = new PdfDictionary();
+ js.put(PdfName.NAMES, ar);
+ names.put(PdfName.JAVASCRIPT, writer.addToBody(js).getIndirectReference());
+ }
+ if (documentFileAttachment.size() > 0) {
+ names.put(PdfName.EMBEDDEDFILES, writer.addToBody(PdfNameTree.writeTree(documentFileAttachment, writer)).getIndirectReference());
+ }
+ put(PdfName.NAMES, writer.addToBody(names).getIndirectReference());
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /** Sets the viewer preferences as the sum of several constants.
+ * @param preferences the viewer preferences
+ * @see PdfWriter#setViewerPreferences
+ */
+
+ void setViewerPreferences(int preferences) {
+ PdfReader.setViewerPreferences(preferences, this);
+ }
+
+ void setOpenAction(PdfAction action) {
+ put(PdfName.OPENACTION, action);
+ }
+
+
+ /** Sets the document level additional actions.
+ * @param actions dictionary of actions
+ */
+ void setAdditionalActions(PdfDictionary actions) {
+ try {
+ put(PdfName.AA, writer.addToBody(actions).getIndirectReference());
+ } catch (Exception e) {
+ new ExceptionConverter(e);
+ }
+ }
+
+
+ void setPageLabels(PdfPageLabels pageLabels) {
+ put(PdfName.PAGELABELS, pageLabels.getDictionary());
+ }
+
+ void setAcroForm(PdfObject fields) {
+ put(PdfName.ACROFORM, fields);
+ }
+ }
+
+ // membervariables
+ private PdfIndirectReference thumb;
+
+ /** The characters to be applied the hanging ponctuation. */
+ static final String hangingPunctuation = ".,;:'";
+
+ /** The PdfWriter
. */
+ private PdfWriter writer;
+
+ /** some meta information about the Document. */
+ private PdfInfo info = new PdfInfo();
+
+ /** Signals that OnOpenDocument should be called. */
+ private boolean firstPageEvent = true;
+
+ /** Signals that onParagraph is valid. */
+ private boolean isParagraph = true;
+
+ // Horizontal line
+
+ /** The line that is currently being written. */
+ private PdfLine line = null;
+
+ /** This represents the current indentation of the PDF Elements on the left side. */
+ private float indentLeft = 0;
+
+ /** This represents the current indentation of the PDF Elements on the right side. */
+ private float indentRight = 0;
+
+ /** This represents the current indentation of the PDF Elements on the left side. */
+ private float listIndentLeft = 0;
+
+ /** This represents the current alignment of the PDF Elements. */
+ private int alignment = Element.ALIGN_LEFT;
+
+ // Vertical lines
+
+ /** This is the PdfContentByte object, containing the text. */
+ private PdfContentByte text;
+
+ /** This is the PdfContentByte object, containing the borders and other Graphics. */
+ private PdfContentByte graphics;
+
+ /** The lines that are written until now. */
+ private ArrayList lines = new ArrayList();
+
+ /** This represents the leading of the lines. */
+ private float leading = 0;
+
+ /** This is the current height of the document. */
+ private float currentHeight = 0;
+
+ /** This represents the current indentation of the PDF Elements on the top side. */
+ private float indentTop = 0;
+
+ /** This represents the current indentation of the PDF Elements on the bottom side. */
+ private float indentBottom = 0;
+
+ /** This checks if the page is empty. */
+ private boolean pageEmpty = true;
+
+ private int textEmptySize;
+ // resources
+
+ /** This is the size of the next page. */
+ protected Rectangle nextPageSize = null;
+
+ /** This is the size of the several boxes of the current Page. */
+ protected HashMap thisBoxSize = new HashMap();
+
+ /** This is the size of the several boxes that will be used in
+ * the next page. */
+ protected HashMap boxSize = new HashMap();
+
+ /** This are the page resources of the current Page. */
+ protected PageResources pageResources;
+
+ // images
+
+ /** This is the image that could not be shown on a previous page. */
+ private Image imageWait = null;
+
+ /** This is the position where the image ends. */
+ private float imageEnd = -1;
+
+ /** This is the indentation caused by an image on the left. */
+ private float imageIndentLeft = 0;
+
+ /** This is the indentation caused by an image on the right. */
+ private float imageIndentRight = 0;
+
+ // annotations and outlines
+
+ /** This is the array containing the references to the annotations. */
+ private ArrayList annotations;
+
+ /** This is an array containg references to some delayed annotations. */
+ private ArrayList delayedAnnotations = new ArrayList();
+
+ /** This is the AcroForm object. */
+ PdfAcroForm acroForm;
+
+ /** This is the root outline of the document. */
+ private PdfOutline rootOutline;
+
+ /** This is the current PdfOutline
in the hierarchy of outlines. */
+ private PdfOutline currentOutline;
+
+ /** The current active PdfAction
when processing an Anchor
. */
+ private PdfAction currentAction = null;
+
+ /**
+ * Stores the destinations keyed by name. Value is
+ * Object[]{PdfAction,PdfIndirectReference,PdfDestintion}
.
+ */
+ private TreeMap localDestinations = new TreeMap(new StringCompare());
+
+ private ArrayList documentJavaScript = new ArrayList();
+
+ private HashMap documentFileAttachment = new HashMap();
+
+ /** these are the viewerpreferences of the document */
+ private int viewerPreferences = 0;
+
+ private String openActionName;
+ private PdfAction openActionAction;
+ private PdfDictionary additionalActions;
+ private PdfPageLabels pageLabels;
+
+ //add by Jin-Hsia Yang
+ private boolean isNewpage = false;
+
+ private float paraIndent = 0;
+ //end add by Jin-Hsia Yang
+
+ /** margin in x direction starting from the left. Will be valid in the next page */
+ protected float nextMarginLeft;
+
+ /** margin in x direction starting from the right. Will be valid in the next page */
+ protected float nextMarginRight;
+
+ /** margin in y direction starting from the top. Will be valid in the next page */
+ protected float nextMarginTop;
+
+ /** margin in y direction starting from the bottom. Will be valid in the next page */
+ protected float nextMarginBottom;
+
+/** The duration of the page */
+ protected int duration=-1; // negative values will indicate no duration
+
+/** The page transition */
+ protected PdfTransition transition=null;
+
+ protected PdfDictionary pageAA = null;
+
+ /** Holds value of property strictImageSequence. */
+ private boolean strictImageSequence = false;
+
+ /** Holds the type of the last element, that has been added to the document. */
+ private int lastElementType = -1;
+
+ private boolean isNewPagePending;
+
+ protected int markPoint;
+
+ // constructors
+
+ /**
+ * Constructs a new PDF document.
+ * @throws DocumentException on error
+ */
+
+ public PdfDocument() throws DocumentException {
+ super();
+ addProducer();
+ addCreationDate();
+ }
+
+ // listener methods
+
+ /**
+ * Adds a PdfWriter
to the PdfDocument
.
+ *
+ * @param writer the PdfWriter
that writes everything
+ * what is added to this document to an outputstream.
+ * @throws DocumentException on error
+ */
+
+ public void addWriter(PdfWriter writer) throws DocumentException {
+ if (this.writer == null) {
+ this.writer = writer;
+ acroForm = new PdfAcroForm(writer);
+ return;
+ }
+ throw new DocumentException("You can only add a writer to a PdfDocument once.");
+ }
+
+ /**
+ * Sets the pagesize.
+ *
+ * @param pageSize the new pagesize
+ * @return true
if the page size was set
+ */
+
+ public boolean setPageSize(Rectangle pageSize) {
+ if (writer != null && writer.isPaused()) {
+ return false;
+ }
+ nextPageSize = new Rectangle(pageSize);
+ return true;
+ }
+
+ /**
+ * Changes the header of this document.
+ *
+ * @param header the new header
+ */
+
+ public void setHeader(HeaderFooter header) {
+ if (writer != null && writer.isPaused()) {
+ return;
+ }
+ super.setHeader(header);
+ }
+
+ /**
+ * Resets the header of this document.
+ */
+
+ public void resetHeader() {
+ if (writer != null && writer.isPaused()) {
+ return;
+ }
+ super.resetHeader();
+ }
+
+ /**
+ * Changes the footer of this document.
+ *
+ * @param footer the new footer
+ */
+
+ public void setFooter(HeaderFooter footer) {
+ if (writer != null && writer.isPaused()) {
+ return;
+ }
+ super.setFooter(footer);
+ }
+
+ /**
+ * Resets the footer of this document.
+ */
+
+ public void resetFooter() {
+ if (writer != null && writer.isPaused()) {
+ return;
+ }
+ super.resetFooter();
+ }
+
+ /**
+ * Sets the page number to 0.
+ */
+
+ public void resetPageCount() {
+ if (writer != null && writer.isPaused()) {
+ return;
+ }
+ super.resetPageCount();
+ }
+
+ /**
+ * Sets the page number.
+ *
+ * @param pageN the new page number
+ */
+
+ public void setPageCount(int pageN) {
+ if (writer != null && writer.isPaused()) {
+ return;
+ }
+ super.setPageCount(pageN);
+ }
+
+ /**
+ * Sets the Watermark
.
+ *
+ * @param watermark the watermark to add
+ * @return true
if the element was added, false
if not.
+ */
+
+ public boolean add(Watermark watermark) {
+ if (writer != null && writer.isPaused()) {
+ return false;
+ }
+ this.watermark = watermark;
+ return true;
+ }
+
+ /**
+ * Removes the Watermark
.
+ */
+
+ public void removeWatermark() {
+ if (writer != null && writer.isPaused()) {
+ return;
+ }
+ this.watermark = null;
+ }
+
+ /**
+ * Sets the margins.
+ *
+ * @param marginLeft the margin on the left
+ * @param marginRight the margin on the right
+ * @param marginTop the margin on the top
+ * @param marginBottom the margin on the bottom
+ * @return a boolean
+ */
+
+ public boolean setMargins(float marginLeft, float marginRight, float marginTop, float marginBottom) {
+ if (writer != null && writer.isPaused()) {
+ return false;
+ }
+ nextMarginLeft = marginLeft;
+ nextMarginRight = marginRight;
+ nextMarginTop = marginTop;
+ nextMarginBottom = marginBottom;
+ return true;
+ }
+
+ protected PdfArray rotateAnnotations() {
+ PdfArray array = new PdfArray();
+ int rotation = pageSize.getRotation() % 360;
+ int currentPage = writer.getCurrentPageNumber();
+ for (int k = 0; k < annotations.size(); ++k) {
+ PdfAnnotation dic = (PdfAnnotation)annotations.get(k);
+ int page = dic.getPlaceInPage();
+ if (page > currentPage) {
+ delayedAnnotations.add(dic);
+ continue;
+ }
+ if (dic.isForm()) {
+ if (!dic.isUsed()) {
+ HashMap templates = dic.getTemplates();
+ if (templates != null)
+ acroForm.addFieldTemplates(templates);
+ }
+ PdfFormField field = (PdfFormField)dic;
+ if (field.getParent() == null)
+ acroForm.addDocumentField(field.getIndirectReference());
+ }
+ if (dic.isAnnotation()) {
+ array.add(dic.getIndirectReference());
+ if (!dic.isUsed()) {
+ PdfRectangle rect = (PdfRectangle)dic.get(PdfName.RECT);
+ if (rect != null) {
+ switch (rotation) {
+ case 90:
+ dic.put(PdfName.RECT, new PdfRectangle(
+ pageSize.top() - rect.bottom(),
+ rect.left(),
+ pageSize.top() - rect.top(),
+ rect.right()));
+ break;
+ case 180:
+ dic.put(PdfName.RECT, new PdfRectangle(
+ pageSize.right() - rect.left(),
+ pageSize.top() - rect.bottom(),
+ pageSize.right() - rect.right(),
+ pageSize.top() - rect.top()));
+ break;
+ case 270:
+ dic.put(PdfName.RECT, new PdfRectangle(
+ rect.bottom(),
+ pageSize.right() - rect.left(),
+ rect.top(),
+ pageSize.right() - rect.right()));
+ break;
+ }
+ }
+ }
+ }
+ if (!dic.isUsed()) {
+ dic.setUsed();
+ try {
+ writer.addToBody(dic, dic.getIndirectReference());
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Makes a new page and sends it to the PdfWriter
.
+ *
+ * @return a boolean
+ * @throws DocumentException on error
+ */
+
+ public boolean newPage() throws DocumentException {
+ lastElementType = -1;
+ //add by Jin-Hsia Yang
+ isNewpage = true;
+ //end add by Jin-Hsia Yang
+ if (writer.getDirectContent().size() == 0 && writer.getDirectContentUnder().size() == 0 && (pageEmpty || (writer != null && writer.isPaused()))) {
+ return false;
+ }
+ PdfPageEvent pageEvent = writer.getPageEvent();
+ if (pageEvent != null)
+ pageEvent.onEndPage(writer, this);
+
+ //Added to inform any listeners that we are moving to a new page (added by David Freels)
+ super.newPage();
+
+ // the following 2 lines were added by Pelikan Stephan
+ imageIndentLeft = 0;
+ imageIndentRight = 0;
+
+ // we flush the arraylist with recently written lines
+ flushLines();
+ // we assemble the resources of this pages
+ pageResources.addDefaultColorDiff(writer.getDefaultColorspace());
+ PdfDictionary resources = pageResources.getResources();
+ // we make a new page and add it to the document
+ if (writer.getPDFXConformance() != PdfWriter.PDFXNONE) {
+ if (thisBoxSize.containsKey("art") && thisBoxSize.containsKey("trim"))
+ throw new PdfXConformanceException("Only one of ArtBox or TrimBox can exist in the page.");
+ if (!thisBoxSize.containsKey("art") && !thisBoxSize.containsKey("trim")) {
+ if (thisBoxSize.containsKey("crop"))
+ thisBoxSize.put("trim", thisBoxSize.get("crop"));
+ else
+ thisBoxSize.put("trim", new PdfRectangle(pageSize, pageSize.getRotation()));
+ }
+ }
+ PdfPage page;
+ int rotation = pageSize.getRotation();
+ page = new PdfPage(new PdfRectangle(pageSize, rotation), thisBoxSize, resources, rotation);
+ // we add tag info
+ if (writer.isTagged())
+ page.put(PdfName.STRUCTPARENTS, new PdfNumber(writer.getCurrentPageNumber() - 1));
+// we add the transitions
+ if (this.transition!=null) {
+ page.put(PdfName.TRANS, this.transition.getTransitionDictionary());
+ transition = null;
+ }
+ if (this.duration>0) {
+ page.put(PdfName.DUR,new PdfNumber(this.duration));
+ duration = 0;
+ }
+ // we add the page object additional actions
+ if (pageAA != null) {
+ try {
+ page.put(PdfName.AA, writer.addToBody(pageAA).getIndirectReference());
+ }
+ catch (IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ pageAA = null;
+ }
+ // we check if the userunit is defined
+ if (writer.getUserunit() > 0f) {
+ page.put(PdfName.USERUNIT, new PdfNumber(writer.getUserunit()));
+ }
+ // we add the annotations
+ if (annotations.size() > 0) {
+ PdfArray array = rotateAnnotations();
+ if (array.size() != 0)
+ page.put(PdfName.ANNOTS, array);
+ }
+ // we add the thumbs
+ if (thumb != null) {
+ page.put(PdfName.THUMB, thumb);
+ thumb = null;
+ }
+ if (!open || close) {
+ throw new PdfException("The document isn't open.");
+ }
+ if (text.size() > textEmptySize)
+ text.endText();
+ else
+ text = null;
+ writer.add(page, new PdfContents(writer.getDirectContentUnder(), graphics, text, writer.getDirectContent(), pageSize));
+ // we initialize the new page
+ initPage();
+
+ //add by Jin-Hsia Yang
+ isNewpage = false;
+ //end add by Jin-Hsia Yang
+
+ return true;
+ }
+
+ // methods to open and close a document
+
+ /**
+ * Opens the document.
+ * PdfPTable
to the document.
+ * @param ptable the PdfPTable
to be added to the document.
+ * @throws DocumentException on error
+ */
+ void addPTable(PdfPTable ptable) throws DocumentException {
+ ColumnText ct = new ColumnText(writer.getDirectContent());
+ if (currentHeight > 0) {
+ Paragraph p = new Paragraph();
+ p.setLeading(0);
+ ct.addElement(p);
+ // if the table prefers to be on a single page, and it wouldn't
+ //fit on the current page, start a new page.
+ if (ptable.getKeepTogether() && !fitsPage(ptable, 0f))
+ newPage();
+ }
+ ct.addElement(ptable);
+ boolean he = ptable.isHeadersInEvent();
+ ptable.setHeadersInEvent(true);
+ int loop = 0;
+ while (true) {
+ ct.setSimpleColumn(indentLeft(), indentBottom(), indentRight(), indentTop() - currentHeight);
+ int status = ct.go();
+ if ((status & ColumnText.NO_MORE_TEXT) != 0) {
+ text.moveText(0, ct.getYLine() - indentTop() + currentHeight);
+ currentHeight = indentTop() - ct.getYLine();
+ break;
+ }
+ if (indentTop() - currentHeight == ct.getYLine())
+ ++loop;
+ else
+ loop = 0;
+ if (loop == 3) {
+ add(new Paragraph("ERROR: Infinite table loop"));
+ break;
+ }
+ newPage();
+ }
+ ptable.setHeadersInEvent(he);
+ }
+
+ /**
+ * Gets a PdfTable object
+ * (contributed by dperezcar@fcc.es)
+ * @param table a high level table object
+ * @param supportRowAdditions
+ * @return returns a PdfTable object
+ * @see PdfWriter#getPdfTable(Table)
+ */
+
+ PdfTable getPdfTable(Table table, boolean supportRowAdditions) {
+ return new PdfTable(table, indentLeft(), indentRight(), indentTop() - currentHeight, supportRowAdditions);
+ }
+
+ /**
+ * @see PdfWriter#breakTableIfDoesntFit(PdfTable)
+ * (contributed by dperezcar@fcc.es)
+ * @param table Table to add
+ * @return true if the table will be broken
+ * @throws DocumentException
+ */
+
+ boolean breakTableIfDoesntFit(PdfTable table) throws DocumentException {
+ table.updateRowAdditions();
+ // Do we have any full page available?
+ if (!table.hasToFitPageTable() && table.bottom() <= indentBottom) {
+ // Then output that page
+ add(table, true);
+ return true;
+ }
+ return false;
+ }
+
+ private static class RenderingContext {
+ int countPageBreaks = 0;
+ float pagetop = -1;
+ float oldHeight = -1;
+
+ PdfContentByte cellGraphics = null;
+
+ float lostTableBottom;
+
+ float maxCellBottom;
+ float maxCellHeight;
+
+ Map rowspanMap;
+ Map pageMap = new HashMap();
+ /**
+ * A PdfPTable
+ */
+ public PdfTable table;
+
+ /**
+ * Consumes the rowspan
+ * @param c
+ * @return a rowspan.
+ */
+ public int consumeRowspan(PdfCell c) {
+ if (c.rowspan() == 1) {
+ return 1;
+ }
+
+ Integer i = (Integer) rowspanMap.get(c);
+ if (i == null) {
+ i = new Integer(c.rowspan());
+ }
+
+ i = new Integer(i.intValue() - 1);
+ rowspanMap.put(c, i);
+
+ if (i.intValue() < 1) {
+ return 1;
+ }
+ return i.intValue();
+ }
+
+ /**
+ * Looks at the current rowspan.
+ * @param c
+ * @return the current rowspan
+ */
+ public int currentRowspan(PdfCell c) {
+ Integer i = (Integer) rowspanMap.get(c);
+ if (i == null) {
+ return c.rowspan();
+ } else {
+ return i.intValue();
+ }
+ }
+
+ public int cellRendered(PdfCell cell, int pageNumber) {
+ Integer i = (Integer) pageMap.get(cell);
+ if (i == null) {
+ i = new Integer(1);
+ } else {
+ i = new Integer(i.intValue() + 1);
+ }
+ pageMap.put(cell, i);
+
+ Integer pageInteger = new Integer(pageNumber);
+ Set set = (Set) pageMap.get(pageInteger);
+
+ if (set == null) {
+ set = new HashSet();
+ pageMap.put(pageInteger, set);
+ }
+
+ set.add(cell);
+
+ return i.intValue();
+ }
+
+ public int numCellRendered(PdfCell cell) {
+ Integer i = (Integer) pageMap.get(cell);
+ if (i == null) {
+ i = new Integer(0);
+ }
+ return i.intValue();
+ }
+
+ public boolean isCellRenderedOnPage(PdfCell cell, int pageNumber) {
+ Integer pageInteger = new Integer(pageNumber);
+ Set set = (Set) pageMap.get(pageInteger);
+
+ if (set != null) {
+ return set.contains(cell);
+ }
+
+ return false;
+ }
+ };
+
+ private void analyzeRow(ArrayList rows, RenderingContext ctx) {
+ ctx.maxCellBottom = indentBottom();
+
+ // determine whether row(index) is in a rowspan
+ int rowIndex = 0;
+
+ ArrayList row = (ArrayList) rows.get(rowIndex);
+ int maxRowspan = 1;
+ Iterator iterator = row.iterator();
+ while (iterator.hasNext()) {
+ PdfCell cell = (PdfCell) iterator.next();
+ maxRowspan = Math.max(ctx.currentRowspan(cell), maxRowspan);
+ }
+ rowIndex += maxRowspan;
+
+ boolean useTop = true;
+ if (rowIndex == rows.size()) {
+ rowIndex = rows.size() - 1;
+ useTop = false;
+ }
+
+ if (rowIndex < 0 || rowIndex >= rows.size()) return;
+
+ row = (ArrayList) rows.get(rowIndex);
+ iterator = row.iterator();
+ while (iterator.hasNext()) {
+ PdfCell cell = (PdfCell) iterator.next();
+ Rectangle cellRect = cell.rectangle(ctx.pagetop, indentBottom());
+ if (useTop) {
+ ctx.maxCellBottom = Math.max(ctx.maxCellBottom, cellRect.top());
+ } else {
+ if (ctx.currentRowspan(cell) == 1) {
+ ctx.maxCellBottom = Math.max(ctx.maxCellBottom, cellRect.bottom());
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds a new table to
+ * @param table Table to add. Rendered rows will be deleted after processing.
+ * @param onlyFirstPage Render only the first full page
+ * @throws DocumentException
+ */
+ private void add(PdfTable table, boolean onlyFirstPage) throws DocumentException {
+ // before every table, we flush all lines
+ flushLines();
+
+ RenderingContext ctx = new RenderingContext();
+ ctx.pagetop = indentTop();
+ ctx.oldHeight = currentHeight;
+ ctx.cellGraphics = new PdfContentByte(writer);
+ ctx.rowspanMap = new HashMap();
+ ctx.table = table;
+
+ // initialisation of parameters
+ PdfCell cell;
+
+ boolean tableHasToFit =
+ table.hasToFitPageTable() ? (table.bottom() < indentBottom() && table.height() < (top() - bottom())) : false;
+ if (pageEmpty)
+ tableHasToFit = false;
+ boolean cellsHaveToFit = table.hasToFitPageCells();
+
+ // drawing the table
+ ArrayList dataCells = table.getCells();
+
+ ArrayList headercells = table.getHeaderCells();
+ // Check if we have removed header cells in a previous call
+ if (headercells.size() > 0 && (dataCells.size() == 0 || dataCells.get(0) != headercells.get(0))) {
+ ArrayList allCells = new ArrayList(dataCells.size()+headercells.size());
+ allCells.addAll(headercells);
+ allCells.addAll(dataCells);
+ dataCells = allCells;
+ }
+
+ ArrayList cells = dataCells;
+ ArrayList rows = extractRows(cells, ctx);
+ boolean isContinue = false;
+ while (!cells.isEmpty()) {
+ // initialisation of some extra parameters;
+ ctx.lostTableBottom = 0;
+
+ // loop over the cells
+ boolean cellsShown = false;
+ int currentGroupNumber = 0;
+ boolean headerChecked = false;
+
+ float headerHeight = 0;
+
+ // draw the cells (line by line)
+ Iterator iterator = rows.iterator();
+
+ boolean atLeastOneFits = false;
+ while (iterator.hasNext()) {
+ ArrayList row = (ArrayList) iterator.next();
+ analyzeRow(rows, ctx);
+ renderCells(ctx, row, table.hasToFitPageCells() & atLeastOneFits);
+
+ if (!mayBeRemoved(row)) {
+ break;
+ }
+ consumeRowspan(row, ctx);
+ iterator.remove();
+ atLeastOneFits = true;
+ }
+
+// compose cells array list for subsequent code
+ cells.clear();
+ Set opt = new HashSet();
+ iterator = rows.iterator();
+ while (iterator.hasNext()) {
+ ArrayList row = (ArrayList) iterator.next();
+
+ Iterator cellIterator = row.iterator();
+ while (cellIterator.hasNext()) {
+ cell = (PdfCell) cellIterator.next();
+
+ if (!opt.contains(cell)) {
+ cells.add(cell);
+ opt.add(cell);
+ }
+ }
+ }
+
+ tableHasToFit = false;
+
+ // we paint the graphics of the table after looping through all the cells
+ Rectangle tablerec = new Rectangle(table);
+ tablerec.setBorder(table.border());
+ tablerec.setBorderWidth(table.borderWidth());
+ tablerec.setBorderColor(table.borderColor());
+ tablerec.setBackgroundColor(table.backgroundColor());
+ PdfContentByte under = writer.getDirectContentUnder();
+ under.rectangle(tablerec.rectangle(top(), indentBottom()));
+ under.add(ctx.cellGraphics);
+ // bugfix by Gerald Fehringer: now again add the border for the table
+ // since it might have been covered by cell backgrounds
+ tablerec.setBackgroundColor(null);
+ tablerec = tablerec.rectangle(top(), indentBottom());
+ tablerec.setBorder(table.border());
+ under.rectangle(tablerec);
+ // end bugfix
+
+ ctx.cellGraphics = new PdfContentByte(null);
+ // if the table continues on the next page
+
+ if (!rows.isEmpty()) {
+ isContinue = true;
+ graphics.setLineWidth(table.borderWidth());
+ if (cellsShown && (table.border() & Rectangle.BOTTOM) == Rectangle.BOTTOM) {
+ // Draw the bottom line
+
+ // the color is set to the color of the element
+ Color tColor = table.borderColor();
+ if (tColor != null) {
+ graphics.setColorStroke(tColor);
+ }
+ graphics.moveTo(table.left(), Math.max(table.bottom(), indentBottom()));
+ graphics.lineTo(table.right(), Math.max(table.bottom(), indentBottom()));
+ graphics.stroke();
+ if (tColor != null) {
+ graphics.resetRGBColorStroke();
+ }
+ }
+
+ // old page
+ pageEmpty = false;
+ float difference = ctx.lostTableBottom;
+
+ // new page
+ newPage();
+
+ ctx.countPageBreaks++;
+
+ // G.F.: if something added in page event i.e. currentHeight > 0
+ float heightCorrection = 0;
+ boolean somethingAdded = false;
+ if (currentHeight > 0) {
+ heightCorrection = 6;
+ currentHeight += heightCorrection;
+ somethingAdded = true;
+ newLine();
+ flushLines();
+ indentTop = currentHeight - leading;
+ currentHeight = 0;
+ }
+ else {
+ flushLines();
+ }
+
+ // this part repeats the table headers (if any)
+ int size = headercells.size();
+ if (size > 0) {
+ // this is the top of the headersection
+ cell = (PdfCell) headercells.get(0);
+ float oldTop = cell.top(0);
+ // loop over all the cells of the table header
+ for (int i = 0; i < size; i++) {
+ cell = (PdfCell) headercells.get(i);
+ // calculation of the new cellpositions
+ cell.setTop(indentTop() - oldTop + cell.top(0));
+ cell.setBottom(indentTop() - oldTop + cell.bottom(0));
+ ctx.pagetop = cell.bottom();
+ // we paint the borders of the cell
+ ctx.cellGraphics.rectangle(cell.rectangle(indentTop(), indentBottom()));
+ // we write the text of the cell
+ ArrayList images = cell.getImages(indentTop(), indentBottom());
+ for (Iterator im = images.iterator(); im.hasNext();) {
+ cellsShown = true;
+ Image image = (Image) im.next();
+ graphics.addImage(image);
+ }
+ lines = cell.getLines(indentTop(), indentBottom());
+ float cellTop = cell.top(indentTop());
+ text.moveText(0, cellTop-heightCorrection);
+ float cellDisplacement = flushLines() - cellTop+heightCorrection;
+ text.moveText(0, cellDisplacement);
+ }
+
+ currentHeight = indentTop() - ctx.pagetop + table.cellspacing();
+ text.moveText(0, ctx.pagetop - indentTop() - currentHeight);
+ }
+ else {
+ if (somethingAdded) {
+ ctx.pagetop = indentTop();
+ text.moveText(0, -table.cellspacing());
+ }
+ }
+ ctx.oldHeight = currentHeight - heightCorrection;
+
+ // calculating the new positions of the table and the cells
+ size = Math.min(cells.size(), table.columns());
+ int i = 0;
+ while (i < size) {
+ cell = (PdfCell) cells.get(i);
+ if (cell.top(-table.cellspacing()) > ctx.lostTableBottom) {
+ float newBottom = ctx.pagetop - difference + cell.bottom();
+ float neededHeight = cell.remainingHeight();
+ if (newBottom > ctx.pagetop - neededHeight) {
+ difference += newBottom - (ctx.pagetop - neededHeight);
+ }
+ }
+ i++;
+ }
+ size = cells.size();
+ table.setTop(indentTop());
+ table.setBottom(ctx.pagetop - difference + table.bottom(table.cellspacing()));
+ for (i = 0; i < size; i++) {
+ cell = (PdfCell) cells.get(i);
+ float newBottom = ctx.pagetop - difference + cell.bottom();
+ float newTop = ctx.pagetop - difference + cell.top(-table.cellspacing());
+ if (newTop > indentTop() - currentHeight) {
+ newTop = indentTop() - currentHeight;
+ }
+
+ cell.setTop(newTop );
+ cell.setBottom(newBottom );
+ }
+ if (onlyFirstPage) {
+ break;
+ }
+ }
+ }
+
+ float tableHeight = table.top() - table.bottom();
+ // bugfix by Adauto Martins when have more than two tables and more than one page
+ // If continuation of table in other page (bug report #1460051)
+ if (isContinue) {
+ currentHeight = tableHeight;
+ text.moveText(0, -(tableHeight - (ctx.oldHeight * 2)));
+ } else {
+ currentHeight = ctx.oldHeight + tableHeight;
+ text.moveText(0, -tableHeight);
+ }
+ // end bugfix
+ pageEmpty = false;
+
+ if (ctx.countPageBreaks > 0) {
+ // in case of tables covering more that one page have to have
+ // a newPage followed to reset some internal state. Otherwise
+ // subsequent tables are rendered incorrectly.
+ isNewPagePending = true;
+ }
+ }
+
+ private boolean mayBeRemoved(ArrayList row) {
+ Iterator iterator = row.iterator();
+ boolean mayBeRemoved = true;
+ while (iterator.hasNext()) {
+ PdfCell cell = (PdfCell) iterator.next();
+
+ mayBeRemoved &= cell.mayBeRemoved();
+ }
+ return mayBeRemoved;
+ }
+
+ private void consumeRowspan(ArrayList row, RenderingContext ctx) {
+ Iterator iterator = row.iterator();
+ while (iterator.hasNext()) {
+ PdfCell c = (PdfCell) iterator.next();
+ ctx.consumeRowspan(c);
+ }
+ }
+
+ private ArrayList extractRows(ArrayList cells, RenderingContext ctx) {
+ PdfCell cell;
+ PdfCell previousCell = null;
+ ArrayList rows = new ArrayList();
+ java.util.List rowCells = new ArrayList();
+
+ Iterator iterator = cells.iterator();
+ while (iterator.hasNext()) {
+ cell = (PdfCell) iterator.next();
+
+ boolean isAdded = false;
+
+ boolean isEndOfRow = !iterator.hasNext();
+ boolean isCurrentCellPartOfRow = !iterator.hasNext();
+
+ if (previousCell != null) {
+ if (cell.left() <= previousCell.left()) {
+ isEndOfRow = true;
+ isCurrentCellPartOfRow = false;
+ }
+ }
+
+ if (isCurrentCellPartOfRow) {
+ rowCells.add(cell);
+ isAdded = true;
+ }
+
+ if (isEndOfRow) {
+ if (!rowCells.isEmpty()) {
+ // add to rowlist
+ rows.add(rowCells);
+ }
+
+ // start a new list for next line
+ rowCells = new ArrayList();
+ }
+
+ if (!isAdded) {
+ rowCells.add(cell);
+ }
+
+ previousCell = cell;
+ }
+
+ if (!rowCells.isEmpty()) {
+ rows.add(rowCells);
+ }
+
+ // fill row information with rowspan cells to get complete "scan lines"
+ for (int i = rows.size() - 1; i >= 0; i--) {
+ ArrayList row = (ArrayList) rows.get(i);
+ // iterator through row
+ for (int j = 0; j < row.size(); j++) {
+ PdfCell c = (PdfCell) row.get(j);
+ int rowspan = c.rowspan();
+ // fill in missing rowspan cells to complete "scan line"
+ for (int k = 1; k < rowspan; k++) {
+ ArrayList spannedRow = ((ArrayList) rows.get(i + k));
+ if (spannedRow.size() > j)
+ spannedRow.add(j, c);
+ }
+ }
+ }
+
+ return rows;
+ }
+
+ private void renderCells(RenderingContext ctx, java.util.List cells, boolean hasToFit) throws DocumentException {
+ PdfCell cell;
+ Iterator iterator;
+ if (hasToFit) {
+ iterator = cells.iterator();
+ while (iterator.hasNext()) {
+ cell = (PdfCell) iterator.next();
+ if (!cell.isHeader()) {
+ if (cell.bottom() < indentBottom()) return;
+ }
+ }
+ }
+ iterator = cells.iterator();
+
+ while (iterator.hasNext()) {
+ cell = (PdfCell) iterator.next();
+ if (!ctx.isCellRenderedOnPage(cell, getPageNumber())) {
+
+ float correction = 0;
+ if (ctx.numCellRendered(cell) >= 1) {
+ correction = 1.0f;
+ }
+
+ lines = cell.getLines(ctx.pagetop, indentBottom() - correction);
+
+ // if there is still text to render we render it
+ if (lines != null && lines.size() > 0) {
+
+ // we write the text
+ float cellTop = cell.top(ctx.pagetop - ctx.oldHeight);
+ text.moveText(0, cellTop);
+ float cellDisplacement = flushLines() - cellTop;
+
+ text.moveText(0, cellDisplacement);
+ if (ctx.oldHeight + cellDisplacement > currentHeight) {
+ currentHeight = ctx.oldHeight + cellDisplacement;
+ }
+
+ ctx.cellRendered(cell, getPageNumber());
+ }
+ float indentBottom = Math.max(cell.bottom(), indentBottom());
+ Rectangle tableRect = ctx.table.rectangle(ctx.pagetop, indentBottom());
+ indentBottom = Math.max(tableRect.bottom(), indentBottom);
+
+ // we paint the borders of the cells
+ Rectangle cellRect = cell.rectangle(tableRect.top(), indentBottom);
+ //cellRect.setBottom(cellRect.bottom());
+ if (cellRect.height() > 0) {
+ ctx.lostTableBottom = indentBottom;
+ ctx.cellGraphics.rectangle(cellRect);
+ }
+
+ // and additional graphics
+ ArrayList images = cell.getImages(ctx.pagetop, indentBottom());
+ for (Iterator i = images.iterator(); i.hasNext();) {
+ Image image = (Image) i.next();
+ graphics.addImage(image);
+ }
+
+ }
+ }
+
+ }
+
+
+ /**
+ * Signals that an Element
was added to the Document
.
+ *
+ * @param element the element to add
+ * @return true
if the element was added, false
if not.
+ * @throws DocumentException when a document isn't open yet, or has been closed
+ */
+
+ public boolean add(Element element) throws DocumentException {
+ if (writer != null && writer.isPaused()) {
+ return false;
+ }
+ try {
+// resolves problem described in add(PdfTable)
+ if (isNewPagePending) {
+ isNewPagePending = false;
+ newPage();
+ }
+ switch(element.type()) {
+
+ // Information (headers)
+ case Element.HEADER:
+ info.addkey(((Meta)element).name(), ((Meta)element).content());
+ break;
+ case Element.TITLE:
+ info.addTitle(((Meta)element).content());
+ break;
+ case Element.SUBJECT:
+ info.addSubject(((Meta)element).content());
+ break;
+ case Element.KEYWORDS:
+ info.addKeywords(((Meta)element).content());
+ break;
+ case Element.AUTHOR:
+ info.addAuthor(((Meta)element).content());
+ break;
+ case Element.CREATOR:
+ info.addCreator(((Meta)element).content());
+ break;
+ case Element.PRODUCER:
+ // you can not change the name of the producer
+ info.addProducer();
+ break;
+ case Element.CREATIONDATE:
+ // you can not set the creation date, only reset it
+ info.addCreationDate();
+ break;
+
+ // content (text)
+ case Element.CHUNK: {
+ // if there isn't a current line available, we make one
+ if (line == null) {
+ carriageReturn();
+ }
+
+ // we cast the element to a chunk
+ PdfChunk chunk = new PdfChunk((Chunk) element, currentAction);
+ // we try to add the chunk to the line, until we succeed
+ {
+ PdfChunk overflow;
+ while ((overflow = line.add(chunk)) != null) {
+ carriageReturn();
+ chunk = overflow;
+ }
+ }
+ pageEmpty = false;
+ if (chunk.isAttribute(Chunk.NEWPAGE)) {
+ newPage();
+ }
+ break;
+ }
+ case Element.ANCHOR: {
+ Anchor anchor = (Anchor) element;
+ String url = anchor.reference();
+ leading = anchor.leading();
+ if (url != null) {
+ currentAction = new PdfAction(url);
+ }
+
+ // we process the element
+ element.process(this);
+ currentAction = null;
+ break;
+ }
+ case Element.ANNOTATION: {
+ if (line == null) {
+ carriageReturn();
+ }
+ Annotation annot = (Annotation) element;
+ PdfAnnotation an = convertAnnotation(writer, annot);
+ annotations.add(an);
+ pageEmpty = false;
+ break;
+ }
+ case Element.PHRASE: {
+ // we cast the element to a phrase and set the leading of the document
+ leading = ((Phrase) element).leading();
+ // we process the element
+ element.process(this);
+ break;
+ }
+ case Element.PARAGRAPH: {
+ // we cast the element to a paragraph
+ Paragraph paragraph = (Paragraph) element;
+
+ float spacingBefore = paragraph.spacingBefore();
+ if (spacingBefore != 0) {
+ leading = spacingBefore;
+ carriageReturn();
+ if (!pageEmpty) {
+ /*
+ * Don't add spacing before a paragraph if it's the first
+ * on the page
+ */
+ Chunk space = new Chunk(" ");
+ space.process(this);
+ carriageReturn();
+ }
+ }
+
+ // we adjust the parameters of the document
+ alignment = paragraph.alignment();
+ leading = paragraph.leading();
+
+ carriageReturn();
+ // we don't want to make orphans/widows
+ if (currentHeight + line.height() + leading > indentTop() - indentBottom()) {
+ newPage();
+ }
+
+ // Begin added: Bonf (Marc Schneider) 2003-07-29
+ //carriageReturn();
+ // End added: Bonf (Marc Schneider) 2003-07-29
+
+ indentLeft += paragraph.indentationLeft();
+ indentRight += paragraph.indentationRight();
+
+ // Begin removed: Bonf (Marc Schneider) 2003-07-29
+ carriageReturn();
+ // End removed: Bonf (Marc Schneider) 2003-07-29
+
+
+ //add by Jin-Hsia Yang
+
+ paraIndent += paragraph.indentationLeft();
+ //end add by Jin-Hsia Yang
+
+ PdfPageEvent pageEvent = writer.getPageEvent();
+ if (pageEvent != null && isParagraph)
+ pageEvent.onParagraph(writer, this, indentTop() - currentHeight);
+
+ // if a paragraph has to be kept together, we wrap it in a table object
+ if (paragraph.getKeepTogether()) {
+ Table table = new Table(1, 1);
+ table.setOffset(0f);
+ table.setBorder(Table.NO_BORDER);
+ table.setWidth(100f);
+ table.setTableFitsPage(true);
+ Cell cell = new Cell(paragraph);
+ cell.setBorder(Table.NO_BORDER);
+ //patch by Matt Benson 11/01/2002 - 14:32:00
+ cell.setHorizontalAlignment(paragraph.alignment());
+ //end patch by Matt Benson
+ table.addCell(cell);
+ this.add(table);
+ break;
+ }
+ else
+ // we process the paragraph
+ element.process(this);
+
+ //add by Jin-Hsia Yang and blowagie
+ paraIndent -= paragraph.indentationLeft();
+ //end add by Jin-Hsia Yang and blowagie
+
+ // Begin removed: Bonf (Marc Schneider) 2003-07-29
+ // carriageReturn();
+ // End removed: Bonf (Marc Schneider) 2003-07-29
+
+ float spacingAfter = paragraph.spacingAfter();
+ if (spacingAfter != 0) {
+ leading = spacingAfter;
+ carriageReturn();
+ if (currentHeight + line.height() + leading < indentTop() - indentBottom()) {
+ /*
+ * Only add spacing after a paragraph if the extra
+ * spacing fits on the page.
+ */
+ Chunk space = new Chunk(" ");
+ space.process(this);
+ carriageReturn();
+ }
+ leading = paragraph.leading(); // restore original leading
+ }
+
+ if (pageEvent != null && isParagraph)
+ pageEvent.onParagraphEnd(writer, this, indentTop() - currentHeight);
+
+ alignment = Element.ALIGN_LEFT;
+ indentLeft -= paragraph.indentationLeft();
+ indentRight -= paragraph.indentationRight();
+
+ // Begin added: Bonf (Marc Schneider) 2003-07-29
+ carriageReturn();
+ // End added: Bonf (Marc Schneider) 2003-07-29
+
+ //add by Jin-Hsia Yang
+
+ //end add by Jin-Hsia Yang
+
+ break;
+ }
+ case Element.SECTION:
+ case Element.CHAPTER: {
+ // Chapters and Sections only differ in their constructor
+ // so we cast both to a Section
+ Section section = (Section) element;
+
+ boolean hasTitle = section.title() != null;
+
+ // if the section is a chapter, we begin a new page
+ if (section.isChapter()) {
+ newPage();
+ }
+ // otherwise, we begin a new line
+ else {
+ newLine();
+ }
+
+ if (hasTitle) {
+ float fith = indentTop() - currentHeight;
+ int rotation = pageSize.getRotation();
+ if (rotation == 90 || rotation == 180)
+ fith = pageSize.height() - fith;
+ PdfDestination destination = new PdfDestination(PdfDestination.FITH, fith);
+ while (currentOutline.level() >= section.depth()) {
+ currentOutline = currentOutline.parent();
+ }
+ PdfOutline outline = new PdfOutline(currentOutline, destination, section.getBookmarkTitle(), section.isBookmarkOpen());
+ currentOutline = outline;
+ }
+
+ // some values are set
+ carriageReturn();
+ indentLeft += section.indentationLeft();
+ indentRight += section.indentationRight();
+
+ PdfPageEvent pageEvent = writer.getPageEvent();
+ if (pageEvent != null)
+ if (element.type() == Element.CHAPTER)
+ pageEvent.onChapter(writer, this, indentTop() - currentHeight, section.title());
+ else
+ pageEvent.onSection(writer, this, indentTop() - currentHeight, section.depth(), section.title());
+
+ // the title of the section (if any has to be printed)
+ if (hasTitle) {
+ isParagraph = false;
+ add(section.title());
+ isParagraph = true;
+ }
+ indentLeft += section.indentation();
+ // we process the section
+ element.process(this);
+ // some parameters are set back to normal again
+ indentLeft -= section.indentationLeft() + section.indentation();
+ indentRight -= section.indentationRight();
+
+ if (pageEvent != null)
+ if (element.type() == Element.CHAPTER)
+ pageEvent.onChapterEnd(writer, this, indentTop() - currentHeight);
+ else
+ pageEvent.onSectionEnd(writer, this, indentTop() - currentHeight);
+
+ break;
+ }
+ case Element.LIST: {
+ // we cast the element to a List
+ List list = (List) element;
+ // we adjust the document
+ listIndentLeft += list.indentationLeft();
+ indentRight += list.indentationRight();
+ // we process the items in the list
+ element.process(this);
+ // some parameters are set back to normal again
+ listIndentLeft -= list.indentationLeft();
+ indentRight -= list.indentationRight();
+ break;
+ }
+ case Element.LISTITEM: {
+ // we cast the element to a ListItem
+ ListItem listItem = (ListItem) element;
+
+ float spacingBefore = listItem.spacingBefore();
+ if (spacingBefore != 0) {
+ leading = spacingBefore;
+ carriageReturn();
+ if (!pageEmpty) {
+ /*
+ * Don't add spacing before a paragraph if it's the first
+ * on the page
+ */
+ Chunk space = new Chunk(" ");
+ space.process(this);
+ carriageReturn();
+ }
+ }
+
+ // we adjust the document
+ alignment = listItem.alignment();
+ listIndentLeft += listItem.indentationLeft();
+ indentRight += listItem.indentationRight();
+ leading = listItem.leading();
+ carriageReturn();
+ // we prepare the current line to be able to show us the listsymbol
+ line.setListItem(listItem);
+ // we process the item
+ element.process(this);
+
+ float spacingAfter = listItem.spacingAfter();
+ if (spacingAfter != 0) {
+ leading = spacingAfter;
+ carriageReturn();
+ if (currentHeight + line.height() + leading < indentTop() - indentBottom()) {
+ /*
+ * Only add spacing after a paragraph if the extra
+ * spacing fits on the page.
+ */
+ Chunk space = new Chunk(" ");
+ space.process(this);
+ carriageReturn();
+ }
+ leading = listItem.leading(); // restore original leading
+ }
+
+ // if the last line is justified, it should be aligned to the left
+ // if (line.hasToBeJustified()) {
+ // line.resetAlignment();
+ // }
+ // some parameters are set back to normal again
+ carriageReturn();
+ listIndentLeft -= listItem.indentationLeft();
+ indentRight -= listItem.indentationRight();
+ break;
+ }
+ case Element.RECTANGLE: {
+ Rectangle rectangle = (Rectangle) element;
+ graphics.rectangle(rectangle);
+ pageEmpty = false;
+ break;
+ }
+ case Element.PTABLE: {
+ PdfPTable ptable = (PdfPTable)element;
+ if (ptable.size() <= ptable.getHeaderRows())
+ break; //nothing to do
+
+ // before every table, we add a new line and flush all lines
+ ensureNewLine();
+ flushLines();
+ addPTable(ptable);
+ pageEmpty = false;
+ break;
+ }
+ case Element.MULTI_COLUMN_TEXT: {
+ ensureNewLine();
+ flushLines();
+ MultiColumnText multiText = (MultiColumnText) element;
+ float height = multiText.write(writer.getDirectContent(), this, indentTop() - currentHeight);
+ currentHeight += height;
+ text.moveText(0, -1f* height);
+ pageEmpty = false;
+ break;
+ }
+ case Element.TABLE : {
+
+ /**
+ * This is a list of people who worked on the Table functionality.
+ * To see who did what, please check the CVS repository:
+ *
+ * Leslie Baski
+ * Matt Benson
+ * Francesco De Milato
+ * David Freels
+ * Bruno Lowagie
+ * Veerendra Namineni
+ * Geert Poels
+ * Tom Ring
+ * Paulo Soares
+ * Gerald Fehringer
+ * Steve Appling
+ * Karsten Klein
+ */
+
+ PdfTable table;
+ if (element instanceof PdfTable) {
+ // Already pre-rendered
+ table = (PdfTable)element;
+ table.updateRowAdditions();
+ } else if (element instanceof SimpleTable) {
+ PdfPTable ptable = ((SimpleTable)element).createPdfPTable();
+ if (ptable.size() <= ptable.getHeaderRows())
+ break; //nothing to do
+
+ // before every table, we add a new line and flush all lines
+ ensureNewLine();
+ flushLines();
+ addPTable(ptable);
+ pageEmpty = false;
+ break;
+ } else if (element instanceof Table) {
+ try {
+ PdfPTable ptable = ((Table)element).createPdfPTable();
+ if (ptable.size() <= ptable.getHeaderRows())
+ break; //nothing to do
+
+ // before every table, we add a new line and flush all lines
+ ensureNewLine();
+ flushLines();
+ addPTable(ptable);
+ pageEmpty = false;
+ break;
+ }
+ catch(BadElementException bee) {
+ // constructing the PdfTable
+ // Before the table, add a blank line using offset or default leading
+ float offset = ((Table)element).getOffset();
+ if (Float.isNaN(offset))
+ offset = leading;
+ carriageReturn();
+ lines.add(new PdfLine(indentLeft(), indentRight(), alignment, offset));
+ currentHeight += offset;
+ table = getPdfTable((Table)element, false);
+ }
+ } else {
+ return false;
+ }
+ add(table, false);
+ break;
+ }
+ case Element.JPEG:
+ case Element.IMGRAW:
+ case Element.IMGTEMPLATE: {
+ //carriageReturn(); suggestion by Marc Campforts
+ add((Image) element);
+ break;
+ }
+ case Element.GRAPHIC: {
+ Graphic graphic = (Graphic) element;
+ graphic.processAttributes(indentLeft(), indentBottom(), indentRight(), indentTop(), indentTop() - currentHeight);
+ graphics.add(graphic);
+ pageEmpty = false;
+ break;
+ }
+ default:
+ return false;
+ }
+ lastElementType = element.type();
+ return true;
+ }
+ catch(Exception e) {
+ throw new DocumentException(e);
+ }
+ }
+
+ // methods to add Content
+
+ /**
+ * Adds an image to the document.
+ * @param image the Image
to add
+ * @throws PdfException on error
+ * @throws DocumentException on error
+ */
+
+ private void add(Image image) throws PdfException, DocumentException {
+
+ if (image.hasAbsolutePosition()) {
+ graphics.addImage(image);
+ pageEmpty = false;
+ return;
+ }
+
+ // if there isn't enough room for the image on this page, save it for the next page
+ if (currentHeight != 0 && indentTop() - currentHeight - image.scaledHeight() < indentBottom()) {
+ if (!strictImageSequence && imageWait == null) {
+ imageWait = image;
+ return;
+ }
+ newPage();
+ if (currentHeight != 0 && indentTop() - currentHeight - image.scaledHeight() < indentBottom()) {
+ imageWait = image;
+ return;
+ }
+ }
+ pageEmpty = false;
+ // avoid endless loops
+ if (image == imageWait)
+ imageWait = null;
+ boolean textwrap = (image.alignment() & Image.TEXTWRAP) == Image.TEXTWRAP
+ && !((image.alignment() & Image.MIDDLE) == Image.MIDDLE);
+ boolean underlying = (image.alignment() & Image.UNDERLYING) == Image.UNDERLYING;
+ float diff = leading / 2;
+ if (textwrap) {
+ diff += leading;
+ }
+ float lowerleft = indentTop() - currentHeight - image.scaledHeight() -diff;
+ float mt[] = image.matrix();
+ float startPosition = indentLeft() - mt[4];
+ if ((image.alignment() & Image.RIGHT) == Image.RIGHT) startPosition = indentRight() - image.scaledWidth() - mt[4];
+ if ((image.alignment() & Image.MIDDLE) == Image.MIDDLE) startPosition = indentLeft() + ((indentRight() - indentLeft() - image.scaledWidth()) / 2) - mt[4];
+ if (image.hasAbsoluteX()) startPosition = image.absoluteX();
+ if (textwrap) {
+ if (imageEnd < 0 || imageEnd < currentHeight + image.scaledHeight() + diff) {
+ imageEnd = currentHeight + image.scaledHeight() + diff;
+ }
+ if ((image.alignment() & Image.RIGHT) == Image.RIGHT) {
+ // indentation suggested by Pelikan Stephan
+ imageIndentRight += image.scaledWidth() + image.indentationLeft();
+ }
+ else {
+ // indentation suggested by Pelikan Stephan
+ imageIndentLeft += image.scaledWidth() + image.indentationRight();
+ }
+ }
+ else {
+ if ((image.alignment() & Image.RIGHT) == Image.RIGHT) startPosition -= image.indentationRight();
+ else if ((image.alignment() & Image.LEFT) == Image.LEFT) startPosition += image.indentationLeft();
+ else if ((image.alignment() & Image.MIDDLE) == Image.MIDDLE) startPosition += image.indentationLeft() - image.indentationRight();
+ }
+ graphics.addImage(image, mt[0], mt[1], mt[2], mt[3], startPosition, lowerleft - mt[5]);
+ if (!(textwrap || underlying)) {
+ currentHeight += image.scaledHeight() + diff;
+ flushLines();
+ text.moveText(0, - (image.scaledHeight() + diff));
+ newLine();
+ }
+ }
+
+ /**
+ * Initializes a page.
+ * PdfInfo
-object.
+ *
+ * @return PdfInfo
+ */
+
+ PdfInfo getInfo() {
+ return info;
+ }
+
+ /**
+ * Gets the
bytePdfCatalog
-object.
+ *
+ * @param pages an indirect reference to this document pages
+ * @return PdfCatalog
+ */
+
+ PdfCatalog getCatalog(PdfIndirectReference pages) {
+ PdfCatalog catalog;
+ if (rootOutline.getKids().size() > 0) {
+ catalog = new PdfCatalog(pages, rootOutline.indirectReference(), writer);
+ }
+ else
+ catalog = new PdfCatalog(pages, writer);
+ if (openActionName != null) {
+ PdfAction action = getLocalGotoAction(openActionName);
+ catalog.setOpenAction(action);
+ }
+ else if (openActionAction != null)
+ catalog.setOpenAction(openActionAction);
+
+ if (additionalActions != null) {
+ catalog.setAdditionalActions(additionalActions);
+ }
+
+ if (pageLabels != null)
+ catalog.setPageLabels(pageLabels);
+ catalog.addNames(localDestinations, documentJavaScript, documentFileAttachment, writer);
+ catalog.setViewerPreferences(viewerPreferences);
+ if (acroForm.isValid()) {
+ try {
+ catalog.setAcroForm(writer.addToBody(acroForm).getIndirectReference());
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+ return catalog;
+ }
+
+ // methods concerning the layout
+
+ /**
+ * Returns the bottomvalue of a Table
if it were added to this document.
+ *
+ * @param table the table that may or may not be added to this document
+ * @return a bottom value
+ */
+
+ float bottom(Table table) {
+// where will the table begin?
+ float h = (currentHeight > 0) ? indentTop() - currentHeight - 2f * leading : indentTop();
+ // constructing a PdfTable
+ PdfTable tmp = getPdfTable(table, false);
+ return tmp.bottom();
+ }
+
+ /**
+ * Checks if a PdfPTable
fits the current page of the PdfDocument
.
+ *
+ * @param table the table that has to be checked
+ * @param margin a certain margin
+ * @return true
if the PdfPTable
fits the page, false
otherwise.
+ */
+
+ boolean fitsPage(PdfPTable table, float margin) {
+ if (!table.isLockedWidth()) {
+ float totalWidth = (indentRight() - indentLeft()) * table.getWidthPercentage() / 100;
+ table.setTotalWidth(totalWidth);
+ }
+ // ensuring that a new line has been started.
+ ensureNewLine();
+ return table.getTotalHeight() <= indentTop() - currentHeight - indentBottom() - margin;
+ }
+
+
+ /**
+ * Gets the current vertical page position.
+ * @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects
+ * for elements that do not terminate the lines they've started because those lines will get
+ * terminated.
+ * @return The current vertical page position.
+ */
+ public float getVerticalPosition(boolean ensureNewLine) {
+ // ensuring that a new line has been started.
+ if (ensureNewLine) {
+ ensureNewLine();
+ }
+ return top() - currentHeight - indentTop;
+ }
+
+ /**
+ * Ensures that a new line has been started.
+ */
+ private void ensureNewLine() {
+ try {
+ if ((lastElementType == Element.PHRASE) ||
+ (lastElementType == Element.CHUNK)) {
+ newLine();
+ flushLines();
+ }
+ } catch (DocumentException ex) {
+ throw new ExceptionConverter(ex);
+ }
+ }
+
+ /**
+ * Gets the indentation on the left side.
+ *
+ * @return a margin
+ */
+
+ private float indentLeft() {
+ return left(indentLeft + listIndentLeft + imageIndentLeft);
+ }
+
+ /**
+ * Gets the indentation on the right side.
+ *
+ * @return a margin
+ */
+
+ private float indentRight() {
+ return right(indentRight + imageIndentRight);
+ }
+
+ /**
+ * Gets the indentation on the top side.
+ *
+ * @return a margin
+ */
+
+ private float indentTop() {
+ return top(indentTop);
+ }
+
+ /**
+ * Gets the indentation on the bottom side.
+ *
+ * @return a margin
+ */
+
+ float indentBottom() {
+ return bottom(indentBottom);
+ }
+
+ /**
+ * Adds a named outline to the document .
+ * @param outline the outline to be added
+ * @param name the name of this local destination
+ */
+ void addOutline(PdfOutline outline, String name) {
+ localDestination(name, outline.getPdfDestination());
+ }
+
+ /**
+ * Gets the AcroForm object.
+ * @return the PdfAcroform object of the PdfDocument
+ */
+
+ public PdfAcroForm getAcroForm() {
+ return acroForm;
+ }
+
+ /**
+ * Gets the root outline. All the outlines must be created with a parent.
+ * The first level is created with this outline.
+ * @return the root outline
+ */
+ public PdfOutline getRootOutline() {
+ return rootOutline;
+ }
+
+ /**
+ * Writes a text line to the document. It takes care of all the attributes.
+ * text
argument must be in text object scope (beginText()
).
+ * @param line the line to be written
+ * @param text the PdfContentByte
where the text will be written to
+ * @param graphics the PdfContentByte
where the graphics will be written to
+ * @param currentValues the current font and extra spacing values
+ * @param ratio
+ * @throws DocumentException on error
+ */
+ void writeLineToContent(PdfLine line, PdfContentByte text, PdfContentByte graphics, Object currentValues[], float ratio) throws DocumentException {
+ PdfFont currentFont = (PdfFont)(currentValues[0]);
+ float lastBaseFactor = ((Float)(currentValues[1])).floatValue();
+ PdfChunk chunk;
+ int numberOfSpaces;
+ int lineLen;
+ boolean isJustified;
+ float hangingCorrection = 0;
+ float hScale = 1;
+ float lastHScale = Float.NaN;
+ float baseWordSpacing = 0;
+ float baseCharacterSpacing = 0;
+
+ numberOfSpaces = line.numberOfSpaces();
+ lineLen = line.toString().length();
+ // does the line need to be justified?
+ isJustified = line.hasToBeJustified() && (numberOfSpaces != 0 || lineLen > 1);
+ if (isJustified) {
+ if (line.isNewlineSplit() && line.widthLeft() >= (lastBaseFactor * (ratio * numberOfSpaces + lineLen - 1))) {
+ if (line.isRTL()) {
+ text.moveText(line.widthLeft() - lastBaseFactor * (ratio * numberOfSpaces + lineLen - 1), 0);
+ }
+ baseWordSpacing = ratio * lastBaseFactor;
+ baseCharacterSpacing = lastBaseFactor;
+ }
+ else {
+ float width = line.widthLeft();
+ PdfChunk last = line.getChunk(line.size() - 1);
+ if (last != null) {
+ String s = last.toString();
+ char c;
+ if (s.length() > 0 && hangingPunctuation.indexOf((c = s.charAt(s.length() - 1))) >= 0) {
+ float oldWidth = width;
+ width += last.font().width(c) * 0.4f;
+ hangingCorrection = width - oldWidth;
+ }
+ }
+ float baseFactor = width / (ratio * numberOfSpaces + lineLen - 1);
+ baseWordSpacing = ratio * baseFactor;
+ baseCharacterSpacing = baseFactor;
+ lastBaseFactor = baseFactor;
+ }
+ }
+
+ int lastChunkStroke = line.getLastStrokeChunk();
+ int chunkStrokeIdx = 0;
+ float xMarker = text.getXTLM();
+ float baseXMarker = xMarker;
+ float yMarker = text.getYTLM();
+ boolean adjustMatrix = false;
+
+ // looping over all the chunks in 1 line
+ for (Iterator j = line.iterator(); j.hasNext(); ) {
+ chunk = (PdfChunk) j.next();
+ Color color = chunk.color();
+ hScale = 1;
+
+ if (chunkStrokeIdx <= lastChunkStroke) {
+ float width;
+ if (isJustified) {
+ width = chunk.getWidthCorrected(baseCharacterSpacing, baseWordSpacing);
+ }
+ else
+ width = chunk.width();
+ if (chunk.isStroked()) {
+ PdfChunk nextChunk = line.getChunk(chunkStrokeIdx + 1);
+ if (chunk.isAttribute(Chunk.BACKGROUND)) {
+ float subtract = lastBaseFactor;
+ if (nextChunk != null && nextChunk.isAttribute(Chunk.BACKGROUND))
+ subtract = 0;
+ if (nextChunk == null)
+ subtract += hangingCorrection;
+ float fontSize = chunk.font().size();
+ float ascender = chunk.font().getFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
+ float descender = chunk.font().getFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
+ Object bgr[] = (Object[])chunk.getAttribute(Chunk.BACKGROUND);
+ graphics.setColorFill((Color)bgr[0]);
+ float extra[] = (float[])bgr[1];
+ graphics.rectangle(xMarker - extra[0],
+ yMarker + descender - extra[1] + chunk.getTextRise(),
+ width - subtract + extra[0] + extra[2],
+ ascender - descender + extra[1] + extra[3]);
+ graphics.fill();
+ graphics.setGrayFill(0);
+ }
+ if (chunk.isAttribute(Chunk.UNDERLINE)) {
+ float subtract = lastBaseFactor;
+ if (nextChunk != null && nextChunk.isAttribute(Chunk.UNDERLINE))
+ subtract = 0;
+ if (nextChunk == null)
+ subtract += hangingCorrection;
+ Object unders[][] = (Object[][])chunk.getAttribute(Chunk.UNDERLINE);
+ Color scolor = null;
+ for (int k = 0; k < unders.length; ++k) {
+ Object obj[] = unders[k];
+ scolor = (Color)obj[0];
+ float ps[] = (float[])obj[1];
+ if (scolor == null)
+ scolor = color;
+ if (scolor != null)
+ graphics.setColorStroke(scolor);
+ float fsize = chunk.font().size();
+ graphics.setLineWidth(ps[0] + fsize * ps[1]);
+ float shift = ps[2] + fsize * ps[3];
+ int cap2 = (int)ps[4];
+ if (cap2 != 0)
+ graphics.setLineCap(cap2);
+ graphics.moveTo(xMarker, yMarker + shift);
+ graphics.lineTo(xMarker + width - subtract, yMarker + shift);
+ graphics.stroke();
+ if (scolor != null)
+ graphics.resetGrayStroke();
+ if (cap2 != 0)
+ graphics.setLineCap(0);
+ }
+ graphics.setLineWidth(1);
+ }
+ if (chunk.isAttribute(Chunk.ACTION)) {
+ float subtract = lastBaseFactor;
+ if (nextChunk != null && nextChunk.isAttribute(Chunk.ACTION))
+ subtract = 0;
+ if (nextChunk == null)
+ subtract += hangingCorrection;
+ text.addAnnotation(new PdfAnnotation(writer, xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size(), (PdfAction)chunk.getAttribute(Chunk.ACTION)));
+ }
+ if (chunk.isAttribute(Chunk.REMOTEGOTO)) {
+ float subtract = lastBaseFactor;
+ if (nextChunk != null && nextChunk.isAttribute(Chunk.REMOTEGOTO))
+ subtract = 0;
+ if (nextChunk == null)
+ subtract += hangingCorrection;
+ Object obj[] = (Object[])chunk.getAttribute(Chunk.REMOTEGOTO);
+ String filename = (String)obj[0];
+ if (obj[1] instanceof String)
+ remoteGoto(filename, (String)obj[1], xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
+ else
+ remoteGoto(filename, ((Integer)obj[1]).intValue(), xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
+ }
+ if (chunk.isAttribute(Chunk.LOCALGOTO)) {
+ float subtract = lastBaseFactor;
+ if (nextChunk != null && nextChunk.isAttribute(Chunk.LOCALGOTO))
+ subtract = 0;
+ if (nextChunk == null)
+ subtract += hangingCorrection;
+ localGoto((String)chunk.getAttribute(Chunk.LOCALGOTO), xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
+ }
+ if (chunk.isAttribute(Chunk.LOCALDESTINATION)) {
+ float subtract = lastBaseFactor;
+ if (nextChunk != null && nextChunk.isAttribute(Chunk.LOCALDESTINATION))
+ subtract = 0;
+ if (nextChunk == null)
+ subtract += hangingCorrection;
+ localDestination((String)chunk.getAttribute(Chunk.LOCALDESTINATION), new PdfDestination(PdfDestination.XYZ, xMarker, yMarker + chunk.font().size(), 0));
+ }
+ if (chunk.isAttribute(Chunk.GENERICTAG)) {
+ float subtract = lastBaseFactor;
+ if (nextChunk != null && nextChunk.isAttribute(Chunk.GENERICTAG))
+ subtract = 0;
+ if (nextChunk == null)
+ subtract += hangingCorrection;
+ Rectangle rect = new Rectangle(xMarker, yMarker, xMarker + width - subtract, yMarker + chunk.font().size());
+ PdfPageEvent pev = writer.getPageEvent();
+ if (pev != null)
+ pev.onGenericTag(writer, this, rect, (String)chunk.getAttribute(Chunk.GENERICTAG));
+ }
+ if (chunk.isAttribute(Chunk.PDFANNOTATION)) {
+ float subtract = lastBaseFactor;
+ if (nextChunk != null && nextChunk.isAttribute(Chunk.PDFANNOTATION))
+ subtract = 0;
+ if (nextChunk == null)
+ subtract += hangingCorrection;
+ float fontSize = chunk.font().size();
+ float ascender = chunk.font().getFont().getFontDescriptor(BaseFont.ASCENT, fontSize);
+ float descender = chunk.font().getFont().getFontDescriptor(BaseFont.DESCENT, fontSize);
+ PdfAnnotation annot = PdfFormField.shallowDuplicate((PdfAnnotation)chunk.getAttribute(Chunk.PDFANNOTATION));
+ annot.put(PdfName.RECT, new PdfRectangle(xMarker, yMarker + descender, xMarker + width - subtract, yMarker + ascender));
+ text.addAnnotation(annot);
+ }
+ float params[] = (float[])chunk.getAttribute(Chunk.SKEW);
+ Float hs = (Float)chunk.getAttribute(Chunk.HSCALE);
+ if (params != null || hs != null) {
+ float b = 0, c = 0;
+ if (params != null) {
+ b = params[0];
+ c = params[1];
+ }
+ if (hs != null)
+ hScale = hs.floatValue();
+ text.setTextMatrix(hScale, b, c, 1, xMarker, yMarker);
+ }
+ if (chunk.isImage()) {
+ Image image = chunk.getImage();
+ float matrix[] = image.matrix();
+ matrix[Image.CX] = xMarker + chunk.getImageOffsetX() - matrix[Image.CX];
+ matrix[Image.CY] = yMarker + chunk.getImageOffsetY() - matrix[Image.CY];
+ graphics.addImage(image, matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+ text.moveText(xMarker + lastBaseFactor + image.scaledWidth() - text.getXTLM(), 0);
+ }
+ }
+ xMarker += width;
+ ++chunkStrokeIdx;
+ }
+
+ if (chunk.font().compareTo(currentFont) != 0) {
+ currentFont = chunk.font();
+ text.setFontAndSize(currentFont.getFont(), currentFont.size());
+ }
+ float rise = 0;
+ Object textRender[] = (Object[])chunk.getAttribute(Chunk.TEXTRENDERMODE);
+ int tr = 0;
+ float strokeWidth = 1;
+ Color strokeColor = null;
+ Float fr = (Float)chunk.getAttribute(Chunk.SUBSUPSCRIPT);
+ if (textRender != null) {
+ tr = ((Integer)textRender[0]).intValue() & 3;
+ if (tr != PdfContentByte.TEXT_RENDER_MODE_FILL)
+ text.setTextRenderingMode(tr);
+ if (tr == PdfContentByte.TEXT_RENDER_MODE_STROKE || tr == PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE) {
+ strokeWidth = ((Float)textRender[1]).floatValue();
+ if (strokeWidth != 1)
+ text.setLineWidth(strokeWidth);
+ strokeColor = (Color)textRender[2];
+ if (strokeColor == null)
+ strokeColor = color;
+ if (strokeColor != null)
+ text.setColorStroke(strokeColor);
+ }
+ }
+ if (fr != null)
+ rise = fr.floatValue();
+ if (color != null)
+ text.setColorFill(color);
+ if (rise != 0)
+ text.setTextRise(rise);
+ if (chunk.isImage()) {
+ adjustMatrix = true;
+ }
+ // If it is a CJK chunk or Unicode TTF we will have to simulate the
+ // space adjustment.
+ else if (isJustified && numberOfSpaces > 0 && chunk.isSpecialEncoding()) {
+ if (hScale != lastHScale) {
+ lastHScale = hScale;
+ text.setWordSpacing(baseWordSpacing / hScale);
+ text.setCharacterSpacing(baseCharacterSpacing / hScale);
+ }
+ String s = chunk.toString();
+ int idx = s.indexOf(' ');
+ if (idx < 0)
+ text.showText(chunk.toString());
+ else {
+ float spaceCorrection = - baseWordSpacing * 1000f / chunk.font.size() / hScale;
+ PdfTextArray textArray = new PdfTextArray(s.substring(0, idx));
+ int lastIdx = idx;
+ while ((idx = s.indexOf(' ', lastIdx + 1)) >= 0) {
+ textArray.add(spaceCorrection);
+ textArray.add(s.substring(lastIdx, idx));
+ lastIdx = idx;
+ }
+ textArray.add(spaceCorrection);
+ textArray.add(s.substring(lastIdx));
+ text.showText(textArray);
+ }
+ }
+ else {
+ if (isJustified && hScale != lastHScale) {
+ lastHScale = hScale;
+ text.setWordSpacing(baseWordSpacing / hScale);
+ text.setCharacterSpacing(baseCharacterSpacing / hScale);
+ }
+ text.showText(chunk.toString());
+ }
+
+ if (rise != 0)
+ text.setTextRise(0);
+ if (color != null)
+ text.resetRGBColorFill();
+ if (tr != PdfContentByte.TEXT_RENDER_MODE_FILL)
+ text.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL);
+ if (strokeColor != null)
+ text.resetRGBColorStroke();
+ if (strokeWidth != 1)
+ text.setLineWidth(1);
+ if (chunk.isAttribute(Chunk.SKEW) || chunk.isAttribute(Chunk.HSCALE)) {
+ adjustMatrix = true;
+ text.setTextMatrix(xMarker, yMarker);
+ }
+ }
+ if (isJustified) {
+ text.setWordSpacing(0);
+ text.setCharacterSpacing(0);
+ if (line.isNewlineSplit())
+ lastBaseFactor = 0;
+ }
+ if (adjustMatrix)
+ text.moveText(baseXMarker - text.getXTLM(), 0);
+ currentValues[0] = currentFont;
+ currentValues[1] = new Float(lastBaseFactor);
+ }
+
+ /**
+ * Implements a link to other part of the document. The jump will
+ * be made to a local destination with the same name, that must exist.
+ * @param name the name for this link
+ * @param llx the lower left x corner of the activation area
+ * @param lly the lower left y corner of the activation area
+ * @param urx the upper right x corner of the activation area
+ * @param ury the upper right y corner of the activation area
+ */
+ void localGoto(String name, float llx, float lly, float urx, float ury) {
+ PdfAction action = getLocalGotoAction(name);
+ annotations.add(new PdfAnnotation(writer, llx, lly, urx, ury, action));
+ }
+
+ PdfAction getLocalGotoAction(String name) {
+ PdfAction action;
+ Object obj[] = (Object[])localDestinations.get(name);
+ if (obj == null)
+ obj = new Object[3];
+ if (obj[0] == null) {
+ if (obj[1] == null) {
+ obj[1] = writer.getPdfIndirectReference();
+ }
+ action = new PdfAction((PdfIndirectReference)obj[1]);
+ obj[0] = action;
+ localDestinations.put(name, obj);
+ }
+ else {
+ action = (PdfAction)obj[0];
+ }
+ return action;
+ }
+
+ /**
+ * The local destination to where a local goto with the same
+ * name will jump to.
+ * @param name the name of this local destination
+ * @param destination the PdfDestination
with the jump coordinates
+ * @return true
if the local destination was added,
+ * false
if a local destination with the same name
+ * already existed
+ */
+ boolean localDestination(String name, PdfDestination destination) {
+ Object obj[] = (Object[])localDestinations.get(name);
+ if (obj == null)
+ obj = new Object[3];
+ if (obj[2] != null)
+ return false;
+ obj[2] = destination;
+ localDestinations.put(name, obj);
+ destination.addPage(writer.getCurrentPage());
+ return true;
+ }
+
+ /**
+ * Implements a link to another document.
+ * @param filename the filename for the remote document
+ * @param name the name to jump to
+ * @param llx the lower left x corner of the activation area
+ * @param lly the lower left y corner of the activation area
+ * @param urx the upper right x corner of the activation area
+ * @param ury the upper right y corner of the activation area
+ */
+ void remoteGoto(String filename, String name, float llx, float lly, float urx, float ury) {
+ annotations.add(new PdfAnnotation(writer, llx, lly, urx, ury, new PdfAction(filename, name)));
+ }
+
+ /**
+ * Implements a link to another document.
+ * @param filename the filename for the remote document
+ * @param page the page to jump to
+ * @param llx the lower left x corner of the activation area
+ * @param lly the lower left y corner of the activation area
+ * @param urx the upper right x corner of the activation area
+ * @param ury the upper right y corner of the activation area
+ */
+ void remoteGoto(String filename, int page, float llx, float lly, float urx, float ury) {
+ writer.addAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, new PdfAction(filename, page)));
+ }
+
+ /** Sets the viewer preferences as the sum of several constants.
+ * @param preferences the viewer preferences
+ * @see PdfWriter#setViewerPreferences
+ */
+
+ public void setViewerPreferences(int preferences) {
+ viewerPreferences |= preferences;
+ }
+
+ /** Implements an action in an area.
+ * @param action the PdfAction
+ * @param llx the lower left x corner of the activation area
+ * @param lly the lower left y corner of the activation area
+ * @param urx the upper right x corner of the activation area
+ * @param ury the upper right y corner of the activation area
+ */
+ void setAction(PdfAction action, float llx, float lly, float urx, float ury) {
+ writer.addAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, action));
+ }
+
+ void setOpenAction(String name) {
+ openActionName = name;
+ openActionAction = null;
+ }
+
+ void setOpenAction(PdfAction action) {
+ openActionAction = action;
+ openActionName = null;
+ }
+
+ void addAdditionalAction(PdfName actionType, PdfAction action) {
+ if (additionalActions == null) {
+ additionalActions = new PdfDictionary();
+ }
+ if (action == null)
+ additionalActions.remove(actionType);
+ else
+ additionalActions.put(actionType, action);
+ if (additionalActions.size() == 0)
+ additionalActions = null;
+ }
+
+ void setPageLabels(PdfPageLabels pageLabels) {
+ this.pageLabels = pageLabels;
+ }
+
+ void addJavaScript(PdfAction js) {
+ if (js.get(PdfName.JS) == null)
+ throw new RuntimeException("Only JavaScript actions are allowed.");
+ try {
+ documentJavaScript.add(writer.addToBody(js).getIndirectReference());
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ void setCropBoxSize(Rectangle crop) {
+ setBoxSize("crop", crop);
+ }
+
+ void setBoxSize(String boxName, Rectangle size) {
+ if (size == null)
+ boxSize.remove(boxName);
+ else
+ boxSize.put(boxName, new PdfRectangle(size));
+ }
+
+ void addCalculationOrder(PdfFormField formField) {
+ acroForm.addCalculationOrder(formField);
+ }
+
+ /**
+ * Gives the size of a trim, art, crop or bleed box, or null if not defined.
+ * @param boxName crop, trim, art or bleed
+ */
+ Rectangle getBoxSize(String boxName) {
+ PdfRectangle r = (PdfRectangle)thisBoxSize.get(boxName);
+ if (r != null) return r.getRectangle();
+ return null;
+ }
+
+ void setSigFlags(int f) {
+ acroForm.setSigFlags(f);
+ }
+
+ void addFormFieldRaw(PdfFormField field) {
+ annotations.add(field);
+ ArrayList kids = field.getKids();
+ if (kids != null) {
+ for (int k = 0; k < kids.size(); ++k)
+ addFormFieldRaw((PdfFormField)kids.get(k));
+ }
+ }
+
+ void addAnnotation(PdfAnnotation annot) {
+ pageEmpty = false;
+ if (annot.isForm()) {
+ PdfFormField field = (PdfFormField)annot;
+ if (field.getParent() == null)
+ addFormFieldRaw(field);
+ }
+ else
+ annotations.add(annot);
+ }
+
+ /**
+ * Sets the display duration for the page (for presentations)
+ * @param seconds the number of seconds to display the page
+ */
+ void setDuration(int seconds) {
+ if (seconds > 0)
+ this.duration=seconds;
+ else
+ this.duration=-1;
+ }
+
+ /**
+ * Sets the transition for the page
+ * @param transition the PdfTransition object
+ */
+ void setTransition(PdfTransition transition) {
+ this.transition=transition;
+ }
+
+ void setPageAction(PdfName actionType, PdfAction action) {
+ if (pageAA == null) {
+ pageAA = new PdfDictionary();
+ }
+ pageAA.put(actionType, action);
+ }
+
+ /** Getter for property strictImageSequence.
+ * @return Value of property strictImageSequence.
+ *
+ */
+ boolean isStrictImageSequence() {
+ return this.strictImageSequence;
+ }
+
+ /** Setter for property strictImageSequence.
+ * @param strictImageSequence New value of property strictImageSequence.
+ *
+ */
+ void setStrictImageSequence(boolean strictImageSequence) {
+ this.strictImageSequence = strictImageSequence;
+ }
+
+ void setPageEmpty(boolean pageEmpty) {
+ this.pageEmpty = pageEmpty;
+ }
+ /**
+ * Method added by Pelikan Stephan
+ * @see com.lowagie.text.DocListener#clearTextWrap()
+ */
+ public void clearTextWrap() throws DocumentException {
+ super.clearTextWrap();
+ float tmpHeight = imageEnd - currentHeight;
+ if (line != null) {
+ tmpHeight += line.height();
+ }
+ if ((imageEnd > -1) && (tmpHeight > 0)) {
+ carriageReturn();
+ currentHeight += tmpHeight;
+ }
+ }
+
+ ArrayList getDocumentJavaScript() {
+ return documentJavaScript;
+ }
+
+ /**
+ * @see com.lowagie.text.DocListener#setMarginMirroring(boolean)
+ */
+ public boolean setMarginMirroring(boolean MarginMirroring) {
+ if (writer != null && writer.isPaused()) {
+ return false;
+ }
+ return super.setMarginMirroring(MarginMirroring);
+ }
+
+ void setThumbnail(Image image) throws PdfException, DocumentException {
+ thumb = writer.getImageReference(writer.addDirectImageSimple(image));
+ }
+
+ void addFileAttachment(String description, PdfFileSpecification fs) throws IOException {
+ if (description == null)
+ description = "";
+ fs.put(PdfName.DESC, new PdfString(description, PdfObject.TEXT_UNICODE));
+ if (description.length() == 0)
+ description = "Unnamed";
+ String fn = PdfEncodings.convertToString(new PdfString(description, PdfObject.TEXT_UNICODE).getBytes(), null);
+ int k = 0;
+ while (documentFileAttachment.containsKey(fn)) {
+ ++k;
+ fn = PdfEncodings.convertToString(new PdfString(description + " " + k, PdfObject.TEXT_UNICODE).getBytes(), null);
+ }
+ documentFileAttachment.put(fn, fs.getReference());
+ }
+
+ HashMap getDocumentFileAttachment() {
+ return documentFileAttachment;
+ }
+
+ static PdfAnnotation convertAnnotation(PdfWriter writer, Annotation annot) throws IOException {
+ switch(annot.annotationType()) {
+ case Annotation.URL_NET:
+ return new PdfAnnotation(writer, annot.llx(), annot.lly(), annot.urx(), annot.ury(), new PdfAction((URL) annot.attributes().get(Annotation.URL)));
+ case Annotation.URL_AS_STRING:
+ return new PdfAnnotation(writer, annot.llx(), annot.lly(), annot.urx(), annot.ury(), new PdfAction((String) annot.attributes().get(Annotation.FILE)));
+ case Annotation.FILE_DEST:
+ return new PdfAnnotation(writer, annot.llx(), annot.lly(), annot.urx(), annot.ury(), new PdfAction((String) annot.attributes().get(Annotation.FILE), (String) annot.attributes().get(Annotation.DESTINATION)));
+ case Annotation.SCREEN:
+ boolean sparams[] = (boolean[])annot.attributes().get(Annotation.PARAMETERS);
+ String fname = (String) annot.attributes().get(Annotation.FILE);
+ String mimetype = (String) annot.attributes().get(Annotation.MIMETYPE);
+ PdfFileSpecification fs;
+ if (sparams[0])
+ fs = PdfFileSpecification.fileEmbedded(writer, fname, fname, null);
+ else
+ fs = PdfFileSpecification.fileExtern(writer, fname);
+ PdfAnnotation ann = PdfAnnotation.createScreen(writer, new Rectangle(annot.llx(), annot.lly(), annot.urx(), annot.ury()),
+ fname, fs, mimetype, sparams[1]);
+ return ann;
+ case Annotation.FILE_PAGE:
+ return new PdfAnnotation(writer, annot.llx(), annot.lly(), annot.urx(), annot.ury(), new PdfAction((String) annot.attributes().get(Annotation.FILE), ((Integer) annot.attributes().get(Annotation.PAGE)).intValue()));
+ case Annotation.NAMED_DEST:
+ return new PdfAnnotation(writer, annot.llx(), annot.lly(), annot.urx(), annot.ury(), new PdfAction(((Integer) annot.attributes().get(Annotation.NAMED)).intValue()));
+ case Annotation.LAUNCH:
+ return new PdfAnnotation(writer, annot.llx(), annot.lly(), annot.urx(), annot.ury(), new PdfAction((String) annot.attributes().get(Annotation.APPLICATION),(String) annot.attributes().get(Annotation.PARAMETERS),(String) annot.attributes().get(Annotation.OPERATION),(String) annot.attributes().get(Annotation.DEFAULTDIR)));
+ default:
+ PdfDocument doc = writer.getPdfDocument();
+ if (doc.line == null)
+ return null;
+ PdfAnnotation an = new PdfAnnotation(writer, annot.llx(doc.indentRight() - doc.line.widthLeft()), annot.lly(doc.indentTop() - doc.currentHeight), annot.urx(doc.indentRight() - doc.line.widthLeft() + 20), annot.ury(doc.indentTop() - doc.currentHeight - 20), new PdfString(annot.title()), new PdfString(annot.content()));
+ return an;
+ }
+ }
+ /**
+ * @return an XmpMetadata byte array
+ */
+ public byte[] createXmpMetadata() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ XmpWriter xmp = new XmpWriter(baos, getInfo());
+ xmp.close();
+ }
+ catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return baos.toByteArray();
+ }
+
+ int getMarkPoint() {
+ return markPoint;
+ }
+
+ void incMarkPoint() {
+ ++markPoint;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfEncodings.java b/src/main/java/com/lowagie/text/pdf/PdfEncodings.java
new file mode 100644
index 0000000..ab9c468
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfEncodings.java
@@ -0,0 +1,716 @@
+/*
+ * Copyright 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import com.lowagie.text.ExceptionConverter;
+import java.io.UnsupportedEncodingException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+/** Supports fast encodings for winansi and PDFDocEncoding.
+ * Supports conversions from CJK encodings to CID.
+ * Supports custom encodings.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfEncodings {
+ protected static final int CIDNONE = 0;
+ protected static final int CIDRANGE = 1;
+ protected static final int CIDCHAR = 2;
+
+ static final char winansiByteToChar[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 8364, 65533, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, 8249, 338, 65533, 381, 65533,
+ 65533, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, 8482, 353, 8250, 339, 65533, 382, 376,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255};
+
+ static final char pdfEncodingByteToChar[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044, 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018,
+ 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160, 0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, 65533,
+ 0x20ac, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255};
+
+ static final IntHashtable winansi = new IntHashtable();
+
+ static final IntHashtable pdfEncoding = new IntHashtable();
+
+ static final HashMap extraEncodings = new HashMap();
+
+ static {
+ for (int k = 128; k < 160; ++k) {
+ char c = winansiByteToChar[k];
+ if (c != 65533)
+ winansi.put(c, k);
+ }
+
+ for (int k = 128; k < 161; ++k) {
+ char c = pdfEncodingByteToChar[k];
+ if (c != 65533)
+ pdfEncoding.put(c, k);
+ }
+
+ addExtraEncoding("Wingdings", new WingdingsConversion());
+ addExtraEncoding("Symbol", new SymbolConversion(true));
+ addExtraEncoding("ZapfDingbats", new SymbolConversion(false));
+ addExtraEncoding("SymbolTT", new SymbolTTConversion());
+ addExtraEncoding("Cp437", new Cp437Conversion());
+ }
+
+ /** Converts a String
to a byte
representing the conversion according to the font's encoding
+ * @param encoding the encoding
+ * @param text the String
to be converted
+ */
+ public static final byte[] convertToBytes(String text, String encoding) {
+ if (text == null)
+ return new byte[0];
+ if (encoding == null || encoding.length() == 0) {
+ int len = text.length();
+ byte b[] = new byte[len];
+ for (int k = 0; k < len; ++k)
+ b[k] = (byte)text.charAt(k);
+ return b;
+ }
+ ExtraEncoding extra = null;
+ synchronized (extraEncodings) {
+ extra = (ExtraEncoding)extraEncodings.get(encoding.toLowerCase());
+ }
+ if (extra != null) {
+ byte b[] = extra.charToByte(text, encoding);
+ if (b != null)
+ return b;
+ }
+ IntHashtable hash = null;
+ if (encoding.equals(BaseFont.WINANSI))
+ hash = winansi;
+ else if (encoding.equals(PdfObject.TEXT_PDFDOCENCODING))
+ hash = pdfEncoding;
+ if (hash != null) {
+ char cc[] = text.toCharArray();
+ int len = cc.length;
+ int ptr = 0;
+ byte b[] = new byte[len];
+ int c = 0;
+ for (int k = 0; k < len; ++k) {
+ char char1 = cc[k];
+ if (char1 < 128 || (char1 >= 160 && char1 <= 255))
+ c = char1;
+ else
+ c = hash.get(char1);
+ if (c != 0)
+ b[ptr++] = (byte)c;
+ }
+ if (ptr == len)
+ return b;
+ byte b2[] = new byte[ptr];
+ System.arraycopy(b, 0, b2, 0, ptr);
+ return b2;
+ }
+ if (encoding.equals(PdfObject.TEXT_UNICODE)) {
+ // workaround for jdk 1.2.2 bug
+ char cc[] = text.toCharArray();
+ int len = cc.length;
+ byte b[] = new byte[cc.length * 2 + 2];
+ b[0] = -2;
+ b[1] = -1;
+ int bptr = 2;
+ for (int k = 0; k < len; ++k) {
+ char c = cc[k];
+ b[bptr++] = (byte)(c >> 8);
+ b[bptr++] = (byte)(c & 0xff);
+ }
+ return b;
+ }
+ try {
+ return text.getBytes(encoding);
+ }
+ catch (UnsupportedEncodingException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /** Converts a String
according
+ * to the some encoding.
+ * @param bytes the bytes to convert
+ * @param encoding the encoding
+ * @return the converted String
+ */
+ public static final String convertToString(byte bytes[], String encoding) {
+ if (bytes == null)
+ return PdfObject.NOTHING;
+ if (encoding == null || encoding.length() == 0) {
+ char c[] = new char[bytes.length];
+ for (int k = 0; k < bytes.length; ++k)
+ c[k] = (char)(bytes[k] & 0xff);
+ return new String(c);
+ }
+ ExtraEncoding extra = null;
+ synchronized (extraEncodings) {
+ extra = (ExtraEncoding)extraEncodings.get(encoding.toLowerCase());
+ }
+ if (extra != null) {
+ String text = extra.byteToChar(bytes, encoding);
+ if (text != null)
+ return text;
+ }
+ char ch[] = null;
+ if (encoding.equals(BaseFont.WINANSI))
+ ch = winansiByteToChar;
+ else if (encoding.equals(PdfObject.TEXT_PDFDOCENCODING))
+ ch = pdfEncodingByteToChar;
+ if (ch != null) {
+ int len = bytes.length;
+ char c[] = new char[len];
+ for (int k = 0; k < len; ++k) {
+ c[k] = ch[bytes[k] & 0xff];
+ }
+ return new String(c);
+ }
+ try {
+ return new String(bytes, encoding);
+ }
+ catch (UnsupportedEncodingException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /** Checks is text
only has PdfDocEncoding characters.
+ * @param text the String
to test
+ * @return true
if only PdfDocEncoding characters are present
+ */
+ public static boolean isPdfDocEncoding(String text) {
+ if (text == null)
+ return true;
+ int len = text.length();
+ for (int k = 0; k < len; ++k) {
+ char char1 = text.charAt(k);
+ if (char1 < 128 || (char1 >= 160 && char1 <= 255))
+ continue;
+ if (!pdfEncoding.containsKey(char1))
+ return false;
+ }
+ return true;
+ }
+
+ static final HashMap cmaps = new HashMap();
+ /** Assumes that '\\n' and '\\r\\n' are the newline sequences. It may not work for
+ * all CJK encodings. To be used with loadCmap().
+ */
+ public static final byte CRLF_CID_NEWLINE[][] = new byte[][]{{(byte)'\n'}, {(byte)'\r', (byte)'\n'}};
+
+ /** Clears the CJK cmaps from the cache. If name
is the
+ * empty string then all the cache is cleared. Calling this method
+ * has no consequences other than the need to reload the cmap
+ * if needed.
+ * @param name the name of the cmap to clear or all the cmaps if the empty string
+ */
+ public static void clearCmap(String name) {
+ synchronized (cmaps) {
+ if (name.length() == 0)
+ cmaps.clear();
+ else
+ cmaps.remove(name);
+ }
+ }
+
+ /** Loads a CJK cmap to the cache with the option of associating
+ * sequences to the newline.
+ * @param name the CJK cmap name
+ * @param newline the sequences to be replaced bi a newline in the resulting CID. See CRLF_CID_NEWLINE
+ */
+ public static void loadCmap(String name, byte newline[][]) {
+ try {
+ char planes[][] = null;
+ synchronized (cmaps) {
+ planes = (char[][])cmaps.get(name);
+ }
+ if (planes == null) {
+ planes = readCmap(name, newline);
+ synchronized (cmaps) {
+ cmaps.put(name, planes);
+ }
+ }
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /** Converts a byte
array encoded as name
+ * to a CID string. This is needed to reach some CJK characters
+ * that don't exist in 16 bit Unicode.byte
array to be decoded
+ * @return the CID string
+ */
+ public static String convertCmap(String name, byte seq[]) {
+ return convertCmap(name, seq, 0, seq.length);
+ }
+
+ /** Converts a byte
array encoded as name
+ * to a CID string. This is needed to reach some CJK characters
+ * that don't exist in 16 bit Unicode.
byte
array to be decoded
+ * @return the CID string
+ */
+ public static String convertCmap(String name, byte seq[], int start, int length) {
+ try {
+ char planes[][] = null;
+ synchronized (cmaps) {
+ planes = (char[][])cmaps.get(name);
+ }
+ if (planes == null) {
+ planes = readCmap(name, (byte[][])null);
+ synchronized (cmaps) {
+ cmaps.put(name, planes);
+ }
+ }
+ return decodeSequence(seq, start, length, planes);
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ static String decodeSequence(byte seq[], int start, int length, char planes[][]) {
+ StringBuffer buf = new StringBuffer();
+ int end = start + length;
+ int currentPlane = 0;
+ for (int k = start; k < end; ++k) {
+ int one = (int)seq[k] & 0xff;
+ char plane[] = planes[currentPlane];
+ int cid = plane[one];
+ if ((cid & 0x8000) == 0) {
+ buf.append((char)cid);
+ currentPlane = 0;
+ }
+ else
+ currentPlane = cid & 0x7fff;
+ }
+ return buf.toString();
+ }
+
+ static char[][] readCmap(String name, byte newline[][]) throws IOException {
+ ArrayList planes = new ArrayList();
+ planes.add(new char[256]);
+ readCmap(name, planes);
+ if (newline != null) {
+ for (int k = 0; k < newline.length; ++k)
+ encodeSequence(newline[k].length, newline[k], BaseFont.CID_NEWLINE, planes);
+ }
+ char ret[][] = new char[planes.size()][];
+ return (char[][])planes.toArray(ret);
+ }
+
+ static void readCmap(String name, ArrayList planes) throws IOException {
+ String fullName = BaseFont.RESOURCE_PATH + "cmaps/" + name;
+ InputStream in = BaseFont.getResourceStream(fullName);
+ if (in == null)
+ throw new IOException("The Cmap " + name + " was not found.");
+ encodeStream(in, planes);
+ in.close();
+ }
+
+ static void encodeStream(InputStream in, ArrayList planes) throws IOException {
+ BufferedReader rd = new BufferedReader(new InputStreamReader(in, "iso-8859-1"));
+ String line = null;
+ int state = CIDNONE;
+ byte seqs[] = new byte[7];
+ while ((line = rd.readLine()) != null) {
+ if (line.length() < 6)
+ continue;
+ switch (state) {
+ case CIDNONE: {
+ if (line.indexOf("begincidrange") >= 0)
+ state = CIDRANGE;
+ else if (line.indexOf("begincidchar") >= 0)
+ state = CIDCHAR;
+ else if (line.indexOf("usecmap") >= 0) {
+ StringTokenizer tk = new StringTokenizer(line);
+ String t = tk.nextToken();
+ readCmap(t.substring(1), planes);
+ }
+ break;
+ }
+ case CIDRANGE: {
+ if (line.indexOf("endcidrange") >= 0) {
+ state = CIDNONE;
+ break;
+ }
+ StringTokenizer tk = new StringTokenizer(line);
+ String t = tk.nextToken();
+ int size = t.length() / 2 - 1;
+ long start = Long.parseLong(t.substring(1, t.length() - 1), 16);
+ t = tk.nextToken();
+ long end = Long.parseLong(t.substring(1, t.length() - 1), 16);
+ t = tk.nextToken();
+ int cid = Integer.parseInt(t);
+ for (long k = start; k <= end; ++k) {
+ breakLong(k, size, seqs);
+ encodeSequence(size, seqs, (char)cid, planes);
+ ++cid;
+ }
+ break;
+ }
+ case CIDCHAR: {
+ if (line.indexOf("endcidchar") >= 0) {
+ state = CIDNONE;
+ break;
+ }
+ StringTokenizer tk = new StringTokenizer(line);
+ String t = tk.nextToken();
+ int size = t.length() / 2 - 1;
+ long start = Long.parseLong(t.substring(1, t.length() - 1), 16);
+ t = tk.nextToken();
+ int cid = Integer.parseInt(t);
+ breakLong(start, size, seqs);
+ encodeSequence(size, seqs, (char)cid, planes);
+ break;
+ }
+ }
+ }
+ }
+
+ static void breakLong(long n, int size, byte seqs[]) {
+ for (int k = 0; k < size; ++k) {
+ seqs[k] = (byte)(n >> ((size - 1 - k) * 8));
+ }
+ }
+
+ static void encodeSequence(int size, byte seqs[], char cid, ArrayList planes) {
+ --size;
+ int nextPlane = 0;
+ for (int idx = 0; idx < size; ++idx) {
+ char plane[] = (char[])planes.get(nextPlane);
+ int one = (int)seqs[idx] & 0xff;
+ char c = plane[one];
+ if (c != 0 && (c & 0x8000) == 0)
+ throw new RuntimeException("Inconsistent mapping.");
+ if (c == 0) {
+ planes.add(new char[256]);
+ c = (char)((planes.size() - 1) | 0x8000);
+ plane[one] = c;
+ }
+ nextPlane = c & 0x7fff;
+ }
+ char plane[] = (char[])planes.get(nextPlane);
+ int one = (int)seqs[size] & 0xff;
+ char c = plane[one];
+ if ((c & 0x8000) != 0)
+ throw new RuntimeException("Inconsistent mapping.");
+ plane[one] = cid;
+ }
+
+ /** Adds an extra encoding.
+ * @param name the name of the encoding. The encoding recognition is case insensitive
+ * @param enc the conversion class
+ */
+ public static void addExtraEncoding(String name, ExtraEncoding enc) {
+ synchronized (extraEncodings) {
+ extraEncodings.put(name.toLowerCase(), enc);
+ }
+ }
+
+ private static class WingdingsConversion implements ExtraEncoding {
+
+ public byte[] charToByte(String text, String encoding) {
+ char cc[] = text.toCharArray();
+ byte b[] = new byte[cc.length];
+ int ptr = 0;
+ int len = cc.length;
+ for (int k = 0; k < len; ++k) {
+ char c = cc[k];
+ if (c == ' ')
+ b[ptr++] = (byte)c;
+ else if (c >= '\u2701' && c <= '\u27BE') {
+ byte v = table[c - 0x2700];
+ if (v != 0)
+ b[ptr++] = v;
+ }
+ }
+ if (ptr == len)
+ return b;
+ byte b2[] = new byte[ptr];
+ System.arraycopy(b, 0, b2, 0, ptr);
+ return b2;
+ }
+
+ public String byteToChar(byte[] b, String encoding) {
+ return null;
+ }
+
+ private final static byte table[] = {
+ 0, 35, 34, 0, 0, 0, 41, 62, 81, 42,
+ 0, 0, 65, 63, 0, 0, 0, 0, 0, -4,
+ 0, 0, 0, -5, 0, 0, 0, 0, 0, 0,
+ 86, 0, 88, 89, 0, 0, 0, 0, 0, 0,
+ 0, 0, -75, 0, 0, 0, 0, 0, -74, 0,
+ 0, 0, -83, -81, -84, 0, 0, 0, 0, 0,
+ 0, 0, 0, 124, 123, 0, 0, 0, 84, 0,
+ 0, 0, 0, 0, 0, 0, 0, -90, 0, 0,
+ 0, 113, 114, 0, 0, 0, 117, 0, 0, 0,
+ 0, 0, 0, 125, 126, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, -116, -115,
+ -114, -113, -112, -111, -110, -109, -108, -107, -127, -126,
+ -125, -124, -123, -122, -121, -120, -119, -118, -116, -115,
+ -114, -113, -112, -111, -110, -109, -108, -107, -24, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, -24, -40, 0, 0, -60, -58, 0, 0, -16,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, -36,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0
+ };
+ }
+
+ private static class Cp437Conversion implements ExtraEncoding {
+ private static IntHashtable c2b = new IntHashtable();
+
+ public byte[] charToByte(String text, String encoding) {
+ char cc[] = text.toCharArray();
+ byte b[] = new byte[cc.length];
+ int ptr = 0;
+ int len = cc.length;
+ for (int k = 0; k < len; ++k) {
+ char c = cc[k];
+ if (c < ' ')
+ continue;
+ if (c < 128)
+ b[ptr++] = (byte)c;
+ else {
+ byte v = (byte)c2b.get(c);
+ if (v != 0)
+ b[ptr++] = v;
+ }
+ }
+ if (ptr == len)
+ return b;
+ byte b2[] = new byte[ptr];
+ System.arraycopy(b, 0, b2, 0, ptr);
+ return b2;
+ }
+
+ public String byteToChar(byte[] b, String encoding) {
+ int len = b.length;
+ char cc[] = new char[len];
+ int ptr = 0;
+ for (int k = 0; k < len; ++k) {
+ int c = b[k] & 0xff;
+ if (c < ' ')
+ continue;
+ if (c < 128)
+ cc[ptr++] = (char)c;
+ else {
+ char v = table[c - 128];
+ cc[ptr++] = v;
+ }
+ }
+ return new String(cc, 0, ptr);
+ }
+
+ private final static char table[] = {
+ '\u00C7', '\u00FC', '\u00E9', '\u00E2', '\u00E4', '\u00E0', '\u00E5', '\u00E7', '\u00EA', '\u00EB', '\u00E8', '\u00EF', '\u00EE', '\u00EC', '\u00C4', '\u00C5',
+ '\u00C9', '\u00E6', '\u00C6', '\u00F4', '\u00F6', '\u00F2', '\u00FB', '\u00F9', '\u00FF', '\u00D6', '\u00DC', '\u00A2', '\u00A3', '\u00A5', '\u20A7', '\u0192',
+ '\u00E1', '\u00ED', '\u00F3', '\u00FA', '\u00F1', '\u00D1', '\u00AA', '\u00BA', '\u00BF', '\u2310', '\u00AC', '\u00BD', '\u00BC', '\u00A1', '\u00AB', '\u00BB',
+ '\u2591', '\u2592', '\u2593', '\u2502', '\u2524', '\u2561', '\u2562', '\u2556', '\u2555', '\u2563', '\u2551', '\u2557', '\u255D', '\u255C', '\u255B', '\u2510',
+ '\u2514', '\u2534', '\u252C', '\u251C', '\u2500', '\u253C', '\u255E', '\u255F', '\u255A', '\u2554', '\u2569', '\u2566', '\u2560', '\u2550', '\u256C', '\u2567',
+ '\u2568', '\u2564', '\u2565', '\u2559', '\u2558', '\u2552', '\u2553', '\u256B', '\u256A', '\u2518', '\u250C', '\u2588', '\u2584', '\u258C', '\u2590', '\u2580',
+ '\u03B1', '\u00DF', '\u0393', '\u03C0', '\u03A3', '\u03C3', '\u00B5', '\u03C4', '\u03A6', '\u0398', '\u03A9', '\u03B4', '\u221E', '\u03C6', '\u03B5', '\u2229',
+ '\u2261', '\u00B1', '\u2265', '\u2264', '\u2320', '\u2321', '\u00F7', '\u2248', '\u00B0', '\u2219', '\u00B7', '\u221A', '\u207F', '\u00B2', '\u25A0', '\u00A0'
+ };
+
+ static {
+ for (int k = 0; k < table.length; ++k)
+ c2b.put(table[k], k + 128);
+ }
+ }
+
+ private static class SymbolConversion implements ExtraEncoding {
+
+ private static final IntHashtable t1 = new IntHashtable();
+ private static final IntHashtable t2 = new IntHashtable();
+ private IntHashtable translation;
+
+ SymbolConversion(boolean symbol) {
+ if (symbol)
+ translation = t1;
+ else
+ translation = t2;
+ }
+
+ public byte[] charToByte(String text, String encoding) {
+ char cc[] = text.toCharArray();
+ byte b[] = new byte[cc.length];
+ int ptr = 0;
+ int len = cc.length;
+ for (int k = 0; k < len; ++k) {
+ char c = cc[k];
+ byte v = (byte)translation.get((int)c);
+ if (v != 0)
+ b[ptr++] = v;
+ }
+ if (ptr == len)
+ return b;
+ byte b2[] = new byte[ptr];
+ System.arraycopy(b, 0, b2, 0, ptr);
+ return b2;
+ }
+
+ public String byteToChar(byte[] b, String encoding) {
+ return null;
+ }
+
+ private final static char table1[] = {
+ ' ','!','\u2200','#','\u2203','%','&','\u220b','(',')','*','+',',','-','.','/',
+ '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
+ '\u2245','\u0391','\u0392','\u03a7','\u0394','\u0395','\u03a6','\u0393','\u0397','\u0399','\u03d1','\u039a','\u039b','\u039c','\u039d','\u039f',
+ '\u03a0','\u0398','\u03a1','\u03a3','\u03a4','\u03a5','\u03c2','\u03a9','\u039e','\u03a8','\u0396','[','\u2234',']','\u22a5','_',
+ '\u0305','\u03b1','\u03b2','\u03c7','\u03b4','\u03b5','\u03d5','\u03b3','\u03b7','\u03b9','\u03c6','\u03ba','\u03bb','\u03bc','\u03bd','\u03bf',
+ '\u03c0','\u03b8','\u03c1','\u03c3','\u03c4','\u03c5','\u03d6','\u03c9','\u03be','\u03c8','\u03b6','{','|','}','~','\0',
+ '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
+ '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
+ '\u20ac','\u03d2','\u2032','\u2264','\u2044','\u221e','\u0192','\u2663','\u2666','\u2665','\u2660','\u2194','\u2190','\u2191','\u2192','\u2193',
+ '\u00b0','\u00b1','\u2033','\u2265','\u00d7','\u221d','\u2202','\u2022','\u00f7','\u2260','\u2261','\u2248','\u2026','\u2502','\u2500','\u21b5',
+ '\u2135','\u2111','\u211c','\u2118','\u2297','\u2295','\u2205','\u2229','\u222a','\u2283','\u2287','\u2284','\u2282','\u2286','\u2208','\u2209',
+ '\u2220','\u2207','\u00ae','\u00a9','\u2122','\u220f','\u221a','\u2022','\u00ac','\u2227','\u2228','\u21d4','\u21d0','\u21d1','\u21d2','\u21d3',
+ '\u25ca','\u2329','\0','\0','\0','\u2211','\u239b','\u239c','\u239d','\u23a1','\u23a2','\u23a3','\u23a7','\u23a8','\u23a9','\u23aa',
+ '\0','\u232a','\u222b','\u2320','\u23ae','\u2321','\u239e','\u239f','\u23a0','\u23a4','\u23a5','\u23a6','\u23ab','\u23ac','\u23ad','\0'
+ };
+
+ private final static char table2[] = {
+ '\u0020','\u2701','\u2702','\u2703','\u2704','\u260e','\u2706','\u2707','\u2708','\u2709','\u261b','\u261e','\u270C','\u270D','\u270E','\u270F',
+ '\u2710','\u2711','\u2712','\u2713','\u2714','\u2715','\u2716','\u2717','\u2718','\u2719','\u271A','\u271B','\u271C','\u271D','\u271E','\u271F',
+ '\u2720','\u2721','\u2722','\u2723','\u2724','\u2725','\u2726','\u2727','\u2605','\u2729','\u272A','\u272B','\u272C','\u272D','\u272E','\u272F',
+ '\u2730','\u2731','\u2732','\u2733','\u2734','\u2735','\u2736','\u2737','\u2738','\u2739','\u273A','\u273B','\u273C','\u273D','\u273E','\u273F',
+ '\u2740','\u2741','\u2742','\u2743','\u2744','\u2745','\u2746','\u2747','\u2748','\u2749','\u274A','\u274B','\u25cf','\u274D','\u25a0','\u274F',
+ '\u2750','\u2751','\u2752','\u25b2','\u25bc','\u25c6','\u2756','\u25d7','\u2758','\u2759','\u275A','\u275B','\u275C','\u275D','\u275E','\u0000',
+ '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
+ '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
+ '\u0000','\u2761','\u2762','\u2763','\u2764','\u2765','\u2766','\u2767','\u2663','\u2666','\u2665','\u2660','\u2460','\u2461','\u2462','\u2463',
+ '\u2464','\u2465','\u2466','\u2467','\u2468','\u2469','\u2776','\u2777','\u2778','\u2779','\u277A','\u277B','\u277C','\u277D','\u277E','\u277F',
+ '\u2780','\u2781','\u2782','\u2783','\u2784','\u2785','\u2786','\u2787','\u2788','\u2789','\u278A','\u278B','\u278C','\u278D','\u278E','\u278F',
+ '\u2790','\u2791','\u2792','\u2793','\u2794','\u2192','\u2194','\u2195','\u2798','\u2799','\u279A','\u279B','\u279C','\u279D','\u279E','\u279F',
+ '\u27A0','\u27A1','\u27A2','\u27A3','\u27A4','\u27A5','\u27A6','\u27A7','\u27A8','\u27A9','\u27AA','\u27AB','\u27AC','\u27AD','\u27AE','\u27AF',
+ '\u0000','\u27B1','\u27B2','\u27B3','\u27B4','\u27B5','\u27B6','\u27B7','\u27B8','\u27B9','\u27BA','\u27BB','\u27BC','\u27BD','\u27BE','\u0000'
+ };
+
+ static {
+ for (int k = 0; k < table1.length; ++k) {
+ int v = (int)table1[k];
+ if (v != 0)
+ t1.put(v, k + 32);
+ }
+ for (int k = 0; k < table2.length; ++k) {
+ int v = (int)table2[k];
+ if (v != 0)
+ t2.put(v, k + 32);
+ }
+ }
+ }
+
+ private static class SymbolTTConversion implements ExtraEncoding {
+
+ public byte[] charToByte(String text, String encoding) {
+ char ch[] = text.toCharArray();
+ byte b[] = new byte[ch.length];
+ int ptr = 0;
+ int len = ch.length;
+ for (int k = 0; k < len; ++k) {
+ char c = ch[k];
+ if ((c & 0xff00) == 0 || (c & 0xff00) == 0xf000)
+ b[ptr++] = (byte)c;
+ }
+ if (ptr == len)
+ return b;
+ byte b2[] = new byte[ptr];
+ System.arraycopy(b, 0, b2, 0, ptr);
+ return b2;
+ }
+
+ public String byteToChar(byte[] b, String encoding) {
+ return null;
+ }
+
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfEncryption.java b/src/main/java/com/lowagie/text/pdf/PdfEncryption.java
new file mode 100644
index 0000000..18046a8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfEncryption.java
@@ -0,0 +1,390 @@
+/*
+ * $Id: PdfEncryption.java,v 1.54 2006/05/03 11:35:12 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.security.MessageDigest;
+import com.lowagie.text.ExceptionConverter;
+
+/**
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ * @author Kazuya Ujihara
+ */
+public class PdfEncryption {
+
+ static final byte pad[] = {
+ (byte)0x28, (byte)0xBF, (byte)0x4E, (byte)0x5E, (byte)0x4E, (byte)0x75,
+ (byte)0x8A, (byte)0x41, (byte)0x64, (byte)0x00, (byte)0x4E, (byte)0x56,
+ (byte)0xFF, (byte)0xFA, (byte)0x01, (byte)0x08, (byte)0x2E, (byte)0x2E,
+ (byte)0x00, (byte)0xB6, (byte)0xD0, (byte)0x68, (byte)0x3E, (byte)0x80,
+ (byte)0x2F, (byte)0x0C, (byte)0xA9, (byte)0xFE, (byte)0x64, (byte)0x53,
+ (byte)0x69, (byte)0x7A};
+
+ byte state[] = new byte[256];
+ int x;
+ int y;
+ /** The encryption key for a particular object/generation */
+ byte key[];
+ /** The encryption key length for a particular object/generation */
+ int keySize;
+ /** The global encryption key */
+ byte mkey[];
+ /** Work area to prepare the object/generation bytes */
+ byte extra[] = new byte[5];
+ /** The message digest algorithm MD5 */
+ MessageDigest md5;
+ /** The encryption key for the owner */
+ byte ownerKey[] = new byte[32];
+ /** The encryption key for the user */
+ byte userKey[] = new byte[32];
+ int permissions;
+ byte documentID[];
+ static long seq = System.currentTimeMillis();
+
+ public PdfEncryption() {
+ try {
+ md5 = MessageDigest.getInstance("MD5");
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ public PdfEncryption(PdfEncryption enc) {
+ this();
+ mkey = (byte[])enc.mkey.clone();
+ ownerKey = (byte[])enc.ownerKey.clone();
+ userKey = (byte[])enc.userKey.clone();
+ permissions = enc.permissions;
+ if (enc.documentID != null)
+ documentID = (byte[])enc.documentID.clone();
+ }
+
+ /**
+ */
+ private byte[] padPassword(byte userPassword[]) {
+ byte userPad[] = new byte[32];
+ if (userPassword == null) {
+ System.arraycopy(pad, 0, userPad, 0, 32);
+ }
+ else {
+ System.arraycopy(userPassword, 0, userPad, 0, Math.min(userPassword.length, 32));
+ if (userPassword.length < 32)
+ System.arraycopy(pad, 0, userPad, userPassword.length, 32 - userPassword.length);
+ }
+
+ return userPad;
+ }
+
+ /**
+ */
+ private byte[] computeOwnerKey(byte userPad[], byte ownerPad[], int keylength, int revision) {
+ byte ownerKey[] = new byte[32];
+
+ byte digest[] = md5.digest(ownerPad);
+ if (revision == 3) {
+ byte mkey[] = new byte[keylength/8];
+ // only use for the input as many bit as the key consists of
+ for (int k = 0; k < 50; ++k)
+ System.arraycopy(md5.digest(), 0, digest, 0, mkey.length);
+ System.arraycopy(userPad, 0, ownerKey, 0, 32);
+ for (int i = 0; i < 20; ++i) {
+ for (int j = 0; j < mkey.length ; ++j)
+ mkey[j] = (byte)(digest[j] ^ i);
+ prepareRC4Key(mkey);
+ encryptRC4(ownerKey);
+ }
+ }
+ else {
+ prepareRC4Key(digest, 0, 5);
+ encryptRC4(userPad, ownerKey);
+ }
+
+ return ownerKey;
+ }
+
+ /**
+ *
+ * ownerKey, documentID must be setuped
+ */
+ private void setupGlobalEncryptionKey(byte[] documentID, byte userPad[], byte ownerKey[], int permissions, int keylength, int revision) {
+ this.documentID = documentID;
+ this.ownerKey = ownerKey;
+ this.permissions = permissions;
+ // use variable keylength
+ mkey = new byte[keylength/8];
+
+ //fixed by ujihara in order to follow PDF refrence
+ md5.reset();
+ md5.update(userPad);
+ md5.update(ownerKey);
+
+ byte ext[] = new byte[4];
+ ext[0] = (byte)permissions;
+ ext[1] = (byte)(permissions >> 8);
+ ext[2] = (byte)(permissions >> 16);
+ ext[3] = (byte)(permissions >> 24);
+ md5.update(ext, 0, 4);
+ if (documentID != null) md5.update(documentID);
+
+ byte digest[] = new byte[mkey.length];
+ System.arraycopy(md5.digest(), 0, digest, 0, mkey.length);
+
+ // only use the really needed bits as input for the hash
+ if (revision == 3){
+ for (int k = 0; k < 50; ++k)
+ System.arraycopy(md5.digest(digest), 0, digest, 0, mkey.length);
+ }
+
+
+ System.arraycopy(digest, 0, mkey, 0, mkey.length);
+ }
+
+ /**
+ *
+ * mkey must be setuped
+ */
+ // use the revision to choose the setup method
+ private void setupUserKey(int revision) {
+ if (revision == 3) {
+ md5.update(pad);
+ byte digest[] = md5.digest(documentID);
+ System.arraycopy(digest, 0, userKey, 0, 16);
+ for (int k = 16; k < 32; ++k)
+ userKey[k] = 0;
+ for (int i = 0; i < 20; ++i) {
+ for (int j = 0; j < mkey.length; ++j)
+ digest[j] = (byte)(mkey[j] ^ i);
+ prepareRC4Key(digest, 0, mkey.length);
+ encryptRC4(userKey, 0, 16);
+ }
+ }
+ else {
+ prepareRC4Key(mkey);
+ encryptRC4(pad, userKey);
+ }
+ }
+
+ // gets keylength and revision and uses revison to choose the initial values for permissions
+ public void setupAllKeys(byte userPassword[], byte ownerPassword[], int permissions, int keylength, int revision) {
+ if (ownerPassword == null || ownerPassword.length == 0)
+ ownerPassword = md5.digest(createDocumentId());
+ permissions |= revision==3 ? 0xfffff0c0 : 0xffffffc0;
+ permissions &= 0xfffffffc;
+ //PDF refrence 3.5.2 Standard Security Handler, Algorithum 3.3-1
+ //If there is no owner password, use the user password instead.
+ byte userPad[] = padPassword(userPassword);
+ byte ownerPad[] = padPassword(ownerPassword);
+
+ this.ownerKey = computeOwnerKey(userPad, ownerPad, keylength, revision);
+ documentID = createDocumentId();
+ setupByUserPad(this.documentID, userPad, this.ownerKey, permissions, keylength, revision);
+ }
+
+ // calls the setupAllKeys function with default values to keep the old behavior and signature
+ public void setupAllKeys(byte userPassword[], byte ownerPassword[], int permissions, boolean strength128Bits) {
+ setupAllKeys(userPassword, ownerPassword, permissions, strength128Bits?128:40, strength128Bits?3:2);
+ }
+
+ public static byte[] createDocumentId() {
+ MessageDigest md5;
+ try {
+ md5 = MessageDigest.getInstance("MD5");
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ long time = System.currentTimeMillis();
+ long mem = Runtime.getRuntime().freeMemory();
+ String s = time + "+" + mem + "+" + (seq++);
+ return md5.digest(s.getBytes());
+ }
+
+ /**
+ */
+ // the following functions use the new parameters for the call of the functions
+ // resp. they map the call of the old functions to the changed in order to keep the
+ // old behaviour and signatures
+ public void setupByUserPassword(byte[] documentID, byte userPassword[], byte ownerKey[], int permissions, boolean strength128Bits) {
+ setupByUserPassword(documentID, userPassword, ownerKey, permissions, strength128Bits?128:40, strength128Bits?3:2);
+ }
+
+ /**
+ */
+ public void setupByUserPassword(byte[] documentID, byte userPassword[], byte ownerKey[], int permissions, int keylength, int revision) {
+ setupByUserPad(documentID, padPassword(userPassword), ownerKey, permissions, keylength, revision);
+ }
+
+ /**
+ */
+ private void setupByUserPad(byte[] documentID, byte userPad[], byte ownerKey[], int permissions, int keylength, int revision) {
+ setupGlobalEncryptionKey(documentID, userPad, ownerKey, permissions, keylength, revision);
+ setupUserKey(revision);
+ }
+
+ /**
+ */
+ public void setupByOwnerPassword(byte[] documentID, byte ownerPassword[], byte userKey[], byte ownerKey[], int permissions, boolean strength128Bits) {
+ setupByOwnerPassword(documentID, ownerPassword, userKey, ownerKey, permissions, strength128Bits?128:40, strength128Bits?3:2);
+ }
+
+ /**
+ */
+ public void setupByOwnerPassword(byte[] documentID, byte ownerPassword[], byte userKey[], byte ownerKey[], int permissions, int keylength, int revision) {
+ setupByOwnerPad(documentID, padPassword(ownerPassword), userKey, ownerKey, permissions, keylength, revision);
+ }
+
+ private void setupByOwnerPad(byte[] documentID, byte ownerPad[], byte userKey[], byte ownerKey[], int permissions, int keylength, int revision) {
+ byte userPad[] = computeOwnerKey(ownerKey, ownerPad, keylength, revision); //userPad will be set in this.ownerKey
+ setupGlobalEncryptionKey(documentID, userPad, ownerKey, permissions, keylength, revision); //step 3
+ setupUserKey(revision);
+ }
+
+ public void prepareKey() {
+ prepareRC4Key(key, 0, keySize);
+ }
+
+ public void setHashKey(int number, int generation) {
+ md5.reset(); //added by ujihara
+ extra[0] = (byte)number;
+ extra[1] = (byte)(number >> 8);
+ extra[2] = (byte)(number >> 16);
+ extra[3] = (byte)generation;
+ extra[4] = (byte)(generation >> 8);
+ md5.update(mkey);
+ key = md5.digest(extra);
+ keySize = mkey.length + 5;
+ if (keySize > 16)
+ keySize = 16;
+ }
+
+ public static PdfObject createInfoId(byte id[]) {
+ ByteBuffer buf = new ByteBuffer(90);
+ buf.append('[').append('<');
+ for (int k = 0; k < 16; ++k)
+ buf.appendHex(id[k]);
+ buf.append('>').append('<');
+ id = createDocumentId();
+ for (int k = 0; k < 16; ++k)
+ buf.appendHex(id[k]);
+ buf.append('>').append(']');
+ return new PdfLiteral(buf.toByteArray());
+ }
+
+ public PdfDictionary getEncryptionDictionary() {
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.FILTER, PdfName.STANDARD);
+ dic.put(PdfName.O, new PdfLiteral(PdfContentByte.escapeString(ownerKey)));
+ dic.put(PdfName.U, new PdfLiteral(PdfContentByte.escapeString(userKey)));
+ dic.put(PdfName.P, new PdfNumber(permissions));
+ if (mkey.length > 5) {
+ dic.put(PdfName.V, new PdfNumber(2));
+ dic.put(PdfName.R, new PdfNumber(3));
+ dic.put(PdfName.LENGTH, new PdfNumber(128));
+ }
+ else {
+ dic.put(PdfName.V, new PdfNumber(1));
+ dic.put(PdfName.R, new PdfNumber(2));
+ }
+ return dic;
+ }
+
+ public void prepareRC4Key(byte key[]) {
+ prepareRC4Key(key, 0, key.length);
+ }
+
+ public void prepareRC4Key(byte key[], int off, int len) {
+ int index1 = 0;
+ int index2 = 0;
+ for (int k = 0; k < 256; ++k)
+ state[k] = (byte)k;
+ x = 0;
+ y = 0;
+ byte tmp;
+ for (int k = 0; k < 256; ++k) {
+ index2 = (key[index1 + off] + state[k] + index2) & 255;
+ tmp = state[k];
+ state[k] = state[index2];
+ state[index2] = tmp;
+ index1 = (index1 + 1) % len;
+ }
+ }
+
+ public void encryptRC4(byte dataIn[], int off, int len, byte dataOut[]) {
+ int length = len + off;
+ byte tmp;
+ for (int k = off; k < length; ++k) {
+ x = (x + 1) & 255;
+ y = (state[x] + y) & 255;
+ tmp = state[x];
+ state[x] = state[y];
+ state[y] = tmp;
+ dataOut[k] = (byte)(dataIn[k] ^ state[(state[x] + state[y]) & 255]);
+ }
+ }
+
+ public void encryptRC4(byte data[], int off, int len) {
+ encryptRC4(data, off, len, data);
+ }
+
+ public void encryptRC4(byte dataIn[], byte dataOut[]) {
+ encryptRC4(dataIn, 0, dataIn.length, dataOut);
+ }
+
+ public void encryptRC4(byte data[]) {
+ encryptRC4(data, 0, data.length, data);
+ }
+
+ public PdfObject getFileID() {
+ return createInfoId(documentID);
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfEncryptionStream.java b/src/main/java/com/lowagie/text/pdf/PdfEncryptionStream.java
new file mode 100644
index 0000000..99170ca
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfEncryptionStream.java
@@ -0,0 +1,85 @@
+/*
+ * $Id: PdfEncryptionStream.java,v 1.3 2005/02/17 09:20:54 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2004 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999-2005 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000-2005 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.FilterOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+public class PdfEncryptionStream extends FilterOutputStream {
+
+ protected PdfEncryption enc;
+ private byte buf[] = new byte[1];
+
+ public PdfEncryptionStream(OutputStream out, PdfEncryption enc) {
+ super(out);
+ this.enc = enc;
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ if ((off | len | (b.length - (len + off)) | (off + len)) < 0)
+ throw new IndexOutOfBoundsException();
+ enc.encryptRC4(b, off, len);
+ out.write(b, off, len);
+ }
+
+ public void close() throws IOException {
+ }
+
+ public void write(int b) throws IOException {
+ buf[0] = (byte)b;
+ write(buf);
+ }
+
+ public void flush() throws IOException {
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfEncryptor.java b/src/main/java/com/lowagie/text/pdf/PdfEncryptor.java
new file mode 100644
index 0000000..7bea4d5
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfEncryptor.java
@@ -0,0 +1,176 @@
+/*
+/*
+ * Copyright 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import com.lowagie.text.DocumentException;
+import java.util.HashMap;
+
+/** This class takes any PDF and returns exactly the same but
+ * encrypted. All the content, links, outlines, etc, are kept.
+ * It is also possible to change the info dictionary.
+ */
+public class PdfEncryptor {
+
+ private PdfEncryptor(){
+ }
+
+ /** Entry point to encrypt a PDF document. The encryption parameters are the same as in
+ * PdfWriter
. The userPassword and the
+ * ownerPassword can be null or have zero length. In this case the ownerPassword
+ * is replaced by a random string. The open permissions for the document can be
+ * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
+ * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
+ * The permissions can be combined by ORing them.
+ * @param reader the read PDF
+ * @param os the output destination
+ * @param userPassword the user password. Can be null or empty
+ * @param ownerPassword the owner password. Can be null or empty
+ * @param permissions the user permissions
+ * @param strength128Bits true
for 128 bit key length, false
for 40 bit key length
+ * @throws DocumentException on error
+ * @throws IOException on error */
+ public static void encrypt(PdfReader reader, OutputStream os, byte userPassword[], byte ownerPassword[], int permissions, boolean strength128Bits) throws DocumentException, IOException {
+ PdfStamper stamper = new PdfStamper(reader, os);
+ stamper.setEncryption(userPassword, ownerPassword, permissions, strength128Bits);
+ stamper.close();
+ }
+
+ /** Entry point to encrypt a PDF document. The encryption parameters are the same as in
+ * PdfWriter
. The userPassword and the
+ * ownerPassword can be null or have zero length. In this case the ownerPassword
+ * is replaced by a random string. The open permissions for the document can be
+ * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
+ * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
+ * The permissions can be combined by ORing them.
+ * @param reader the read PDF
+ * @param os the output destination
+ * @param userPassword the user password. Can be null or empty
+ * @param ownerPassword the owner password. Can be null or empty
+ * @param permissions the user permissions
+ * @param strength128Bits true
for 128 bit key length, false
for 40 bit key length
+ * @param newInfo an optional String
map to add or change
+ * the info dictionary. Entries with null
+ * values delete the key in the original info dictionary
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public static void encrypt(PdfReader reader, OutputStream os, byte userPassword[], byte ownerPassword[], int permissions, boolean strength128Bits, HashMap newInfo) throws DocumentException, IOException {
+ PdfStamper stamper = new PdfStamper(reader, os);
+ stamper.setEncryption(userPassword, ownerPassword, permissions, strength128Bits);
+ stamper.setMoreInfo(newInfo);
+ stamper.close();
+ }
+
+ /** Entry point to encrypt a PDF document. The encryption parameters are the same as in
+ * PdfWriter
. The userPassword and the
+ * ownerPassword can be null or have zero length. In this case the ownerPassword
+ * is replaced by a random string. The open permissions for the document can be
+ * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
+ * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
+ * The permissions can be combined by ORing them.
+ * @param reader the read PDF
+ * @param os the output destination
+ * @param strength true
for 128 bit key length, false
for 40 bit key length
+ * @param userPassword the user password. Can be null or empty
+ * @param ownerPassword the owner password. Can be null or empty
+ * @param permissions the user permissions
+ * @throws DocumentException on error
+ * @throws IOException on error */
+ public static void encrypt(PdfReader reader, OutputStream os, boolean strength, String userPassword, String ownerPassword, int permissions) throws DocumentException, IOException {
+ PdfStamper stamper = new PdfStamper(reader, os);
+ stamper.setEncryption(strength, userPassword, ownerPassword, permissions);
+ stamper.close();
+ }
+
+ /** Entry point to encrypt a PDF document. The encryption parameters are the same as in
+ * PdfWriter
. The userPassword and the
+ * ownerPassword can be null or have zero length. In this case the ownerPassword
+ * is replaced by a random string. The open permissions for the document can be
+ * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
+ * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
+ * The permissions can be combined by ORing them.
+ * @param reader the read PDF
+ * @param os the output destination
+ * @param strength true
for 128 bit key length, false
for 40 bit key length
+ * @param userPassword the user password. Can be null or empty
+ * @param ownerPassword the owner password. Can be null or empty
+ * @param permissions the user permissions
+ * @param newInfo an optional String
map to add or change
+ * the info dictionary. Entries with null
+ * values delete the key in the original info dictionary
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public static void encrypt(PdfReader reader, OutputStream os, boolean strength, String userPassword, String ownerPassword, int permissions, HashMap newInfo) throws DocumentException, IOException {
+ PdfStamper stamper = new PdfStamper(reader, os);
+ stamper.setEncryption(strength, userPassword, ownerPassword, permissions);
+ stamper.setMoreInfo(newInfo);
+ stamper.close();
+ }
+
+ /**
+ * Give you a verbose analysis of the permissions.
+ * @param permissions the permissions value of a PDF file
+ * @return a String that explains the meaning of the permissions value
+ */
+ public static String getPermissionsVerbose(int permissions) {
+ StringBuffer buf = new StringBuffer("Allowed:");
+ if ((PdfWriter.AllowPrinting & permissions) == PdfWriter.AllowPrinting) buf.append(" Printing");
+ if ((PdfWriter.AllowModifyContents & permissions) == PdfWriter.AllowModifyContents) buf.append(" Modify contents");
+ if ((PdfWriter.AllowCopy & permissions) == PdfWriter.AllowCopy) buf.append(" Copy");
+ if ((PdfWriter.AllowModifyAnnotations & permissions) == PdfWriter.AllowModifyAnnotations) buf.append(" Modify annotations");
+ if ((PdfWriter.AllowFillIn & permissions) == PdfWriter.AllowFillIn) buf.append(" Fill in");
+ if ((PdfWriter.AllowScreenReaders & permissions) == PdfWriter.AllowScreenReaders) buf.append(" Screen readers");
+ if ((PdfWriter.AllowAssembly & permissions) == PdfWriter.AllowAssembly) buf.append(" Assembly");
+ if ((PdfWriter.AllowDegradedPrinting & permissions) == PdfWriter.AllowDegradedPrinting) buf.append(" Degraded printing");
+ return buf.toString();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfException.java b/src/main/java/com/lowagie/text/pdf/PdfException.java
new file mode 100644
index 0000000..486ece0
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfException.java
@@ -0,0 +1,86 @@
+/*
+ * $Id: PdfException.java,v 1.54 2005/05/04 14:31:43 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.DocumentException;
+
+/**
+ * Signals that an unspecified problem while constructing a PDF document.
+ *
+ * @see BadPdfFormatException
+ */
+
+public class PdfException extends DocumentException {
+
+ // constructors
+
+ public PdfException(Exception ex) {
+ super(ex);
+ }
+
+ /**
+ * Constructs a PdfException
whithout a message.
+ */
+
+ PdfException() {
+ super();
+ }
+
+/**
+ * Constructs a PdfException
with a message.
+ *
+ * @param message a message describing the exception
+ */
+
+ PdfException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfFileSpecification.java b/src/main/java/com/lowagie/text/pdf/PdfFileSpecification.java
new file mode 100644
index 0000000..cec2b20
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfFileSpecification.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2003 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.*;
+import java.net.URL;
+/** Specifies a file or an URL. The file can be extern or embedded.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfFileSpecification extends PdfDictionary {
+ protected PdfWriter writer;
+ protected PdfIndirectReference ref;
+
+ /** Creates a new instance of PdfFileSpecification. The static methods are preferred. */
+ public PdfFileSpecification() {
+ super(PdfName.FILESPEC);
+ }
+
+ /**
+ * Creates a file specification of type URL.
+ * @param writer the PdfWriter
+ * @param url the URL
+ * @return the file specification
+ */
+ public static PdfFileSpecification url(PdfWriter writer, String url) {
+ PdfFileSpecification fs = new PdfFileSpecification();
+ fs.writer = writer;
+ fs.put(PdfName.FS, PdfName.URL);
+ fs.put(PdfName.F, new PdfString(url));
+ return fs;
+ }
+
+ /**
+ * Creates a file specification with the file embedded. The file may
+ * come from the file system or from a byte array. The data is flate compressed.
+ * @param writer the PdfWriter
+ * @param filePath the file path
+ * @param fileDisplay the file information that is presented to the user
+ * @param fileStore the byte array with the file. If it is not null
+ * it takes precedence over filePath
+ * @throws IOException on error
+ * @return the file specification
+ */
+ public static PdfFileSpecification fileEmbedded(PdfWriter writer, String filePath, String fileDisplay, byte fileStore[]) throws IOException {
+ return fileEmbedded(writer, filePath, fileDisplay, fileStore, true);
+ }
+
+
+ /**
+ * Creates a file specification with the file embedded. The file may
+ * come from the file system or from a byte array.
+ * @param writer the PdfWriter
+ * @param filePath the file path
+ * @param fileDisplay the file information that is presented to the user
+ * @param fileStore the byte array with the file. If it is not null
+ * it takes precedence over filePath
+ * @param compress sets the compression on the data. Multimedia content will benefit little
+ * from compression
+ * @throws IOException on error
+ * @return the file specification
+ */
+ public static PdfFileSpecification fileEmbedded(PdfWriter writer, String filePath, String fileDisplay, byte fileStore[], boolean compress) throws IOException {
+ PdfFileSpecification fs = new PdfFileSpecification();
+ fs.writer = writer;
+ fs.put(PdfName.F, new PdfString(fileDisplay));
+ PdfStream stream;
+ InputStream in = null;
+ PdfIndirectReference ref;
+ PdfIndirectReference refFileLength;
+ try {
+ refFileLength = writer.getPdfIndirectReference();
+ if (fileStore == null) {
+ File file = new File(filePath);
+ if (file.canRead()) {
+ in = new FileInputStream(filePath);
+ }
+ else {
+ if (filePath.startsWith("file:/") || filePath.startsWith("http://") || filePath.startsWith("https://") || filePath.startsWith("jar:")) {
+ in = new URL(filePath).openStream();
+ }
+ else {
+ in = BaseFont.getResourceStream(filePath);
+ if (in == null)
+ throw new IOException(filePath + " not found as file or resource.");
+ }
+ }
+ stream = new PdfStream(in, writer);
+ }
+ else
+ stream = new PdfStream(fileStore);
+ stream.put(PdfName.TYPE, PdfName.EMBEDDEDFILE);
+ if (compress)
+ stream.flateCompress();
+ stream.put(PdfName.PARAMS, refFileLength);
+ ref = writer.addToBody(stream).getIndirectReference();
+ if (fileStore == null) {
+ stream.writeLength();
+ }
+ PdfDictionary params = new PdfDictionary();
+ params.put(PdfName.SIZE, new PdfNumber(stream.getRawLength()));
+ writer.addToBody(params, refFileLength);
+ }
+ finally {
+ if (in != null)
+ try{in.close();}catch(Exception e){}
+ }
+ PdfDictionary f = new PdfDictionary();
+ f.put(PdfName.F, ref);
+ fs.put(PdfName.EF, f);
+ return fs;
+ }
+
+ /**
+ * Creates a file specification for an external file.
+ * @param writer the PdfWriter
+ * @param filePath the file path
+ * @return the file specification
+ */
+ public static PdfFileSpecification fileExtern(PdfWriter writer, String filePath) {
+ PdfFileSpecification fs = new PdfFileSpecification();
+ fs.writer = writer;
+ fs.put(PdfName.F, new PdfString(filePath));
+ return fs;
+ }
+
+ /**
+ * Gets the indirect reference to this file specification.
+ * Multiple invocations will retrieve the same value.
+ * @throws IOException on error
+ * @return the indirect reference
+ */
+ public PdfIndirectReference getReference() throws IOException {
+ if (ref != null)
+ return ref;
+ ref = writer.addToBody(this).getIndirectReference();
+ return ref;
+ }
+
+ /**
+ * Sets the file name (the key /F) string as an hex representation
+ * to support multi byte file names. The name must heve th slash and
+ * backslash escaped according to the file specification rules
+ * @param fileName the file name as a byte array
+ */
+ public void setMultiByteFileName(byte fileName[]) {
+ put(PdfName.F, new PdfString(fileName).setHexWriting(true));
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfFont.java b/src/main/java/com/lowagie/text/pdf/PdfFont.java
new file mode 100644
index 0000000..0f8e3c8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfFont.java
@@ -0,0 +1,188 @@
+/*
+ * $Id: PdfFont.java,v 1.64 2005/05/04 14:32:21 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.Image;
+import com.lowagie.text.ExceptionConverter;
+
+/**
+ * PdfFont
is the Pdf Font object.
+ *
+ * Limitation: in this class only base 14 Type 1 fonts (courier, courier bold, courier oblique,
+ * courier boldoblique, helvetica, helvetica bold, helvetica oblique, helvetica boldoblique,
+ * symbol, times roman, times bold, times italic, times bolditalic, zapfdingbats) and their
+ * standard encoding (standard, MacRoman, (MacExpert,) WinAnsi) are supported.
+ * An indirect object is an object that has been labeled so that it can be referenced by
+ * other objects. Any type of
+ * Any object used as an element of an array or as a value in a dictionary may be specified
+ * by either a direct object of an indirect reference. An indirect reference is a reference
+ * to an indirect object, and consists of the indirect object's object number, generation number
+ * and the R keyword.
+ * The alignment of the last line of for instance a
+ * This is only necessary for the first line of a
+ * A name, like a string, is a sequence of characters. It must begin with a slash
+ * followed by a sequence of ASCII characters in the range 32 through 136 except
+ * %, (, ), [, ], <, >, {, }, / and #. Any character except 0x00 may be included
+ * in a name by writing its twocharacter hex code, preceded by #. The maximum number
+ * of characters in a name is 127.
+ *
+ * @see PdfObject
+ * @see PdfDictionary
+ * @see BadPdfFormatException
+ */
+
+public class PdfName extends PdfObject implements Comparable{
+
+ // static membervariables (a variety of standard names used in PDF)
+
+ /** A name */
+ public static final PdfName A = new PdfName("A");
+ /** A name */
+ public static final PdfName AA = new PdfName("AA");
+ /** A name */
+ public static final PdfName ABSOLUTECALORIMETRIC = new PdfName("AbsoluteColorimetric");
+ /** A name */
+ public static final PdfName AC = new PdfName("AC");
+ /** A name */
+ public static final PdfName ACROFORM = new PdfName("AcroForm");
+ /** A name */
+ public static final PdfName ACTION = new PdfName("Action");
+ /** A name */
+ public static final PdfName ADBE_PKCS7_DETACHED = new PdfName("adbe.pkcs7.detached");
+ /** A name */
+ public static final PdfName ADBE_PKCS7_SHA1 = new PdfName("adbe.pkcs7.sha1");
+ /** A name */
+ public static final PdfName ADBE_X509_RSA_SHA1 = new PdfName("adbe.x509.rsa_sha1");
+ /** A name */
+ public static final PdfName ADOBE_PPKLITE = new PdfName("Adobe.PPKLite");
+ /** A name */
+ public static final PdfName ADOBE_PPKMS = new PdfName("Adobe.PPKMS");
+ /** A name */
+ public static final PdfName AIS = new PdfName("AIS");
+ /** A name */
+ public static final PdfName ALLPAGES = new PdfName("AllPages");
+ /** A name */
+ public static final PdfName ALTERNATE = new PdfName("Alternate");
+ /** A name */
+ public static final PdfName ANNOT = new PdfName("Annot");
+ /** A name */
+ public static final PdfName ANTIALIAS = new PdfName("AntiAlias");
+ /** A name */
+ public static final PdfName ANNOTS = new PdfName("Annots");
+ /** A name */
+ public static final PdfName AP = new PdfName("AP");
+ /** A name */
+ public static final PdfName ARTBOX = new PdfName("ArtBox");
+ /** A name */
+ public static final PdfName ASCENT = new PdfName("Ascent");
+ /** A name */
+ public static final PdfName AS = new PdfName("AS");
+ /** A name */
+ public static final PdfName ASCII85DECODE = new PdfName("ASCII85Decode");
+ /** A name */
+ public static final PdfName ASCIIHEXDECODE = new PdfName("ASCIIHexDecode");
+ /** A name */
+ public static final PdfName AUTHOR = new PdfName("Author");
+ /** A name */
+ public static final PdfName B = new PdfName("B");
+ /** A name */
+ public static final PdfName BASEENCODING = new PdfName("BaseEncoding");
+ /** A name */
+ public static final PdfName BASEFONT = new PdfName("BaseFont");
+ /** A name */
+ public static final PdfName BBOX = new PdfName("BBox");
+ /** A name */
+ public static final PdfName BC = new PdfName("BC");
+ /** A name */
+ public static final PdfName BG = new PdfName("BG");
+ /** A name */
+ public static final PdfName BIGFIVE = new PdfName("BigFive");
+ /** A name */
+ public static final PdfName BITSPERCOMPONENT = new PdfName("BitsPerComponent");
+ /** A name */
+ public static final PdfName BITSPERSAMPLE = new PdfName("BitsPerSample");
+ /** A name */
+ public static final PdfName BL = new PdfName("Bl");
+ /** A name */
+ public static final PdfName BLACKIS1 = new PdfName("BlackIs1");
+ /** A name */
+ public static final PdfName BLACKPOINT = new PdfName("BlackPoint");
+ /** A name */
+ public static final PdfName BLEEDBOX = new PdfName("BleedBox");
+ /** A name */
+ public static final PdfName BLINDS = new PdfName("Blinds");
+ /** A name */
+ public static final PdfName BM = new PdfName("BM");
+ /** A name */
+ public static final PdfName BORDER = new PdfName("Border");
+ /** A name */
+ public static final PdfName BOUNDS = new PdfName("Bounds");
+ /** A name */
+ public static final PdfName BOX = new PdfName("Box");
+ /** A name */
+ public static final PdfName BS = new PdfName("BS");
+ /** A name */
+ public static final PdfName BTN = new PdfName("Btn");
+ /** A name */
+ public static final PdfName BYTERANGE = new PdfName("ByteRange");
+ /** A name */
+ public static final PdfName C = new PdfName("C");
+ /** A name */
+ public static final PdfName C0 = new PdfName("C0");
+ /** A name */
+ public static final PdfName C1 = new PdfName("C1");
+ /** A name */
+ public static final PdfName CA = new PdfName("CA");
+ /** A name */
+ public static final PdfName ca = new PdfName("ca");
+ /** A name */
+ public static final PdfName CALGRAY = new PdfName("CalGray");
+ /** A name */
+ public static final PdfName CALRGB = new PdfName("CalRGB");
+ /** A name */
+ public static final PdfName CAPHEIGHT = new PdfName("CapHeight");
+ /** A name */
+ public static final PdfName CATALOG = new PdfName("Catalog");
+ /** A name */
+ public static final PdfName CATEGORY = new PdfName("Category");
+ /** A name */
+ public static final PdfName CCITTFAXDECODE = new PdfName("CCITTFaxDecode");
+ /** A name */
+ public static final PdfName CENTERWINDOW = new PdfName("CenterWindow");
+ /** A name */
+ public static final PdfName CERT = new PdfName("Cert");
+ /** A name */
+ public static final PdfName CH = new PdfName("Ch");
+ /** A name */
+ public static final PdfName CHARPROCS = new PdfName("CharProcs");
+ /** A name */
+ public static final PdfName CIDFONTTYPE0 = new PdfName("CIDFontType0");
+ /** A name */
+ public static final PdfName CIDFONTTYPE2 = new PdfName("CIDFontType2");
+ /** A name */
+ public static final PdfName CIDSYSTEMINFO = new PdfName("CIDSystemInfo");
+ /** A name */
+ public static final PdfName CIDTOGIDMAP = new PdfName("CIDToGIDMap");
+ /** A name */
+ public static final PdfName CIRCLE = new PdfName("Circle");
+ /** A name */
+ public static final PdfName CO = new PdfName("CO");
+ /** A name */
+ public static final PdfName COLORS = new PdfName("Colors");
+ /** A name */
+ public static final PdfName COLORSPACE = new PdfName("ColorSpace");
+ /** A name */
+ public static final PdfName COLUMNS = new PdfName("Columns");
+ /** A name */
+ public static final PdfName CONTACTINFO = new PdfName("ContactInfo");
+ /** A name */
+ public static final PdfName CONTENT = new PdfName("Content");
+ /** A name */
+ public static final PdfName CONTENTS = new PdfName("Contents");
+ /** A name */
+ public static final PdfName COORDS = new PdfName("Coords");
+ /** A name */
+ public static final PdfName COUNT = new PdfName("Count");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName COURIER = new PdfName("Courier");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName COURIER_BOLD = new PdfName("Courier-Bold");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName COURIER_OBLIQUE = new PdfName("Courier-Oblique");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName COURIER_BOLDOBLIQUE = new PdfName("Courier-BoldOblique");
+ /** A name */
+ public static final PdfName CREATIONDATE = new PdfName("CreationDate");
+ /** A name */
+ public static final PdfName CREATOR = new PdfName("Creator");
+ /** A name */
+ public static final PdfName CREATORINFO = new PdfName("CreatorInfo");
+ /** A name */
+ public static final PdfName CROPBOX = new PdfName("CropBox");
+ /** A name */
+ public static final PdfName CS = new PdfName("CS");
+ /** A name */
+ public static final PdfName D = new PdfName("D");
+ /** A name */
+ public static final PdfName DA = new PdfName("DA");
+ /** A name */
+ public static final PdfName DC = new PdfName("DC");
+ /** A name */
+ public static final PdfName DCTDECODE = new PdfName("DCTDecode");
+ /** A name */
+ public static final PdfName DECODE = new PdfName("Decode");
+ /** A name */
+ public static final PdfName DECODEPARMS = new PdfName("DecodeParms");
+ /** A name */
+ public static final PdfName DEFAULTCMYK = new PdfName("DefaultCMYK");
+ /** A name */
+ public static final PdfName DEFAULTGRAY = new PdfName("DefaultGray");
+ /** A name */
+ public static final PdfName DEFAULTRGB = new PdfName("DefaultRGB");
+ /** A name */
+ public static final PdfName DESC = new PdfName("Desc");
+ /** A name */
+ public static final PdfName DESCENDANTFONTS = new PdfName("DescendantFonts");
+ /** A name */
+ public static final PdfName DESCENT = new PdfName("Descent");
+ /** A name */
+ public static final PdfName DEST = new PdfName("Dest");
+ /** A name */
+ public static final PdfName DESTOUTPUTPROFILE = new PdfName("DestOutputProfile");
+ /** A name */
+ public static final PdfName DESTS = new PdfName("Dests");
+ /** A name */
+ public static final PdfName DEVICEGRAY = new PdfName("DeviceGray");
+ /** A name */
+ public static final PdfName DEVICERGB = new PdfName("DeviceRGB");
+ /** A name */
+ public static final PdfName DEVICECMYK = new PdfName("DeviceCMYK");
+ /** A name */
+ public static final PdfName DI = new PdfName("Di");
+ /** A name */
+ public static final PdfName DIFFERENCES = new PdfName("Differences");
+ /** A name */
+ public static final PdfName DISSOLVE = new PdfName("Dissolve");
+ /** A name */
+ public static final PdfName DIRECTION = new PdfName("Direction");
+ /** A name */
+ public static final PdfName DISPLAYDOCTITLE = new PdfName("DisplayDocTitle");
+ /** A name */
+ public static final PdfName DM = new PdfName("Dm");
+ /** A name */
+ public static final PdfName DOMAIN = new PdfName("Domain");
+ /** A name */
+ public static final PdfName DP = new PdfName("DP");
+ /** A name */
+ public static final PdfName DR = new PdfName("DR");
+ /** A name */
+ public static final PdfName DS = new PdfName("DS");
+ /** A name */
+ public static final PdfName DUR = new PdfName("Dur");
+ /** A name */
+ public static final PdfName DV = new PdfName("DV");
+ /** A name */
+ public static final PdfName DW = new PdfName("DW");
+ /** A name */
+ public static final PdfName E = new PdfName("E");
+ /** A name */
+ public static final PdfName EARLYCHANGE = new PdfName("EarlyChange");
+ /** A name */
+ public static final PdfName EF = new PdfName("EF");
+ /** A name */
+ public static final PdfName EMBEDDEDFILE = new PdfName("EmbeddedFile");
+ /** A name */
+ public static final PdfName EMBEDDEDFILES = new PdfName("EmbeddedFiles");
+ /** A name */
+ public static final PdfName ENCODE = new PdfName("Encode");
+ /** A name */
+ public static final PdfName ENCODEDBYTEALIGN = new PdfName("EncodedByteAlign");
+ /** A name */
+ public static final PdfName ENCODING = new PdfName("Encoding");
+ /** A name */
+ public static final PdfName ENCRYPT = new PdfName("Encrypt");
+ /** A name */
+ public static final PdfName ENDOFBLOCK = new PdfName("EndOfBlock");
+ /** A name */
+ public static final PdfName ENDOFLINE = new PdfName("EndOfLine");
+ /** A name */
+ public static final PdfName EXTEND = new PdfName("Extend");
+ /** A name */
+ public static final PdfName EXTGSTATE = new PdfName("ExtGState");
+ /** A name */
+ public static final PdfName EXPORT = new PdfName("Export");
+ /** A name */
+ public static final PdfName EXPORTSTATE = new PdfName("ExportState");
+ /** A name */
+ public static final PdfName EVENT = new PdfName("Event");
+ /** A name */
+ public static final PdfName F = new PdfName("F");
+ /** A name */
+ public static final PdfName FB = new PdfName("FB");
+ /** A name */
+ public static final PdfName FDECODEPARMS = new PdfName("FDecodeParms");
+ /** A name */
+ public static final PdfName FDF = new PdfName("FDF");
+ /** A name */
+ public static final PdfName FF = new PdfName("Ff");
+ /** A name */
+ public static final PdfName FFILTER = new PdfName("FFilter");
+ /** A name */
+ public static final PdfName FIELDS = new PdfName("Fields");
+ /** A name */
+ public static final PdfName FILEATTACHMENT = new PdfName("FileAttachment");
+ /** A name */
+ public static final PdfName FILESPEC = new PdfName("Filespec");
+ /** A name */
+ public static final PdfName FILTER = new PdfName("Filter");
+ /** A name */
+ public static final PdfName FIRST = new PdfName("First");
+ /** A name */
+ public static final PdfName FIRSTCHAR = new PdfName("FirstChar");
+ /** A name */
+ public static final PdfName FIRSTPAGE = new PdfName("FirstPage");
+ /** A name */
+ public static final PdfName FIT = new PdfName("Fit");
+ /** A name */
+ public static final PdfName FITH = new PdfName("FitH");
+ /** A name */
+ public static final PdfName FITV = new PdfName("FitV");
+ /** A name */
+ public static final PdfName FITR = new PdfName("FitR");
+ /** A name */
+ public static final PdfName FITB = new PdfName("FitB");
+ /** A name */
+ public static final PdfName FITBH = new PdfName("FitBH");
+ /** A name */
+ public static final PdfName FITBV = new PdfName("FitBV");
+ /** A name */
+ public static final PdfName FITWINDOW = new PdfName("FitWindow");
+ /** A name */
+ public static final PdfName FLAGS = new PdfName("Flags");
+ /** A name */
+ public static final PdfName FLATEDECODE = new PdfName("FlateDecode");
+ /** A name */
+ public static final PdfName FO = new PdfName("Fo");
+ /** A name */
+ public static final PdfName FONT = new PdfName("Font");
+ /** A name */
+ public static final PdfName FONTBBOX = new PdfName("FontBBox");
+ /** A name */
+ public static final PdfName FONTDESCRIPTOR = new PdfName("FontDescriptor");
+ /** A name */
+ public static final PdfName FONTFILE = new PdfName("FontFile");
+ /** A name */
+ public static final PdfName FONTFILE2 = new PdfName("FontFile2");
+ /** A name */
+ public static final PdfName FONTFILE3 = new PdfName("FontFile3");
+ /** A name */
+ public static final PdfName FONTMATRIX = new PdfName("FontMatrix");
+ /** A name */
+ public static final PdfName FONTNAME = new PdfName("FontName");
+ /** A name */
+ public static final PdfName FORM = new PdfName("Form");
+ /** A name */
+ public static final PdfName FORMTYPE = new PdfName("FormType");
+ /** A name */
+ public static final PdfName FREETEXT = new PdfName("FreeText");
+ /** A name */
+ public static final PdfName FRM = new PdfName("FRM");
+ /** A name */
+ public static final PdfName FS = new PdfName("FS");
+ /** A name */
+ public static final PdfName FT = new PdfName("FT");
+ /** A name */
+ public static final PdfName FULLSCREEN = new PdfName("FullScreen");
+ /** A name */
+ public static final PdfName FUNCTION = new PdfName("Function");
+ /** A name */
+ public static final PdfName FUNCTIONS = new PdfName("Functions");
+ /** A name */
+ public static final PdfName FUNCTIONTYPE = new PdfName("FunctionType");
+ /** A name of an attribute. */
+ public static final PdfName GAMMA = new PdfName("Gamma");
+ /** A name of an attribute. */
+ public static final PdfName GBK = new PdfName("GBK");
+ /** A name of an attribute. */
+ public static final PdfName GLITTER = new PdfName("Glitter");
+ /** A name of an attribute. */
+ public static final PdfName GOTO = new PdfName("GoTo");
+ /** A name of an attribute. */
+ public static final PdfName GOTOR = new PdfName("GoToR");
+ /** A name of an attribute. */
+ public static final PdfName GROUP = new PdfName("Group");
+ /** A name of an attribute. */
+ public static final PdfName GTS_PDFX = new PdfName("GTS_PDFX");
+ /** A name of an attribute. */
+ public static final PdfName GTS_PDFXVERSION = new PdfName("GTS_PDFXVersion");
+ /** A name of an attribute. */
+ public static final PdfName H = new PdfName("H");
+ /** A name of an attribute. */
+ public static final PdfName HEIGHT = new PdfName("Height");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName HELVETICA = new PdfName("Helvetica");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName HELVETICA_BOLD = new PdfName("Helvetica-Bold");
+ /** This is a static PdfName PdfName of a base 14 type 1 font */
+ public static final PdfName HELVETICA_OBLIQUE = new PdfName("Helvetica-Oblique");
+ /** This is a static PdfName PdfName of a base 14 type 1 font */
+ public static final PdfName HELVETICA_BOLDOBLIQUE = new PdfName("Helvetica-BoldOblique");
+ /** A name */
+ public static final PdfName HID = new PdfName("Hid");
+ /** A name */
+ public static final PdfName HIDE = new PdfName("Hide");
+ /** A name */
+ public static final PdfName HIDEMENUBAR = new PdfName("HideMenubar");
+ /** A name */
+ public static final PdfName HIDETOOLBAR = new PdfName("HideToolbar");
+ /** A name */
+ public static final PdfName HIDEWINDOWUI = new PdfName("HideWindowUI");
+ /** A name */
+ public static final PdfName HIGHLIGHT = new PdfName("Highlight");
+ /** A name */
+ public static final PdfName I = new PdfName("I");
+ /** A name */
+ public static final PdfName ICCBASED = new PdfName("ICCBased");
+ /** A name */
+ public static final PdfName ID = new PdfName("ID");
+ /** A name */
+ public static final PdfName IDENTITY = new PdfName("Identity");
+ /** A name */
+ public static final PdfName IF = new PdfName("IF");
+ /** A name */
+ public static final PdfName IMAGE = new PdfName("Image");
+ /** A name */
+ public static final PdfName IMAGEB = new PdfName("ImageB");
+ /** A name */
+ public static final PdfName IMAGEC = new PdfName("ImageC");
+ /** A name */
+ public static final PdfName IMAGEI = new PdfName("ImageI");
+ /** A name */
+ public static final PdfName IMAGEMASK = new PdfName("ImageMask");
+ /** A name */
+ public static final PdfName INDEX = new PdfName("Index");
+ /** A name */
+ public static final PdfName INDEXED = new PdfName("Indexed");
+ /** A name */
+ public static final PdfName INFO = new PdfName("Info");
+ /** A name */
+ public static final PdfName INK = new PdfName("Ink");
+ /** A name */
+ public static final PdfName INKLIST = new PdfName("InkList");
+ /** A name */
+ public static final PdfName IMPORTDATA = new PdfName("ImportData");
+ /** A name */
+ public static final PdfName INTENT = new PdfName("Intent");
+ /** A name */
+ public static final PdfName INTERPOLATE = new PdfName("Interpolate");
+ /** A name */
+ public static final PdfName ISMAP = new PdfName("IsMap");
+ /** A name */
+ public static final PdfName IRT = new PdfName("IRT");
+ /** A name */
+ public static final PdfName ITALICANGLE = new PdfName("ItalicAngle");
+ /** A name */
+ public static final PdfName IX = new PdfName("IX");
+ /** A name */
+ public static final PdfName JAVASCRIPT = new PdfName("JavaScript");
+ /** A name */
+ public static final PdfName JS = new PdfName("JS");
+ /** A name */
+ public static final PdfName K = new PdfName("K");
+ /** A name */
+ public static final PdfName KEYWORDS = new PdfName("Keywords");
+ /** A name */
+ public static final PdfName KIDS = new PdfName("Kids");
+ /** A name */
+ public static final PdfName L = new PdfName("L");
+ /** A name */
+ public static final PdfName L2R = new PdfName("L2R");
+ /** A name */
+ public static final PdfName LANG = new PdfName("Lang");
+ /** A name */
+ public static final PdfName LANGUAGE = new PdfName("Language");
+ /** A name */
+ public static final PdfName LAST = new PdfName("Last");
+ /** A name */
+ public static final PdfName LASTCHAR = new PdfName("LastChar");
+ /** A name */
+ public static final PdfName LASTPAGE = new PdfName("LastPage");
+ /** A name */
+ public static final PdfName LAUNCH = new PdfName("Launch");
+ /** A name */
+ public static final PdfName LENGTH = new PdfName("Length");
+ /** A name */
+ public static final PdfName LENGTH1 = new PdfName("Length1");
+ /** A name */
+ public static final PdfName LIMITS = new PdfName("Limits");
+ /** A name */
+ public static final PdfName LINE = new PdfName("Line");
+ /** A name */
+ public static final PdfName LINK = new PdfName("Link");
+ /** A name */
+ public static final PdfName LISTMODE = new PdfName("ListMode");
+ /** A name */
+ public static final PdfName LOCATION = new PdfName("Location");
+ /** A name */
+ public static final PdfName LOCK = new PdfName("Lock");
+ /** A name */
+ public static final PdfName LZWDECODE = new PdfName("LZWDecode");
+ /** A name */
+ public static final PdfName M = new PdfName("M");
+ /** A name */
+ public static final PdfName MATRIX = new PdfName("Matrix");
+ /** A name of an encoding */
+ public static final PdfName MAC_EXPERT_ENCODING = new PdfName("MacExpertEncoding");
+ /** A name of an encoding */
+ public static final PdfName MAC_ROMAN_ENCODING = new PdfName("MacRomanEncoding");
+ /** A name */
+ public static final PdfName MARKED = new PdfName("Marked");
+ /** A name */
+ public static final PdfName MARKINFO = new PdfName("MarkInfo");
+ /** A name */
+ public static final PdfName MASK = new PdfName("Mask");
+ /** A name */
+ public static final PdfName MAX = new PdfName("max");
+ /** A name */
+ public static final PdfName MAXLEN = new PdfName("MaxLen");
+ /** A name */
+ public static final PdfName MEDIABOX = new PdfName("MediaBox");
+ /** A name */
+ public static final PdfName MCID = new PdfName("MCID");
+ /** A name */
+ public static final PdfName MCR = new PdfName("MCR");
+ /** A name */
+ public static final PdfName METADATA = new PdfName("Metadata");
+ /** A name */
+ public static final PdfName MIN = new PdfName("min");
+ /** A name */
+ public static final PdfName MK = new PdfName("MK");
+ /** A name */
+ public static final PdfName MMTYPE1 = new PdfName("MMType1");
+ /** A name */
+ public static final PdfName MODDATE = new PdfName("ModDate");
+ /** A name */
+ public static final PdfName N = new PdfName("N");
+ /** A name */
+ public static final PdfName N0 = new PdfName("n0");
+ /** A name */
+ public static final PdfName N1 = new PdfName("n1");
+ /** A name */
+ public static final PdfName N2 = new PdfName("n2");
+ /** A name */
+ public static final PdfName N3 = new PdfName("n3");
+ /** A name */
+ public static final PdfName N4 = new PdfName("n4");
+ /** A name */
+ public static final PdfName NAME = new PdfName("Name");
+ /** A name */
+ public static final PdfName NAMED = new PdfName("Named");
+ /** A name */
+ public static final PdfName NAMES = new PdfName("Names");
+ /** A name */
+ public static final PdfName NEEDAPPEARANCES = new PdfName("NeedAppearances");
+ /** A name */
+ public static final PdfName NEWWINDOW = new PdfName("NewWindow");
+ /** A name */
+ public static final PdfName NEXT = new PdfName("Next");
+ /** A name */
+ public static final PdfName NEXTPAGE = new PdfName("NextPage");
+ /** A name */
+ public static final PdfName NM = new PdfName("NM");
+ /** A name */
+ public static final PdfName NONE = new PdfName("None");
+ /** A name */
+ public static final PdfName NONFULLSCREENPAGEMODE = new PdfName("NonFullScreenPageMode");
+ /** A name */
+ public static final PdfName NUMS = new PdfName("Nums");
+ /** A name */
+ public static final PdfName O = new PdfName("O");
+ /** A name */
+ public static final PdfName OBJSTM = new PdfName("ObjStm");
+ /** A name */
+ public static final PdfName OC = new PdfName("OC");
+ /** A name */
+ public static final PdfName OCG = new PdfName("OCG");
+ /** A name */
+ public static final PdfName OCGS = new PdfName("OCGs");
+ /** A name */
+ public static final PdfName OCMD = new PdfName("OCMD");
+ /** A name */
+ public static final PdfName OCPROPERTIES = new PdfName("OCProperties");
+ /** A name */
+ public static final PdfName Off = new PdfName("Off");
+ /** A name */
+ public static final PdfName OFF = new PdfName("OFF");
+ /** A name */
+ public static final PdfName ON = new PdfName("ON");
+ /** A name */
+ public static final PdfName ONECOLUMN = new PdfName("OneColumn");
+ /** A name */
+ public static final PdfName OPEN = new PdfName("Open");
+ /** A name */
+ public static final PdfName OPENACTION = new PdfName("OpenAction");
+ /** A name */
+ public static final PdfName OP = new PdfName("OP");
+ /** A name */
+ public static final PdfName op = new PdfName("op");
+ /** A name */
+ public static final PdfName OPM = new PdfName("OPM");
+ /** A name */
+ public static final PdfName OPT = new PdfName("Opt");
+ /** A name */
+ public static final PdfName ORDER = new PdfName("Order");
+ /** A name */
+ public static final PdfName ORDERING = new PdfName("Ordering");
+ /** A name */
+ public static final PdfName OUTLINES = new PdfName("Outlines");
+ /** A name */
+ public static final PdfName OUTPUTCONDITION = new PdfName("OutputCondition");
+ /** A name */
+ public static final PdfName OUTPUTCONDITIONIDENTIFIER = new PdfName("OutputConditionIdentifier");
+ /** A name */
+ public static final PdfName OUTPUTINTENT = new PdfName("OutputIntent");
+ /** A name */
+ public static final PdfName OUTPUTINTENTS = new PdfName("OutputIntents");
+ /** A name */
+ public static final PdfName P = new PdfName("P");
+ /** A name */
+ public static final PdfName PAGE = new PdfName("Page");
+ /** A name */
+ public static final PdfName PAGELABELS = new PdfName("PageLabels");
+ /** A name */
+ public static final PdfName PAGELAYOUT = new PdfName("PageLayout");
+ /** A name */
+ public static final PdfName PAGEMODE = new PdfName("PageMode");
+ /** A name */
+ public static final PdfName PAGES = new PdfName("Pages");
+ /** A name */
+ public static final PdfName PAINTTYPE = new PdfName("PaintType");
+ /** A name */
+ public static final PdfName PANOSE = new PdfName("Panose");
+ /** A name */
+ public static final PdfName PARAMS = new PdfName("Params");
+ /** A name */
+ public static final PdfName PARENT = new PdfName("Parent");
+ /** A name */
+ public static final PdfName PARENTTREE = new PdfName("ParentTree");
+ /** A name */
+ public static final PdfName PATTERN = new PdfName("Pattern");
+ /** A name */
+ public static final PdfName PATTERNTYPE = new PdfName("PatternType");
+ /** A name */
+ public static final PdfName PDF = new PdfName("PDF");
+ /** A name */
+ public static final PdfName PERCEPTUAL = new PdfName("Perceptual");
+ /** A name */
+ public static final PdfName PG = new PdfName("Pg");
+ /** A name */
+ public static final PdfName POPUP = new PdfName("Popup");
+ /** A name */
+ public static final PdfName PREDICTOR = new PdfName("Predictor");
+ /** A name */
+ public static final PdfName PREFERRED = new PdfName("Preferred");
+ /** A name */
+ public static final PdfName PRESERVERB = new PdfName("PreserveRB");
+ /** A name */
+ public static final PdfName PREV = new PdfName("Prev");
+ /** A name */
+ public static final PdfName PREVPAGE = new PdfName("PrevPage");
+ /** A name */
+ public static final PdfName PRINT = new PdfName("Print");
+ /** A name */
+ public static final PdfName PRINTSCALING = new PdfName("PrintScaling");
+ /** A name */
+ public static final PdfName PRINTSTATE = new PdfName("PrintState");
+ /** A name */
+ public static final PdfName PROCSET = new PdfName("ProcSet");
+ /** A name */
+ public static final PdfName PRODUCER = new PdfName("Producer");
+ /** A name */
+ public static final PdfName PROPERTIES = new PdfName("Properties");
+ /** A name */
+ public static final PdfName PS = new PdfName("PS");
+ /** A name */
+ public static final PdfName Q = new PdfName("Q");
+ /** A name */
+ public static final PdfName QUADPOINTS = new PdfName("QuadPoints");
+ /** A name */
+ public static final PdfName R = new PdfName("R");
+ /** A name */
+ public static final PdfName R2L = new PdfName("R2L");
+ /** A name */
+ public static final PdfName RANGE = new PdfName("Range");
+ /** A name */
+ public static final PdfName RC = new PdfName("RC");
+ /** A name */
+ public static final PdfName RBGROUPS = new PdfName("RBGroups");
+ /** A name */
+ public static final PdfName REASON = new PdfName("Reason");
+ /** A name */
+ public static final PdfName RECT = new PdfName("Rect");
+ /** A name */
+ public static final PdfName REGISTRY = new PdfName("Registry");
+ /** A name */
+ public static final PdfName REGISTRYNAME = new PdfName("RegistryName");
+ /** A name */
+ public static final PdfName RELATIVECALORIMETRIC = new PdfName("RelativeColorimetric");
+ /** A name */
+ public static final PdfName RENDITION = new PdfName("Rendition");
+ /** A name */
+ public static final PdfName RESETFORM = new PdfName("ResetForm");
+ /** A name */
+ public static final PdfName RESOURCES = new PdfName("Resources");
+ /** A name */
+ public static final PdfName RI = new PdfName("RI");
+ /** A name */
+ public static final PdfName ROLEMAP = new PdfName("RoleMap");
+ /** A name */
+ public static final PdfName ROOT = new PdfName("Root");
+ /** A name */
+ public static final PdfName ROTATE = new PdfName("Rotate");
+ /** A name */
+ public static final PdfName ROWS = new PdfName("Rows");
+ /** A name */
+ public static final PdfName RUNLENGTHDECODE = new PdfName("RunLengthDecode");
+ /** A name */
+ public static final PdfName RV = new PdfName("RV");
+ /** A name */
+ public static final PdfName S = new PdfName("S");
+ /** A name */
+ public static final PdfName SATURATION = new PdfName("Saturation");
+ /** A name */
+ public static final PdfName SCREEN = new PdfName("Screen");
+ /** A name */
+ public static final PdfName SEPARATION = new PdfName("Separation");
+ /** A name */
+ public static final PdfName SETOCGSTATE = new PdfName("SetOCGState");
+ /** A name */
+ public static final PdfName SHADING = new PdfName("Shading");
+ /** A name */
+ public static final PdfName SHADINGTYPE = new PdfName("ShadingType");
+ /** A name */
+ public static final PdfName SHIFT_JIS = new PdfName("Shift-JIS");
+ /** A name */
+ public static final PdfName SIG = new PdfName("Sig");
+ /** A name */
+ public static final PdfName SIGFLAGS = new PdfName("SigFlags");
+ /** A name */
+ public static final PdfName SINGLEPAGE = new PdfName("SinglePage");
+ /** A name */
+ public static final PdfName SIZE = new PdfName("Size");
+ /** A name */
+ public static final PdfName SMASK = new PdfName("SMask");
+ /** A name */
+ public static final PdfName SPLIT = new PdfName("Split");
+ /** A name */
+ public static final PdfName SQUARE = new PdfName("Square");
+ /** A name */
+ public static final PdfName ST = new PdfName("St");
+ /** A name */
+ public static final PdfName STAMP = new PdfName("Stamp");
+ /** A name */
+ public static final PdfName STANDARD = new PdfName("Standard");
+ /** A name */
+ public static final PdfName STATE = new PdfName("State");
+ /** A name */
+ public static final PdfName STRIKEOUT = new PdfName("StrikeOut");
+ /** A name */
+ public static final PdfName STRUCTPARENT = new PdfName("StructParent");
+ /** A name */
+ public static final PdfName STRUCTPARENTS = new PdfName("StructParents");
+ /** A name */
+ public static final PdfName STRUCTTREEROOT = new PdfName("StructTreeRoot");
+ /** A name */
+ public static final PdfName STYLE = new PdfName("Style");
+ /** A name */
+ public static final PdfName STEMV = new PdfName("StemV");
+ /** A name */
+ public static final PdfName SUBFILTER = new PdfName("SubFilter");
+ /** A name */
+ public static final PdfName SUBJECT = new PdfName("Subject");
+ /** A name */
+ public static final PdfName SUBMITFORM = new PdfName("SubmitForm");
+ /** A name */
+ public static final PdfName SUBTYPE = new PdfName("Subtype");
+ /** A name */
+ public static final PdfName SUPPLEMENT = new PdfName("Supplement");
+ /** A name */
+ public static final PdfName SV = new PdfName("SV");
+ /** A name */
+ public static final PdfName SW = new PdfName("SW");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName SYMBOL = new PdfName("Symbol");
+ /** A name */
+ public static final PdfName T = new PdfName("T");
+ /** A name */
+ public static final PdfName TEXT = new PdfName("Text");
+ /** A name */
+ public static final PdfName THUMB = new PdfName("Thumb");
+ /** A name */
+ public static final PdfName THREADS = new PdfName("Threads");
+ /** A name */
+ public static final PdfName TI = new PdfName("TI");
+ /** A name */
+ public static final PdfName TILINGTYPE = new PdfName("TilingType");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName TIMES_ROMAN = new PdfName("Times-Roman");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName TIMES_BOLD = new PdfName("Times-Bold");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName TIMES_ITALIC = new PdfName("Times-Italic");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName TIMES_BOLDITALIC = new PdfName("Times-BoldItalic");
+ /** A name */
+ public static final PdfName TITLE = new PdfName("Title");
+ /** A name */
+ public static final PdfName TK = new PdfName("TK");
+ /** A name */
+ public static final PdfName TM = new PdfName("TM");
+ /** A name */
+ public static final PdfName TOGGLE = new PdfName("Toggle");
+ /** A name */
+ public static final PdfName TOUNICODE = new PdfName("ToUnicode");
+ /** A name */
+ public static final PdfName TP = new PdfName("TP");
+ /** A name */
+ public static final PdfName TRANS = new PdfName("Trans");
+ /** A name */
+ public static final PdfName TRANSPARENCY = new PdfName("Transparency");
+ /** A name */
+ public static final PdfName TRAPPED = new PdfName("Trapped");
+ /** A name */
+ public static final PdfName TRIMBOX = new PdfName("TrimBox");
+ /** A name */
+ public static final PdfName TRUETYPE = new PdfName("TrueType");
+ /** A name */
+ public static final PdfName TU = new PdfName("TU");
+ /** A name */
+ public static final PdfName TWOCOLUMNLEFT = new PdfName("TwoColumnLeft");
+ /** A name */
+ public static final PdfName TWOCOLUMNRIGHT = new PdfName("TwoColumnRight");
+ /** A name */
+ public static final PdfName TWOPAGELEFT = new PdfName("TwoPageLeft");
+ /** A name */
+ public static final PdfName TWOPAGERIGHT = new PdfName("TwoPageRight");
+ /** A name */
+ public static final PdfName TX = new PdfName("Tx");
+ /** A name */
+ public static final PdfName TYPE = new PdfName("Type");
+ /** A name */
+ public static final PdfName TYPE0 = new PdfName("Type0");
+ /** A name */
+ public static final PdfName TYPE1 = new PdfName("Type1");
+ /** A name of an attribute. */
+ public static final PdfName TYPE3 = new PdfName("Type3");
+ /** A name of an attribute. */
+ public static final PdfName U = new PdfName("U");
+ /** A name of an attribute. */
+ public static final PdfName UHC = new PdfName("UHC");
+ /** A name of an attribute. */
+ public static final PdfName UNDERLINE = new PdfName("Underline");
+ /** A name */
+ public static final PdfName URI = new PdfName("URI");
+ /** A name */
+ public static final PdfName URL = new PdfName("URL");
+ /** A name */
+ public static final PdfName USAGE = new PdfName("Usage");
+ /** A name */
+ public static final PdfName USEATTACHMENTS = new PdfName("UseAttachments");
+ /** A name */
+ public static final PdfName USENONE = new PdfName("UseNone");
+ /** A name */
+ public static final PdfName USEOC = new PdfName("UseOC");
+ /** A name */
+ public static final PdfName USEOUTLINES = new PdfName("UseOutlines");
+ /** A name */
+ public static final PdfName USER = new PdfName("User");
+ /** A name */
+ public static final PdfName USERUNIT = new PdfName("UserUnit");
+ /** A name */
+ public static final PdfName USETHUMBS = new PdfName("UseThumbs");
+ /** A name */
+ public static final PdfName V = new PdfName("V");
+ /** A name */
+ public static final PdfName VERISIGN_PPKVS = new PdfName("VeriSign.PPKVS");
+ /** A name */
+ public static final PdfName VIEW = new PdfName("View");
+ /** A name */
+ public static final PdfName VIEWERPREFERENCES = new PdfName("ViewerPreferences");
+ /** A name */
+ public static final PdfName VIEWSTATE = new PdfName("ViewState");
+ /** A name */
+ public static final PdfName VISIBLEPAGES = new PdfName("VisiblePages");
+ /** A name of an attribute. */
+ public static final PdfName W = new PdfName("W");
+ /** A name of an attribute. */
+ public static final PdfName W2 = new PdfName("W2");
+ /** A name of an attribute. */
+ public static final PdfName WC = new PdfName("WC");
+ /** A name of an attribute. */
+ public static final PdfName WIDGET = new PdfName("Widget");
+ /** A name of an attribute. */
+ public static final PdfName WIDTH = new PdfName("Width");
+ /** A name */
+ public static final PdfName WIDTHS = new PdfName("Widths");
+ /** A name of an encoding */
+ public static final PdfName WIN = new PdfName("Win");
+ /** A name of an encoding */
+ public static final PdfName WIN_ANSI_ENCODING = new PdfName("WinAnsiEncoding");
+ /** A name of an encoding */
+ public static final PdfName WIPE = new PdfName("Wipe");
+ /** A name */
+ public static final PdfName WHITEPOINT = new PdfName("WhitePoint");
+ /** A name */
+ public static final PdfName WP = new PdfName("WP");
+ /** A name of an encoding */
+ public static final PdfName WS = new PdfName("WS");
+ /** A name */
+ public static final PdfName X = new PdfName("X");
+ /** A name */
+ public static final PdfName XFA = new PdfName("XFA");
+ /** A name */
+ public static final PdfName XML = new PdfName("XML");
+ /** A name */
+ public static final PdfName XOBJECT = new PdfName("XObject");
+ /** A name */
+ public static final PdfName XSTEP = new PdfName("XStep");
+ /** A name */
+ public static final PdfName XREF = new PdfName("XRef");
+ /** A name */
+ public static final PdfName XREFSTM = new PdfName("XRefStm");
+ /** A name */
+ public static final PdfName XYZ = new PdfName("XYZ");
+ /** A name */
+ public static final PdfName YSTEP = new PdfName("YStep");
+ /** A name of a base 14 type 1 font */
+ public static final PdfName ZAPFDINGBATS = new PdfName("ZapfDingbats");
+ /** A name */
+ public static final PdfName ZOOM = new PdfName("Zoom");
+
+ private int hash = 0;
+
+ // constructors
+
+
+ /**
+ * Constructs a new
+ * @param object the Object to be compared.
+ * @return a negative integer, zero, or a positive integer as this object
+ * is less than, equal to, or greater than the specified object.
+ * @throws ClassCastException if the specified object's type prevents it
+ * from being compared to this Object.
+ */
+ public int compareTo(Object object) {
+ PdfName name = (PdfName) object;
+
+ byte myBytes[] = bytes;
+ byte objBytes[] = name.bytes;
+ int len = Math.min(myBytes.length, objBytes.length);
+ for(int i=0; i
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 4.9 (page 53).
+ *
+ * @see PdfObject
+ */
+
+public class PdfNull extends PdfObject {
+
+ // static membervariables
+
+/** This is an instance of the
+ * You never need to do this yourself, you can always use the static final object PDFNULL.
+ */
+
+ public PdfNull() {
+ super(NULL, CONTENT);
+ }
+
+ public String toString() {
+ return "null";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfNumber.java b/src/main/java/com/lowagie/text/pdf/PdfNumber.java
new file mode 100644
index 0000000..07faf07
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfNumber.java
@@ -0,0 +1,159 @@
+/*
+ * $Id: PdfNumber.java,v 1.56 2005/05/04 14:32:42 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ *
+ * Integers may be specified by signed or unsigned constants. Reals may only be
+ * in decimal format.
+ * PDF supports seven basic types of objects: Booleans, numbers, strings, names,
+ * arrays, dictionaries and streams. In addition, PDF provides a null object.
+ * Objects may be labeled so that they can be referred to by other objects.
+ * In some cases, namely for
+ * Remark: the actual content of an object is in most cases identical to its representation.
+ * The following statement is always true: length() >= pdfLength().
+ * In some cases, namely for
+ * Remark: the actual content of an object is in some cases identical to its representation.
+ * The following statement is always true: length() >= pdfLength().
+ * An outline allows a user to access views of a document by name.
+ * This is the constructor for the
+ * This is the constructor for an
+ * This is the constructor for an
+ * This is the constructor for an
+ * This is the constructor for an
+ * This is the constructor for an
+ * This is the constructor for an
+ * This is the constructor for an
+ * This is the constructor for an
+ * This is the constructor for an
+ * This is the constructor for an
+ * This is the constructor for an
+ * This is the constructor for an
+ *
+ * @param cell the cell
+ * @param position the coordinates of the cell
+ * @param canvases an array of
+ * It's based in code found at org.bouncycastle.
+ */
+public class PdfPKCS7 {
+
+ private byte sigAttr[];
+ private byte digestAttr[];
+ private int version, signerversion;
+ private Set digestalgos;
+ private Collection certs, crls;
+ private X509Certificate signCert;
+ private byte[] digest;
+ private MessageDigest messageDigest;
+ private String digestAlgorithm, digestEncryptionAlgorithm;
+ private Signature sig;
+ private transient PrivateKey privKey;
+ private byte RSAdata[];
+ private boolean verified;
+ private boolean verifyResult;
+ private byte externalDigest[];
+ private byte externalRSAdata[];
+
+ private static final String ID_PKCS7_DATA = "1.2.840.113549.1.7.1";
+ private static final String ID_PKCS7_SIGNED_DATA = "1.2.840.113549.1.7.2";
+ private static final String ID_MD5 = "1.2.840.113549.2.5";
+ private static final String ID_MD2 = "1.2.840.113549.2.2";
+ private static final String ID_SHA1 = "1.3.14.3.2.26";
+ private static final String ID_RSA = "1.2.840.113549.1.1.1";
+ private static final String ID_DSA = "1.2.840.10040.4.1";
+ private static final String ID_CONTENT_TYPE = "1.2.840.113549.1.9.3";
+ private static final String ID_MESSAGE_DIGEST = "1.2.840.113549.1.9.4";
+ private static final String ID_SIGNING_TIME = "1.2.840.113549.1.9.5";
+ private static final String ID_MD2RSA = "1.2.840.113549.1.1.2";
+ private static final String ID_MD5RSA = "1.2.840.113549.1.1.4";
+ private static final String ID_SHA1RSA = "1.2.840.113549.1.1.5";
+ /**
+ * Holds value of property reason.
+ */
+ private String reason;
+
+ /**
+ * Holds value of property location.
+ */
+ private String location;
+
+ /**
+ * Holds value of property signDate.
+ */
+ private Calendar signDate;
+
+ /**
+ * Holds value of property signName.
+ */
+ private String signName;
+
+ /**
+ * Verifies a signature using the sub-filter adbe.x509.rsa_sha1.
+ * @param contentsKey the /Contents key
+ * @param certsKey the /Cert key
+ * @param provider the provider or
+ * A simple example:
+ *
+ * Note: if you're trying to be ultra orthodox, don't use this! It shouldn't be in here.
+ */
+ public static final DERObjectIdentifier EmailAddress = new DERObjectIdentifier("1.2.840.113549.1.9.1");
+
+ /**
+ * email address in Verisign certificates
+ */
+ public static final DERObjectIdentifier E = EmailAddress;
+
+ /** object identifier */
+ public static final DERObjectIdentifier DC = new DERObjectIdentifier("0.9.2342.19200300.100.1.25");
+
+ /** LDAP User id. */
+ public static final DERObjectIdentifier UID = new DERObjectIdentifier("0.9.2342.19200300.100.1.1");
+
+ /** A HashMap with default symbols */
+ public static HashMap DefaultSymbols = new HashMap();
+
+ static {
+ DefaultSymbols.put(C, "C");
+ DefaultSymbols.put(O, "O");
+ DefaultSymbols.put(T, "T");
+ DefaultSymbols.put(OU, "OU");
+ DefaultSymbols.put(CN, "CN");
+ DefaultSymbols.put(L, "L");
+ DefaultSymbols.put(ST, "ST");
+ DefaultSymbols.put(SN, "SN");
+ DefaultSymbols.put(EmailAddress, "E");
+ DefaultSymbols.put(DC, "DC");
+ DefaultSymbols.put(UID, "UID");
+ DefaultSymbols.put(SURNAME, "SURNAME");
+ DefaultSymbols.put(GIVENNAME, "GIVENNAME");
+ DefaultSymbols.put(INITIALS, "INITIALS");
+ DefaultSymbols.put(GENERATION, "GENERATION");
+ }
+ /** A HashMap with values */
+ public HashMap values = new HashMap();
+
+ /**
+ * Constructs an X509 name
+ * @param seq an ASN1 Sequence
+ */
+ public X509Name(ASN1Sequence seq) {
+ Enumeration e = seq.getObjects();
+
+ while (e.hasMoreElements()) {
+ ASN1Set set = (ASN1Set)e.nextElement();
+
+ for (int i = 0; i < set.size(); i++) {
+ ASN1Sequence s = (ASN1Sequence)set.getObjectAt(i);
+ String id = (String)DefaultSymbols.get(s.getObjectAt(0));
+ if (id == null)
+ continue;
+ ArrayList vs = (ArrayList)values.get(id);
+ if (vs == null) {
+ vs = new ArrayList();
+ values.put(id, vs);
+ }
+ vs.add(((DERString)s.getObjectAt(1)).getString());
+ }
+ }
+ }
+ /**
+ * Constructs an X509 name
+ * @param dirName a directory name
+ */
+ public X509Name(String dirName) {
+ X509NameTokenizer nTok = new X509NameTokenizer(dirName);
+
+ while (nTok.hasMoreTokens()) {
+ String token = nTok.nextToken();
+ int index = token.indexOf('=');
+
+ if (index == -1) {
+ throw new IllegalArgumentException("badly formated directory string");
+ }
+
+ String id = token.substring(0, index).toUpperCase();
+ String value = token.substring(index + 1);
+ ArrayList vs = (ArrayList)values.get(id);
+ if (vs == null) {
+ vs = new ArrayList();
+ values.put(id, vs);
+ }
+ vs.add(value);
+ }
+
+ }
+
+ public String getField(String name) {
+ ArrayList vs = (ArrayList)values.get(name);
+ return vs == null ? null : (String)vs.get(0);
+ }
+
+ /**
+ * gets a field array from the values Hashmap
+ * @param name
+ * @return an ArrayList
+ */
+ public ArrayList getFieldArray(String name) {
+ ArrayList vs = (ArrayList)values.get(name);
+ return vs == null ? null : vs;
+ }
+
+ /**
+ * getter for values
+ * @return a HashMap with the fields of the X509 name
+ */
+ public HashMap getFields() {
+ return values;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return values.toString();
+ }
+ }
+
+ /**
+ * class for breaking up an X500 Name into it's component tokens, ala
+ * java.util.StringTokenizer. We need this class as some of the
+ * lightweight Java environment don't support classes like
+ * StringTokenizer.
+ */
+ public static class X509NameTokenizer {
+ private String oid;
+ private int index;
+ private StringBuffer buf = new StringBuffer();
+
+ public X509NameTokenizer(
+ String oid) {
+ this.oid = oid;
+ this.index = -1;
+ }
+
+ public boolean hasMoreTokens() {
+ return (index != oid.length());
+ }
+
+ public String nextToken() {
+ if (index == oid.length()) {
+ return null;
+ }
+
+ int end = index + 1;
+ boolean quoted = false;
+ boolean escaped = false;
+
+ buf.setLength(0);
+
+ while (end != oid.length()) {
+ char c = oid.charAt(end);
+
+ if (c == '"') {
+ if (!escaped) {
+ quoted = !quoted;
+ }
+ else {
+ buf.append(c);
+ }
+ escaped = false;
+ }
+ else {
+ if (escaped || quoted) {
+ buf.append(c);
+ escaped = false;
+ }
+ else if (c == '\\') {
+ escaped = true;
+ }
+ else if (c == ',') {
+ break;
+ }
+ else {
+ buf.append(c);
+ }
+ }
+ end++;
+ }
+
+ index = end;
+ return buf.toString().trim();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPRow.java b/src/main/java/com/lowagie/text/pdf/PdfPRow.java
new file mode 100644
index 0000000..31f7cd9
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPRow.java
@@ -0,0 +1,659 @@
+/*
+ * $Id: PdfPRow.java,v 1.72 2006/02/12 18:23:29 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.Image;
+import com.lowagie.text.Rectangle;
+import java.awt.Color;
+
+/**
+ * A row in a PdfPTable.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+
+public class PdfPRow {
+
+ /** the bottom limit (bottom right y) */
+ public static final float BOTTOM_LIMIT = -(1 << 30);
+
+ protected PdfPCell cells[];
+
+ protected float widths[];
+
+ protected float maxHeight = 0;
+
+ protected boolean calculated = false;
+
+ private int[] canvasesPos;
+
+ /**
+ * Constructs a new PdfPRow with the cells in the array that was passed as a parameter.
+ * @param cells
+ */
+ public PdfPRow(PdfPCell cells[]) {
+ this.cells = cells;
+ widths = new float[cells.length];
+ }
+
+ /**
+ * Makes a copy of an existing row.
+ * @param row
+ */
+ public PdfPRow(PdfPRow row) {
+ maxHeight = row.maxHeight;
+ calculated = row.calculated;
+ cells = new PdfPCell[row.cells.length];
+ for (int k = 0; k < cells.length; ++k) {
+ if (row.cells[k] != null)
+ cells[k] = new PdfPCell(row.cells[k]);
+ }
+ widths = new float[cells.length];
+ System.arraycopy(row.widths, 0, widths, 0, cells.length);
+ }
+
+ /**
+ * Sets the widths of the columns in the row.
+ * @param widths
+ * @return true if everything went right
+ */
+ public boolean setWidths(float widths[]) {
+ if (widths.length != cells.length)
+ return false;
+ System.arraycopy(widths, 0, this.widths, 0, cells.length);
+ float total = 0;
+ calculated = false;
+ for (int k = 0; k < widths.length; ++k) {
+ PdfPCell cell = cells[k];
+ cell.setLeft(total);
+ int last = k + cell.getColspan();
+ for (; k < last; ++k)
+ total += widths[k];
+ --k;
+ cell.setRight(total);
+ cell.setTop(0);
+ }
+ return true;
+ }
+
+ /**
+ * Calculates the heights of each cell in the row.
+ * @return the maximum height of the row.
+ */
+ public float calculateHeights() {
+ maxHeight = 0;
+ for (int k = 0; k < cells.length; ++k) {
+ PdfPCell cell = cells[k];
+ if (cell == null)
+ continue;
+ Image img = cell.getImage();
+ if (img != null) {
+ img.scalePercent(100);
+ float refWidth = img.scaledWidth();
+ if (cell.getRotation() == 90 || cell.getRotation() == 270) {
+ refWidth = img.scaledHeight();
+ }
+ float scale = (cell.right() - cell.getEffectivePaddingRight()
+ - cell.getEffectivePaddingLeft() - cell.left())
+ / refWidth;
+ img.scalePercent(scale * 100);
+ float refHeight = img.scaledHeight();
+ if (cell.getRotation() == 90 || cell.getRotation() == 270) {
+ refHeight = img.scaledWidth();
+ }
+ cell.setBottom(cell.top() - cell.getEffectivePaddingTop()
+ - cell.getEffectivePaddingBottom()
+ - refHeight);
+ } else {
+ if (cell.getRotation() == 0 || cell.getRotation() == 180) {
+ float rightLimit = cell.isNoWrap() ? 20000 : cell.right()
+ - cell.getEffectivePaddingRight();
+ float bry = (cell.getFixedHeight() > 0) ? cell.top()
+ - cell.getEffectivePaddingTop()
+ + cell.getEffectivePaddingBottom()
+ - cell.getFixedHeight() : BOTTOM_LIMIT;
+ ColumnText ct = ColumnText.duplicate(cell.getColumn());
+ ct.setSimpleColumn(
+ cell.left() + cell.getEffectivePaddingLeft(), bry,
+ rightLimit, cell.top() - cell.getEffectivePaddingTop());
+ try {
+ ct.go(true);
+ } catch (DocumentException e) {
+ throw new ExceptionConverter(e);
+ }
+ float yLine = ct.getYLine();
+ if (cell.isUseDescender())
+ yLine += ct.getDescender();
+ cell.setBottom(yLine - cell.getEffectivePaddingBottom());
+ }
+ else {
+ if (cell.getFixedHeight() > 0) {
+ cell.setBottom(cell.top() - cell.getFixedHeight());
+ }
+ else {
+ ColumnText ct = ColumnText.duplicate(cell.getColumn());
+ ct.setSimpleColumn(0, cell.left() + cell.getEffectivePaddingLeft(),
+ 20000, cell.right() - cell.getEffectivePaddingRight());
+ try {
+ ct.go(true);
+ } catch (DocumentException e) {
+ throw new ExceptionConverter(e);
+ }
+ cell.setBottom(cell.top() - cell.getEffectivePaddingTop()
+ - cell.getEffectivePaddingBottom() - ct.getFilledWidth());
+ }
+ }
+ }
+ float height = cell.getFixedHeight();
+ if (height <= 0)
+ height = cell.height();
+ if (height < cell.getFixedHeight())
+ height = cell.getFixedHeight();
+ else if (height < cell.getMinimumHeight())
+ height = cell.getMinimumHeight();
+ if (height > maxHeight)
+ maxHeight = height;
+ }
+ calculated = true;
+ return maxHeight;
+ }
+
+ /**
+ * Writes the border and background of one cell in the row.
+ * @param xPos
+ * @param yPos
+ * @param cell
+ * @param canvases
+ */
+ public void writeBorderAndBackground(float xPos, float yPos, PdfPCell cell,
+ PdfContentByte[] canvases) {
+ PdfContentByte lines = canvases[PdfPTable.LINECANVAS];
+ PdfContentByte backgr = canvases[PdfPTable.BACKGROUNDCANVAS];
+ // the coordinates of the border are retrieved
+ float x1 = cell.left() + xPos;
+ float y2 = cell.top() + yPos;
+ float x2 = cell.right() + xPos;
+ float y1 = y2 - maxHeight;
+
+ // the backgroundcolor is set
+ Color background = cell.backgroundColor();
+ if (background != null) {
+ backgr.setColorFill(background);
+ backgr.rectangle(x1, y1, x2 - x1, y2 - y1);
+ backgr.fill();
+ }
+ // if the element hasn't got any borders, nothing is added
+ if (cell.hasBorders()) {
+ if (cell.isUseVariableBorders()) {
+ Rectangle borderRect = new Rectangle(cell.left() + xPos, cell
+ .top()
+ - maxHeight + yPos, cell.right() + xPos, cell.top()
+ + yPos);
+ borderRect.cloneNonPositionParameters(cell);
+ borderRect.setBackgroundColor(null);
+ lines.rectangle(borderRect);
+ } else {
+ // the width is set to the width of the element
+ if (cell.borderWidth() != Rectangle.UNDEFINED) {
+ lines.setLineWidth(cell.borderWidth());
+ }
+ // the color is set to the color of the element
+ Color color = cell.borderColor();
+ if (color != null) {
+ lines.setColorStroke(color);
+ }
+
+ // if the box is a rectangle, it is added as a rectangle
+ if (cell.hasBorder(Rectangle.BOX)) {
+ lines.rectangle(x1, y1, x2 - x1, y2 - y1);
+ }
+ // if the border isn't a rectangle, the different sides are
+ // added apart
+ else {
+ if (cell.hasBorder(Rectangle.RIGHT)) {
+ lines.moveTo(x2, y1);
+ lines.lineTo(x2, y2);
+ }
+ if (cell.hasBorder(Rectangle.LEFT)) {
+ lines.moveTo(x1, y1);
+ lines.lineTo(x1, y2);
+ }
+ if (cell.hasBorder(Rectangle.BOTTOM)) {
+ lines.moveTo(x1, y1);
+ lines.lineTo(x2, y1);
+ }
+ if (cell.hasBorder(Rectangle.TOP)) {
+ lines.moveTo(x1, y2);
+ lines.lineTo(x2, y2);
+ }
+ }
+ lines.stroke();
+ if (color != null) {
+ lines.resetRGBColorStroke();
+ }
+ }
+ }
+ }
+
+ private void saveAndRotateCanvases(PdfContentByte[] canvases, float a, float b, float c, float d, float e, float f) {
+ int last = PdfPTable.TEXTCANVAS + 1;
+ if (canvasesPos == null) {
+ canvasesPos = new int[last * 2];
+ }
+ for (int k = 0; k < last; ++k) {
+ ByteBuffer bb = canvases[k].getInternalBuffer();
+ canvasesPos[k * 2] = bb.size();
+ canvases[k].saveState();
+ canvases[k].concatCTM(a, b, c, d, e, f);
+ canvasesPos[k * 2 + 1] = bb.size();
+ }
+ }
+
+ private void restoreCanvases(PdfContentByte[] canvases) {
+ int last = PdfPTable.TEXTCANVAS + 1;
+ for (int k = 0; k < last; ++k) {
+ ByteBuffer bb = canvases[k].getInternalBuffer();
+ int p1 = bb.size();
+ canvases[k].restoreState();
+ if (p1 == canvasesPos[k * 2 + 1])
+ bb.setSize(canvasesPos[k * 2]);
+ }
+ }
+
+ /**
+ * Writes a number of cells (not necessarily all cells).
+ * @param colStart
+ * @param colEnd
+ * @param xPos
+ * @param yPos
+ * @param canvases
+ */
+ public void writeCells(int colStart, int colEnd, float xPos, float yPos,
+ PdfContentByte[] canvases) {
+ if (!calculated)
+ calculateHeights();
+ if (colEnd < 0)
+ colEnd = cells.length;
+ colEnd = Math.min(colEnd, cells.length);
+ if (colStart < 0)
+ colStart = 0;
+ if (colStart >= colEnd)
+ return;
+ int newStart;
+ for (newStart = colStart; newStart >= 0; --newStart) {
+ if (cells[newStart] != null)
+ break;
+ xPos -= widths[newStart - 1];
+ }
+ xPos -= cells[newStart].left();
+ for (int k = newStart; k < colEnd; ++k) {
+ PdfPCell cell = cells[k];
+ if (cell == null)
+ continue;
+ writeBorderAndBackground(xPos, yPos, cell, canvases);
+ Image img = cell.getImage();
+ float tly = 0;
+ switch (cell.getVerticalAlignment()) {
+ case Element.ALIGN_BOTTOM:
+ tly = cell.top() + yPos - maxHeight + cell.height()
+ - cell.getEffectivePaddingTop();
+ break;
+ case Element.ALIGN_MIDDLE:
+ tly = cell.top() + yPos + (cell.height() - maxHeight) / 2
+ - cell.getEffectivePaddingTop();
+ break;
+ default:
+ tly = cell.top() + yPos - cell.getEffectivePaddingTop();
+ break;
+ }
+ if (img != null) {
+ if (cell.getRotation() != 0) {
+ img = Image.getInstance(img);
+ img.setRotation(img.getImageRotation() + (float)(cell.getRotation() * Math.PI / 180.0));
+ }
+ boolean vf = false;
+ if (cell.height() > maxHeight) {
+ img.scalePercent(100);
+ float scale = (maxHeight - cell.getEffectivePaddingTop() - cell
+ .getEffectivePaddingBottom())
+ / img.scaledHeight();
+ img.scalePercent(scale * 100);
+ vf = true;
+ }
+ float left = cell.left() + xPos
+ + cell.getEffectivePaddingLeft();
+ if (vf) {
+ switch (cell.getHorizontalAlignment()) {
+ case Element.ALIGN_CENTER:
+ left = xPos
+ + (cell.left() + cell.getEffectivePaddingLeft()
+ + cell.right()
+ - cell.getEffectivePaddingRight() - img
+ .scaledWidth()) / 2;
+ break;
+ case Element.ALIGN_RIGHT:
+ left = xPos + cell.right()
+ - cell.getEffectivePaddingRight()
+ - img.scaledWidth();
+ break;
+ default:
+ break;
+ }
+ tly = cell.top() + yPos - cell.getEffectivePaddingTop();
+ }
+ img.setAbsolutePosition(left, tly - img.scaledHeight());
+ try {
+ canvases[PdfPTable.TEXTCANVAS].addImage(img);
+ } catch (DocumentException e) {
+ throw new ExceptionConverter(e);
+ }
+ } else {
+ // rotation sponsored by Connection GmbH
+ if (cell.getRotation() == 90 || cell.getRotation() == 270) {
+ float netWidth = maxHeight - cell.getEffectivePaddingTop() - cell.getEffectivePaddingBottom();
+ float netHeight = cell.width() - cell.getEffectivePaddingLeft() - cell.getEffectivePaddingRight();
+ ColumnText ct = ColumnText.duplicate(cell.getColumn());
+ ct.setCanvases(canvases);
+ ct.setSimpleColumn(0, 0, netWidth + 0.001f, -netHeight);
+ try {
+ ct.go(true);
+ } catch (DocumentException e) {
+ throw new ExceptionConverter(e);
+ }
+ float calcHeight = -ct.getYLine();
+ if (calcHeight > 0) {
+ if (cell.isUseDescender())
+ calcHeight -= ct.getDescender();
+ ct = ColumnText.duplicate(cell.getColumn());
+ ct.setCanvases(canvases);
+ ct.setSimpleColumn(0, -0.001f, netWidth + 0.001f, calcHeight);
+ float pivotX;
+ float pivotY;
+ if (cell.getRotation() == 90) {
+ pivotY = cell.top() + yPos - maxHeight + cell.getEffectivePaddingBottom();
+ switch (cell.getVerticalAlignment()) {
+ case Element.ALIGN_BOTTOM:
+ pivotX = cell.left() + xPos + cell.width() - cell.getEffectivePaddingRight();
+ break;
+ case Element.ALIGN_MIDDLE:
+ pivotX = cell.left() + xPos + (cell.width() + cell.getEffectivePaddingLeft() - cell.getEffectivePaddingRight() + calcHeight) / 2;
+ break;
+ default: //top
+ pivotX = cell.left() + xPos + cell.getEffectivePaddingLeft() + calcHeight;
+ break;
+ }
+ saveAndRotateCanvases(canvases, 0,1,-1,0,pivotX,pivotY);
+ }
+ else {
+ pivotY = cell.top() + yPos - cell.getEffectivePaddingTop();
+ switch (cell.getVerticalAlignment()) {
+ case Element.ALIGN_BOTTOM:
+ pivotX = cell.left() + xPos + cell.getEffectivePaddingLeft();
+ break;
+ case Element.ALIGN_MIDDLE:
+ pivotX = cell.left() + xPos + (cell.width() + cell.getEffectivePaddingLeft() - cell.getEffectivePaddingRight() - calcHeight) / 2;
+ break;
+ default: //top
+ pivotX = cell.left() + xPos + cell.width() - cell.getEffectivePaddingRight() - calcHeight;
+ break;
+ }
+ saveAndRotateCanvases(canvases, 0,-1,1,0,pivotX,pivotY);
+ }
+ try {
+ ct.go();
+ } catch (DocumentException e) {
+ throw new ExceptionConverter(e);
+ } finally {
+ restoreCanvases(canvases);
+ }
+ }
+ }
+ else {
+ float fixedHeight = cell.getFixedHeight();
+ float rightLimit = cell.right() + xPos
+ - cell.getEffectivePaddingRight();
+ float leftLimit = cell.left() + xPos
+ + cell.getEffectivePaddingLeft();
+ if (cell.isNoWrap()) {
+ switch (cell.getHorizontalAlignment()) {
+ case Element.ALIGN_CENTER:
+ rightLimit += 10000;
+ leftLimit -= 10000;
+ break;
+ case Element.ALIGN_RIGHT:
+ leftLimit -= 20000;
+ break;
+ default:
+ rightLimit += 20000;
+ break;
+ }
+ }
+ ColumnText ct = ColumnText.duplicate(cell.getColumn());
+ ct.setCanvases(canvases);
+ float bry = tly
+ - (maxHeight /* cell.height() */
+ - cell.getEffectivePaddingTop() - cell.getEffectivePaddingBottom());
+ if (fixedHeight > 0) {
+ if (cell.height() > maxHeight) {
+ tly = cell.top() + yPos - cell.getEffectivePaddingTop();
+ bry = cell.top() + yPos - maxHeight + cell.getEffectivePaddingBottom();
+ }
+ }
+ if ((tly > bry || ct.zeroHeightElement()) && leftLimit < rightLimit) {
+ ct.setSimpleColumn(leftLimit, bry - 0.001f, rightLimit, tly);
+ if (cell.getRotation() == 180) {
+ float shx = leftLimit + rightLimit;
+ float shy = bry - 0.001f + tly;
+ saveAndRotateCanvases(canvases, -1,0,0,-1,shx,shy);
+ }
+ try {
+ ct.go();
+ } catch (DocumentException e) {
+ throw new ExceptionConverter(e);
+ } finally {
+ if (cell.getRotation() == 180) {
+ restoreCanvases(canvases);
+ }
+ }
+ }
+ }
+ }
+ PdfPCellEvent evt = cell.getCellEvent();
+ if (evt != null) {
+ Rectangle rect = new Rectangle(cell.left() + xPos, cell.top()
+ + yPos - maxHeight, cell.right() + xPos, cell.top()
+ + yPos);
+ evt.cellLayout(cell, rect, canvases);
+ }
+ }
+ }
+
+ /**
+ * Checks if the dimensions of the columns were calculated.
+ * @return true if the dimensions of the columns were calculated
+ */
+ public boolean isCalculated() {
+ return calculated;
+ }
+
+ /**
+ * Gets the maximum height of the row (i.e. of the 'highest' cell).
+ * @return the maximum height of the row
+ */
+ public float getMaxHeights() {
+ if (calculated)
+ return maxHeight;
+ else
+ return calculateHeights();
+ }
+
+ /**
+ * Changes the maximum height of the row (to make it higher).
+ * (added by Jin-Hsia Yang)
+ * @param maxHeight the new maximum height
+ */
+ public void setMaxHeights(float maxHeight) {
+ this.maxHeight = maxHeight;
+ }
+
+ //end add
+
+ float[] getEventWidth(float xPos) {
+ int n = 0;
+ for (int k = 0; k < cells.length; ++k) {
+ if (cells[k] != null)
+ ++n;
+ }
+ float width[] = new float[n + 1];
+ n = 0;
+ width[n++] = xPos;
+ for (int k = 0; k < cells.length; ++k) {
+ if (cells[k] != null) {
+ width[n] = width[n - 1] + cells[k].width();
+ ++n;
+ }
+ }
+ return width;
+ }
+
+ /**
+ * Splits a row to newHeight. The returned row is the remainder. It will
+ * return null if the newHeight was so small that only an empty row would
+ * result.
+ *
+ * @param newHeight
+ * the new height
+ * @return the remainder row or null if the newHeight was so small that only
+ * an empty row would result
+ */
+ public PdfPRow splitRow(float newHeight) {
+ PdfPCell newCells[] = new PdfPCell[cells.length];
+ float fh[] = new float[cells.length * 2];
+ boolean allEmpty = true;
+ for (int k = 0; k < cells.length; ++k) {
+ PdfPCell cell = cells[k];
+ if (cell == null)
+ continue;
+ fh[k * 2] = cell.getFixedHeight();
+ fh[k * 2 + 1] = cell.getMinimumHeight();
+ Image img = cell.getImage();
+ PdfPCell c2 = new PdfPCell(cell);
+ if (img != null) {
+ if (newHeight > cell.getEffectivePaddingBottom()
+ + cell.getEffectivePaddingTop() + 2) {
+ c2.setPhrase(null);
+ allEmpty = false;
+ }
+ } else {
+ int status;
+ float y;
+ ColumnText ct = ColumnText.duplicate(cell.getColumn());
+ if (cell.getRotation() == 90 || cell.getRotation() == 270) {
+ ct.setSimpleColumn(
+ cell.top() - newHeight + cell.getEffectivePaddingBottom(),
+ cell.left() + cell.getEffectivePaddingLeft(),
+ cell.top() - cell.getEffectivePaddingTop(),
+ y = cell.right() - cell.getEffectivePaddingRight());
+ }
+ else {
+ float rightLimit = cell.isNoWrap() ? 20000 : cell.right()
+ - cell.getEffectivePaddingRight();
+ float y1 = cell.top() - newHeight
+ + cell.getEffectivePaddingBottom();
+ float y2 = cell.top() - cell.getEffectivePaddingTop();
+ y = Math.max(y1, y2);
+ ct.setSimpleColumn(
+ cell.left() + cell.getEffectivePaddingLeft(), y1,
+ rightLimit, y2);
+ }
+ try {
+ status = ct.go(true);
+ } catch (DocumentException e) {
+ throw new ExceptionConverter(e);
+ }
+ boolean thisEmpty = (ct.getYLine() == y);
+ if (thisEmpty)
+ ct = ColumnText.duplicate(cell.getColumn());
+ allEmpty = (allEmpty && thisEmpty);
+ if ((status & ColumnText.NO_MORE_TEXT) == 0 || thisEmpty) {
+ c2.setColumn(ct);
+ ct.setFilledWidth(0);
+ } else {
+ c2.setPhrase(null);
+ }
+ }
+ newCells[k] = c2;
+ cell.setFixedHeight(newHeight);
+ }
+ if (allEmpty) {
+ for (int k = 0; k < cells.length; ++k) {
+ PdfPCell cell = cells[k];
+ if (cell == null)
+ continue;
+ float f = fh[k * 2];
+ float m = fh[k * 2 + 1];
+ if (f <= 0)
+ cell.setMinimumHeight(m);
+ else
+ cell.setFixedHeight(f);
+ }
+ return null;
+ }
+ calculateHeights();
+ PdfPRow split = new PdfPRow(newCells);
+ split.widths = (float[]) widths.clone();
+ split.calculateHeights();
+ return split;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPSXObject.java b/src/main/java/com/lowagie/text/pdf/PdfPSXObject.java
new file mode 100644
index 0000000..434a848
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPSXObject.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2005 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+
+/**
+ * Implements the PostScript XObject.
+ */
+public class PdfPSXObject extends PdfTemplate {
+
+ /** Creates a new instance of PdfPSXObject */
+ protected PdfPSXObject() {
+ super();
+ }
+
+ /**
+ * Constructs a PSXObject
+ * @param wr
+ */
+ public PdfPSXObject(PdfWriter wr) {
+ super(wr);
+ }
+
+ /**
+ * Gets the stream representing this object.
+ *
+ * @return the stream representing this object
+ * @throws IOException
+ */
+
+ PdfStream getFormXObject() throws IOException {
+ PdfStream s = new PdfStream(content.toByteArray());
+ s.put(PdfName.TYPE, PdfName.XOBJECT);
+ s.put(PdfName.SUBTYPE, PdfName.PS);
+ s.flateCompress();
+ return s;
+ }
+
+ /**
+ * Gets a duplicate of this
+ * A PdfPTableEvent can be associated to the table to do custom drawing
+ * when the table is rendered.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+
+public class PdfPTable implements Element{
+
+ /** The index of the original
+ *
+ *
+ * The table event is only fired for complete rows.
+ * @param colStart the first column to be written, zero index
+ * @param colEnd the last column to be written + 1. If it is -1 all the
+ * columns to the end are written
+ * @param rowStart the first row to be written, zero index
+ * @param rowEnd the last row to be written + 1. If it is -1 all the
+ * rows to the end are written
+ * @param xPos the x write coodinate
+ * @param yPos the y write coodinate
+ * @param canvases an array of 4
+ * The table event is only fired for complete rows.
+ *
+ * @param colStart the first column to be written, zero index
+ * @param colEnd the last column to be written + 1. If it is -1 all the
+ * @param rowStart the first row to be written, zero index
+ * @param rowEnd the last row to be written + 1. If it is -1 all the
+ * rows to the end are written
+ * @param xPos the x write coodinate
+ * @param yPos the y write coodinate
+ * @param canvas the
+ *
+ * The layers are placed in sequence on top of each other.
+ * @param canvas the
+ * It's only meaningful if setSplitRows(true).
+ * @param splitLate the property value
+ */
+ public void setSplitLate(boolean splitLate) {
+ this.splitLate = splitLate;
+ }
+
+ /**
+ * If true the table will be kept on one page if it fits, by forcing a
+ * new page if it doesn't fit on the current page. The default is to
+ * split the table over multiple pages.
+ *
+ * @param p_KeepTogether whether to try to keep the table on one page
+ */
+ public void setKeepTogether(boolean p_KeepTogether) {
+ keepTogether = p_KeepTogether;
+ }
+
+ public boolean getKeepTogether() {
+ return keepTogether;
+ }
+
+ /**
+ * Gets the number of rows in the footer.
+ * @return the number of rows in the footer
+ */
+ public int getFooterRows() {
+ return this.footerRows;
+ }
+
+ /**
+ * Sets the number of rows to be used for the footer. The number
+ * of footer rows are subtracted from the header rows. For
+ * example, for a table with two header rows and one footer row the
+ * code would be:
+ *
+ *
+ * Row 0 and 1 will be the header rows and row 2 will be the footer row.
+ * @param footerRows the number of rows to be used for the footer
+ */
+ public void setFooterRows(int footerRows) {
+ if (footerRows < 0)
+ footerRows = 0;
+ this.footerRows = footerRows;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPTableEvent.java b/src/main/java/com/lowagie/text/pdf/PdfPTableEvent.java
new file mode 100644
index 0000000..9ae90ff
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPTableEvent.java
@@ -0,0 +1,94 @@
+/*
+ * $Id: PdfPTableEvent.java,v 1.45 2005/05/04 14:32:42 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/** An interface that can be used to retrieve the position of cells in
+ *
+ * The
+ * A Page object is a dictionary whose keys describe a single page containing text,
+ * graphics, and images. A Page onjects is a leaf of the Pages tree.
+ * This method allways returns
+ * Note: do not use Document.add() inside a page event.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+
+public interface PdfPageEvent {
+
+/**
+ * Called when the document is opened.
+ *
+ * @param writer the
+ * Note that if even if a page is not written this method is still
+ * called. It is preferable to use
+ * Note that this method is called with the page number equal
+ * to the last page plus one.
+ *
+ * @param writer the
+ *
+ *
+ *
+ *
+ *
+ *
+ * It is usefull to pinpoint the
+ * Note: do not use Document.add() inside a page event.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+
+public class PdfPageEventHelper implements PdfPageEvent {
+
+/**
+ * Called when the document is opened.
+ *
+ * @param writer the
+ * Note that if even if a page is not written this method is still
+ * called. It is preferable to use
+ * Note that this method is called with the page number equal
+ * to the last page plus one.
+ *
+ * @param writer the
+ *
+ *
+ *
+ *
+ *
+ *
+ * It is usefull to pinpoint the
+ * The Pages of a document are accessible through a tree of nodes known as the Pages tree.
+ * This tree defines the ordering of the pages in the document.
+ * Rectangles are used to describe locations on the page and bounding boxes for several
+ * objects in PDF, such as fonts. A rectangle is represented as an
+ * The marking operations for drawing a page are stored in a stream that is the value of the
+ * Contents key in the Page object's dictionary. Each marking context includes a list
+ * of the named resources it uses. This resource list is stored as a dictionary that is the
+ * value of the context's Resources key, and it serves two functions: it enumerates
+ * the named resources in the contents stream, and it established the mapping from the names
+ * to the objects used by the marking operations.
+ * Consult PPKAppearances.pdf
+ * for further details.
+ * @param layer the layer
+ * @return a template
+ */
+ public PdfTemplate getLayer(int layer) {
+ if (layer < 0 || layer >= app.length)
+ return null;
+ PdfTemplate t = app[layer];
+ if (t == null) {
+ t = app[layer] = new PdfTemplate(writer);
+ t.setBoundingBox(rect);
+ writer.addDirectTemplateSimple(t, new PdfName("n" + layer));
+ }
+ return t;
+ }
+
+ /**
+ * Gets the template that aggregates all appearance layers. This corresponds to the /FRM resource.
+ *
+ * Consult PPKAppearances.pdf
+ * for further details.
+ * @return the template that aggregates all appearance layers
+ */
+ public PdfTemplate getTopLayer() {
+ if (frm == null) {
+ frm = new PdfTemplate(writer);
+ frm.setBoundingBox(rect);
+ writer.addDirectTemplateSimple(frm, new PdfName("FRM"));
+ }
+ return frm;
+ }
+
+ /**
+ * Gets the main appearance layer.
+ *
+ * Consult PPKAppearances.pdf
+ * for further details.
+ * @return the main appearance layer
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public PdfTemplate getAppearance() throws DocumentException, IOException {
+ if (app[0] == null) {
+ PdfTemplate t = app[0] = new PdfTemplate(writer);
+ t.setBoundingBox(new Rectangle(100, 100));
+ writer.addDirectTemplateSimple(t, new PdfName("n0"));
+ t.setLiteral("% DSBlank\n");
+ }
+ if (app[1] == null && !acro6Layers) {
+ PdfTemplate t = app[1] = new PdfTemplate(writer);
+ t.setBoundingBox(new Rectangle(100, 100));
+ writer.addDirectTemplateSimple(t, new PdfName("n1"));
+ t.setLiteral(questionMark);
+ }
+ if (app[2] == null) {
+ String text;
+ if (layer2Text == null) {
+ StringBuffer buf = new StringBuffer();
+ buf.append("Digitally signed by ").append(PdfPKCS7.getSubjectFields((X509Certificate)certChain[0]).getField("CN")).append("\n");
+ SimpleDateFormat sd = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z");
+ buf.append("Date: ").append(sd.format(signDate.getTime()));
+ if (reason != null)
+ buf.append("\n").append("Reason: ").append(reason);
+ if (location != null)
+ buf.append("\n").append("Location: ").append(location);
+ text = buf.toString();
+ }
+ else
+ text = layer2Text;
+ PdfTemplate t = app[2] = new PdfTemplate(writer);
+ t.setBoundingBox(rect);
+ writer.addDirectTemplateSimple(t, new PdfName("n2"));
+ if (image != null) {
+ if (imageScale == 0) {
+ t.addImage(image, rect.width(), 0, 0, rect.height(), 0, 0);
+ }
+ else {
+ float usableScale = imageScale;
+ if (imageScale < 0)
+ usableScale = Math.min(rect.width() / image.width(), rect.height() / image.height());
+ float w = image.width() * usableScale;
+ float h = image.height() * usableScale;
+ float x = (rect.width() - w) / 2;
+ float y = (rect.height() - h) / 2;
+ t.addImage(image, w, 0, 0, h, x, y);
+ }
+ }
+ Font font;
+ if (layer2Font == null)
+ font = new Font();
+ else
+ font = new Font(layer2Font);
+ float size = font.size();
+ if (size <= 0) {
+ Rectangle sr = new Rectangle(rect.width() - 2 * margin, rect.height() * (1 - topSection) - 2 * margin);
+ size = fitText(font, text, sr, 12, runDirection);
+ }
+ ColumnText ct = new ColumnText(t);
+ ct.setRunDirection(runDirection);
+ ct.setSimpleColumn(new Phrase(text, font), margin, 0, rect.width() - margin, rect.height() * (1 - topSection) - margin, size, Element.ALIGN_LEFT);
+ ct.go();
+ }
+ if (app[3] == null && !acro6Layers) {
+ PdfTemplate t = app[3] = new PdfTemplate(writer);
+ t.setBoundingBox(new Rectangle(100, 100));
+ writer.addDirectTemplateSimple(t, new PdfName("n3"));
+ t.setLiteral("% DSBlank\n");
+ }
+ if (app[4] == null && !acro6Layers) {
+ PdfTemplate t = app[4] = new PdfTemplate(writer);
+ t.setBoundingBox(new Rectangle(0, rect.height() * (1 - topSection), rect.right(), rect.top()));
+ writer.addDirectTemplateSimple(t, new PdfName("n4"));
+ Font font;
+ if (layer2Font == null)
+ font = new Font();
+ else
+ font = new Font(layer2Font);
+ float size = font.size();
+ String text = "Signature Not Verified";
+ if (layer4Text != null)
+ text = layer4Text;
+ Rectangle sr = new Rectangle(rect.width() - 2 * margin, rect.height() * topSection - 2 * margin);
+ size = fitText(font, text, sr, 15, runDirection);
+ ColumnText ct = new ColumnText(t);
+ ct.setRunDirection(runDirection);
+ ct.setSimpleColumn(new Phrase(text, font), margin, 0, rect.width() - margin, rect.height() - margin, size, Element.ALIGN_LEFT);
+ ct.go();
+ }
+ int rotation = writer.reader.getPageRotation(page);
+ Rectangle rotated = new Rectangle(rect);
+ int n = rotation;
+ while (n > 0) {
+ rotated = rotated.rotate();
+ n -= 90;
+ }
+ if (frm == null) {
+ frm = new PdfTemplate(writer);
+ frm.setBoundingBox(rotated);
+ writer.addDirectTemplateSimple(frm, new PdfName("FRM"));
+ float scale = Math.min(rect.width(), rect.height()) * 0.9f;
+ float x = (rect.width() - scale) / 2;
+ float y = (rect.height() - scale) / 2;
+ scale /= 100;
+ if (rotation == 90)
+ frm.concatCTM(0, 1, -1, 0, rect.height(), 0);
+ else if (rotation == 180)
+ frm.concatCTM(-1, 0, 0, -1, rect.width(), rect.height());
+ else if (rotation == 270)
+ frm.concatCTM(0, -1, 1, 0, 0, rect.width());
+ frm.addTemplate(app[0], 0, 0);
+ if (!acro6Layers)
+ frm.addTemplate(app[1], scale, 0, 0, scale, x, y);
+ frm.addTemplate(app[2], 0, 0);
+ if (!acro6Layers) {
+ frm.addTemplate(app[3], scale, 0, 0, scale, x, y);
+ frm.addTemplate(app[4], 0, 0);
+ }
+ }
+ PdfTemplate napp = new PdfTemplate(writer);
+ napp.setBoundingBox(rotated);
+ writer.addDirectTemplateSimple(napp, null);
+ napp.addTemplate(frm, 0, 0);
+ return napp;
+ }
+
+ /**
+ * Fits the text to some rectangle adjusting the font size as needed.
+ * @param font the font to use
+ * @param text the text
+ * @param rect the rectangle where the text must fit
+ * @param maxFontSize the maximum font size
+ * @param runDirection the run direction
+ * @return the calculated font size that makes the text fit
+ */
+ public static float fitText(Font font, String text, Rectangle rect, float maxFontSize, int runDirection) {
+ try {
+ ColumnText ct = null;
+ int status = 0;
+ if (maxFontSize <= 0) {
+ int cr = 0;
+ int lf = 0;
+ char t[] = text.toCharArray();
+ for (int k = 0; k < t.length; ++k) {
+ if (t[k] == '\n')
+ ++lf;
+ else if (t[k] == '\r')
+ ++cr;
+ }
+ int minLines = Math.max(cr, lf) + 1;
+ maxFontSize = Math.abs(rect.height()) / minLines - 0.001f;
+ }
+ font.setSize(maxFontSize);
+ Phrase ph = new Phrase(text, font);
+ ct = new ColumnText(null);
+ ct.setSimpleColumn(ph, rect.left(), rect.bottom(), rect.right(), rect.top(), maxFontSize, Element.ALIGN_LEFT);
+ ct.setRunDirection(runDirection);
+ status = ct.go(true);
+ if ((status & ColumnText.NO_MORE_TEXT) != 0)
+ return maxFontSize;
+ float precision = 0.1f;
+ float min = 0;
+ float max = maxFontSize;
+ float size = maxFontSize;
+ for (int k = 0; k < 50; ++k) { //just in case it doesn't converge
+ size = (min + max) / 2;
+ ct = new ColumnText(null);
+ font.setSize(size);
+ ct.setSimpleColumn(new Phrase(text, font), rect.left(), rect.bottom(), rect.right(), rect.top(), size, Element.ALIGN_LEFT);
+ ct.setRunDirection(runDirection);
+ status = ct.go(true);
+ if ((status & ColumnText.NO_MORE_TEXT) != 0) {
+ if (max - min < size * precision)
+ return size;
+ min = size;
+ }
+ else
+ max = size;
+ }
+ return size;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Sets the digest/signature to an external calculated value.
+ * @param digest the digest. This is the actual signature
+ * @param RSAdata the extra data that goes into the data tag in PKCS#7
+ * @param digestEncryptionAlgorithm the encryption algorithm. It may must be
+ * If calling preClose() dont't call PdfStamper.close().
+ *
+ * No external signatures are allowed if this methos is called.
+ * @throws IOException on error
+ * @throws DocumentException on error
+ */
+ public void preClose() throws IOException, DocumentException {
+ preClose(null);
+ }
+ /**
+ * This is the first method to be called when using external signatures. The general sequence is:
+ * preClose(), getDocumentBytes() and close().
+ *
+ * If calling preClose() dont't call PdfStamper.close().
+ *
+ * If using an external signature
+ *
+ * @return the document bytes that are hashable
+ */
+ public InputStream getRangeStream() {
+ return new PdfSignatureAppearance.RangeStream(raf, bout, range);
+ }
+
+ /**
+ * Gets the user made signature dictionary. This is the dictionary at the /V key.
+ * @return the user made signature dictionary
+ */
+ public com.lowagie.text.pdf.PdfDictionary getCryptoDictionary() {
+ return cryptoDictionary;
+ }
+
+ /**
+ * Sets a user made signature dictionary. This is the dictionary at the /V key.
+ * @param cryptoDictionary a user made signature dictionary
+ */
+ public void setCryptoDictionary(com.lowagie.text.pdf.PdfDictionary cryptoDictionary) {
+ this.cryptoDictionary = cryptoDictionary;
+ }
+
+ /**
+ * Gets the
+ * The main use is to insert external signatures.
+ * @return the instance of the standard signature dictionary
+ */
+ public com.lowagie.text.pdf.PdfSigGenericPKCS getSigStandard() {
+ return sigStandard;
+ }
+
+ /**
+ * Gets the signing contact.
+ * @return the signing contact
+ */
+ public String getContact() {
+ return this.contact;
+ }
+
+ /**
+ * Sets the signing contact.
+ * @param contact the signing contact
+ */
+ public void setContact(String contact) {
+ this.contact = contact;
+ }
+
+ /**
+ * Gets the n2 and n4 layer font.
+ * @return the n2 and n4 layer font
+ */
+ public Font getLayer2Font() {
+ return this.layer2Font;
+ }
+
+ /**
+ * Sets the n2 and n4 layer font. If the font size is zero, auto-fit will be used.
+ * @param layer2Font the n2 and n4 font
+ */
+ public void setLayer2Font(Font layer2Font) {
+ this.layer2Font = layer2Font;
+ }
+
+ /**
+ * Gets the Acrobat 6.0 layer mode.
+ * @return the Acrobat 6.0 layer mode
+ */
+ public boolean isAcro6Layers() {
+ return this.acro6Layers;
+ }
+
+ /**
+ * Acrobat 6.0 and higher recomends that only layer n2 and n4 be present. This method sets that mode.
+ * @param acro6Layers if
+ * It is also possible to change the field values and to
+ * flatten them. New fields can be added but not flattened.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfStamper {
+ /**
+ * The writer
+ */
+ protected PdfStamperImp stamper;
+ private HashMap moreInfo;
+ private boolean hasSignature;
+ private PdfSignatureAppearance sigApp;
+
+ /** Starts the process of adding extra content to an existing PDF
+ * document.
+ * @param reader the original document. It cannot be reused
+ * @param os the output stream
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public PdfStamper(PdfReader reader, OutputStream os) throws DocumentException, IOException {
+ stamper = new PdfStamperImp(reader, os, '\0', false);
+ }
+
+ /**
+ * Starts the process of adding extra content to an existing PDF
+ * document.
+ * @param reader the original document. It cannot be reused
+ * @param os the output stream
+ * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
+ * document
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public PdfStamper(PdfReader reader, OutputStream os, char pdfVersion) throws DocumentException, IOException {
+ stamper = new PdfStamperImp(reader, os, pdfVersion, false);
+ }
+
+ /**
+ * Starts the process of adding extra content to an existing PDF
+ * document, possibly as a new revision.
+ * @param reader the original document. It cannot be reused
+ * @param os the output stream
+ * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
+ * document
+ * @param append if
+ * If closing a signed document with an external signature the closing must be done
+ * in the
+ * Calling
+ * A possible use for adding a signature without invalidating an existing one is:
+ *
+ *
+ * Note that the pdf is created in memory.
+ *
+ * A possible use is:
+ *
+ *
+ * A possible use is:
+ *
+ *
+ * A stream, like a string, is a sequence of characters. However, an application can
+ * read a small portion of a stream at a time, while a string must be read in its entirety.
+ * For this reason, objects with potentially large amounts of data, such as images and
+ * page descriptions, are represented as streams.
+ *
+ * This method must be called and can only be called if the contructor {@link #PdfStream(InputStream,PdfWriter)}
+ * is used to create the stream.
+ * @throws IOException on error
+ * @see #PdfStream(InputStream,PdfWriter)
+ */
+ public void writeLength() throws IOException {
+ if (inputStream == null)
+ throw new UnsupportedOperationException("writeLength() can only be called in a contructed PdfStream(InputStream,PdfWriter).");
+ if (inputStreamLength == -1)
+ throw new IOException("writeLength() can only be called after output of the stream body.");
+ writer.addToBody(new PdfNumber(inputStreamLength), ref, false);
+ }
+
+ /**
+ * Gets the raw length of the stream.
+ * @return the raw length of the stream
+ */
+ public int getRawLength() {
+ return rawLength;
+ }
+
+ /**
+ * Compresses the stream.
+ */
+
+ public void flateCompress() {
+ if (!Document.compress)
+ return;
+ // check if the flateCompress-method has already been
+ if (compressed) {
+ return;
+ }
+ if (inputStream != null) {
+ compressed = true;
+ return;
+ }
+ // check if a filter already exists
+ PdfObject filter = get(PdfName.FILTER);
+ if (filter != null) {
+ if (filter.isName() && ((PdfName) filter).compareTo(PdfName.FLATEDECODE) == 0) {
+ return;
+ }
+ else if (filter.isArray() && ((PdfArray) filter).contains(PdfName.FLATEDECODE)) {
+ return;
+ }
+ else {
+ throw new RuntimeException("Stream could not be compressed: filter is not a name or array.");
+ }
+ }
+ try {
+ // compress
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ DeflaterOutputStream zip = new DeflaterOutputStream(stream);
+ if (streamBytes != null)
+ streamBytes.writeTo(zip);
+ else
+ zip.write(bytes);
+ zip.close();
+ // update the object
+ streamBytes = stream;
+ bytes = null;
+ put(PdfName.LENGTH, new PdfNumber(streamBytes.size()));
+ if (filter == null) {
+ put(PdfName.FILTER, PdfName.FLATEDECODE);
+ }
+ else {
+ PdfArray filters = new PdfArray(filter);
+ filters.add(PdfName.FLATEDECODE);
+ put(PdfName.FILTER, filters);
+ }
+ compressed = true;
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+// public int getStreamLength(PdfWriter writer) {
+// if (dicBytes == null)
+// toPdf(writer);
+// if (streamBytes != null)
+// return streamBytes.size() + dicBytes.length + SIZESTREAM;
+// else
+// return bytes.length + dicBytes.length + SIZESTREAM;
+// }
+
+ protected void superToPdf(PdfWriter writer, OutputStream os) throws IOException {
+ super.toPdf(writer, os);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfDictionary#toPdf(com.lowagie.text.pdf.PdfWriter, java.io.OutputStream)
+ */
+ public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
+ if (inputStream != null && compressed)
+ put(PdfName.FILTER, PdfName.FLATEDECODE);
+ superToPdf(writer, os);
+ os.write(STARTSTREAM);
+ PdfEncryption crypto = null;
+ if (writer != null)
+ crypto = writer.getEncryption();
+ if (crypto != null)
+ crypto.prepareKey();
+ if (inputStream != null) {
+ rawLength = 0;
+ DeflaterOutputStream def = null;
+ OutputStreamCounter osc = new OutputStreamCounter(os);
+ OutputStream fout = osc;
+ if (crypto != null)
+ fout = new PdfEncryptionStream(fout, crypto);
+ if (compressed)
+ fout = def = new DeflaterOutputStream(fout, new Deflater(Deflater.BEST_COMPRESSION), 0x8000);
+
+ byte buf[] = new byte[0x10000];
+ while (true) {
+ int n = inputStream.read(buf);
+ if (n <= 0)
+ break;
+ fout.write(buf, 0, n);
+ rawLength += n;
+ }
+ if (def != null)
+ def.finish();
+ inputStreamLength = osc.getCounter();
+ }
+ else {
+ if (crypto == null) {
+ if (streamBytes != null)
+ streamBytes.writeTo(os);
+ else
+ os.write(bytes);
+ }
+ else {
+ byte b[];
+ if (streamBytes != null) {
+ b = streamBytes.toByteArray();
+ crypto.encryptRC4(b);
+ }
+ else {
+ b = new byte[bytes.length];
+ crypto.encryptRC4(bytes, b);
+ }
+ os.write(b);
+ }
+ }
+ os.write(ENDSTREAM);
+ }
+
+ /**
+ * Writes the data content to an
+ * A string is a sequence of characters delimited by parenthesis. If a string is too long
+ * to be conveniently placed on a single line, it may be split across multiple lines by using
+ * the backslash character (\) at the end of a line to indicate that the string continues
+ * on the following line. Within a string, the backslash character is used as an escape to
+ * specify unbalanced parenthesis, non-printing ASCII characters, and the backslash character
+ * itself. Use of the \ddd escape sequence is the preferred way to represent characters
+ * outside the printable ASCII character set. Pre-requisite: the object must have been built with the parameter
+ * A
+ * When this
+ * This class covers the third section of Chapter 5 in the 'Portable Document Format
+ * Reference Manual version 1.3' (page 55-60). It contains the body of a PDF document
+ * (section 5.14) and it can also generate a Cross-reference Table (section 5.15).
+ *
+ * @see PdfWriter
+ * @see PdfObject
+ * @see PdfIndirectObject
+ */
+
+ public static class PdfBody {
+
+ // inner classes
+
+ /**
+ *
+ * This methods creates a
+ * This methods creates a
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 5.16 (page 59-60).
+ */
+
+ static class PdfTrailer extends PdfDictionary {
+
+ // membervariables
+
+ int offset;
+
+ // constructors
+
+ /**
+ * Constructs a PDF-Trailer.
+ *
+ * @param size the number of entries in the
+ * Remark: a PdfWriter can only be constructed by calling the method
+ *
+ * The document has to be open before you can begin to add content
+ * to the body of the document.
+ *
+ * @return a
+ * When this method is called, the PDF-document header is
+ * written to the outputstream.
+ */
+
+ public void open() {
+ super.open();
+ try {
+ os.write(HEADER);
+ body = new PdfBody(this);
+ if (pdfxConformance == PDFX32002) {
+ PdfDictionary sec = new PdfDictionary();
+ sec.put(PdfName.GAMMA, new PdfArray(new float[]{2.2f,2.2f,2.2f}));
+ sec.put(PdfName.MATRIX, new PdfArray(new float[]{0.4124f,0.2126f,0.0193f,0.3576f,0.7152f,0.1192f,0.1805f,0.0722f,0.9505f}));
+ sec.put(PdfName.WHITEPOINT, new PdfArray(new float[]{0.9505f,1f,1.089f}));
+ PdfArray arr = new PdfArray(PdfName.CALRGB);
+ arr.add(sec);
+ setDefaultColorspace(PdfName.DEFAULTRGB, addToBody(arr).getIndirectReference());
+ }
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+ private static void getOCGOrder(PdfArray order, PdfLayer layer) {
+ if (!layer.isOnPanel())
+ return;
+ if (layer.getTitle() == null)
+ order.add(layer.getRef());
+ ArrayList children = layer.getChildren();
+ if (children == null)
+ return;
+ PdfArray kids = new PdfArray();
+ if (layer.getTitle() != null)
+ kids.add(new PdfString(layer.getTitle(), PdfObject.TEXT_UNICODE));
+ for (int k = 0; k < children.size(); ++k) {
+ getOCGOrder(kids, (PdfLayer)children.get(k));
+ }
+ if (kids.size() > 0)
+ order.add(kids);
+ }
+
+ private void addASEvent(PdfName event, PdfName category) {
+ PdfArray arr = new PdfArray();
+ for (Iterator it = documentOCG.iterator(); it.hasNext();) {
+ PdfLayer layer = (PdfLayer)it.next();
+ PdfDictionary usage = (PdfDictionary)layer.get(PdfName.USAGE);
+ if (usage != null && usage.get(category) != null)
+ arr.add(layer.getRef());
+ }
+ if (arr.size() == 0)
+ return;
+ PdfDictionary d = (PdfDictionary)OCProperties.get(PdfName.D);
+ PdfArray arras = (PdfArray)d.get(PdfName.AS);
+ if (arras == null) {
+ arras = new PdfArray();
+ d.put(PdfName.AS, arras);
+ }
+ PdfDictionary as = new PdfDictionary();
+ as.put(PdfName.EVENT, event);
+ as.put(PdfName.CATEGORY, new PdfArray(category));
+ as.put(PdfName.OCGS, arr);
+ arras.add(as);
+ }
+
+ private void fillOCProperties(boolean erase) {
+ if (OCProperties == null)
+ OCProperties = new PdfOCProperties();
+ if (erase) {
+ OCProperties.remove(PdfName.OCGS);
+ OCProperties.remove(PdfName.D);
+ }
+ if (OCProperties.get(PdfName.OCGS) == null) {
+ PdfArray gr = new PdfArray();
+ for (Iterator it = documentOCG.iterator(); it.hasNext();) {
+ PdfLayer layer = (PdfLayer)it.next();
+ gr.add(layer.getRef());
+ }
+ OCProperties.put(PdfName.OCGS, gr);
+ }
+ if (OCProperties.get(PdfName.D) != null)
+ return;
+ ArrayList docOrder = new ArrayList(documentOCGorder);
+ for (Iterator it = docOrder.iterator(); it.hasNext();) {
+ PdfLayer layer = (PdfLayer)it.next();
+ if (layer.getParent() != null)
+ it.remove();
+ }
+ PdfArray order = new PdfArray();
+ for (Iterator it = docOrder.iterator(); it.hasNext();) {
+ PdfLayer layer = (PdfLayer)it.next();
+ getOCGOrder(order, layer);
+ }
+ PdfDictionary d = new PdfDictionary();
+ OCProperties.put(PdfName.D, d);
+ d.put(PdfName.ORDER, order);
+ PdfArray gr = new PdfArray();
+ for (Iterator it = documentOCG.iterator(); it.hasNext();) {
+ PdfLayer layer = (PdfLayer)it.next();
+ if (!layer.isOn())
+ gr.add(layer.getRef());
+ }
+ if (gr.size() > 0)
+ d.put(PdfName.OFF, gr);
+ if (OCGRadioGroup.size() > 0)
+ d.put(PdfName.RBGROUPS, OCGRadioGroup);
+ addASEvent(PdfName.VIEW, PdfName.ZOOM);
+ addASEvent(PdfName.VIEW, PdfName.VIEW);
+ addASEvent(PdfName.PRINT, PdfName.PRINT);
+ addASEvent(PdfName.EXPORT, PdfName.EXPORT);
+ d.put(PdfName.LISTMODE, PdfName.VISIBLEPAGES);
+ }
+
+ protected PdfDictionary getCatalog(PdfIndirectReference rootObj)
+ {
+ PdfDictionary catalog = ((PdfDocument)document).getCatalog(rootObj);
+ if (tagged) {
+ try {
+ getStructureTreeRoot().buildTree();
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ catalog.put(PdfName.STRUCTTREEROOT, structureTreeRoot.getReference());
+ PdfDictionary mi = new PdfDictionary();
+ mi.put(PdfName.MARKED, PdfBoolean.PDFTRUE);
+ catalog.put(PdfName.MARKINFO, mi);
+ }
+ if (documentOCG.size() == 0)
+ return catalog;
+ fillOCProperties(false);
+ catalog.put(PdfName.OCPROPERTIES, OCProperties);
+ return catalog;
+ }
+
+ protected void addSharedObjectsToBody() throws IOException {
+ // add the fonts
+ for (Iterator it = documentFonts.values().iterator(); it.hasNext();) {
+ FontDetails details = (FontDetails)it.next();
+ details.writeFont(this);
+ }
+ // add the form XObjects
+ for (Iterator it = formXObjects.values().iterator(); it.hasNext();) {
+ Object objs[] = (Object[])it.next();
+ PdfTemplate template = (PdfTemplate)objs[1];
+ if (template != null && template.getIndirectReference() instanceof PRIndirectReference)
+ continue;
+ if (template != null && template.getType() == PdfTemplate.TYPE_TEMPLATE) {
+ addToBody(template.getFormXObject(), template.getIndirectReference());
+ }
+ }
+ // add all the dependencies in the imported pages
+ for (Iterator it = importedPages.values().iterator(); it.hasNext();) {
+ currentPdfReaderInstance = (PdfReaderInstance)it.next();
+ currentPdfReaderInstance.writeAllPages();
+ }
+ currentPdfReaderInstance = null;
+ // add the color
+ for (Iterator it = documentColors.values().iterator(); it.hasNext();) {
+ ColorDetails color = (ColorDetails)it.next();
+ addToBody(color.getSpotColor(this), color.getIndirectReference());
+ }
+ // add the pattern
+ for (Iterator it = documentPatterns.keySet().iterator(); it.hasNext();) {
+ PdfPatternPainter pat = (PdfPatternPainter)it.next();
+ addToBody(pat.getPattern(), pat.getIndirectReference());
+ }
+ // add the shading patterns
+ for (Iterator it = documentShadingPatterns.keySet().iterator(); it.hasNext();) {
+ PdfShadingPattern shadingPattern = (PdfShadingPattern)it.next();
+ shadingPattern.addToBody();
+ }
+ // add the shadings
+ for (Iterator it = documentShadings.keySet().iterator(); it.hasNext();) {
+ PdfShading shading = (PdfShading)it.next();
+ shading.addToBody();
+ }
+ // add the extgstate
+ for (Iterator it = documentExtGState.keySet().iterator(); it.hasNext();) {
+ PdfDictionary gstate = (PdfDictionary)it.next();
+ PdfObject obj[] = (PdfObject[])documentExtGState.get(gstate);
+ addToBody(gstate, (PdfIndirectReference)obj[1]);
+ }
+ // add the properties
+ for (Iterator it = documentProperties.keySet().iterator(); it.hasNext();) {
+ Object prop = it.next();
+ PdfObject[] obj = (PdfObject[])documentProperties.get(prop);
+ if (prop instanceof PdfLayerMembership){
+ PdfLayerMembership layer = (PdfLayerMembership)prop;
+ addToBody(layer.getPdfObject(), layer.getRef());
+ }
+ else if ((prop instanceof PdfDictionary) && !(prop instanceof PdfLayer)){
+ addToBody((PdfDictionary)prop, (PdfIndirectReference)obj[1]);
+ }
+ }
+ for (Iterator it = documentOCG.iterator(); it.hasNext();) {
+ PdfOCG layer = (PdfOCG)it.next();
+ addToBody(layer.getPdfObject(), layer.getRef());
+ }
+ }
+
+ /**
+ * Signals that the
+ * The pages-tree is built and written to the outputstream.
+ * A Catalog is constructed, as well as an Info-object,
+ * the referencetable is composed and everything is written
+ * to the outputstream embedded in a Trailer.
+ */
+
+ public synchronized void close() {
+ if (open) {
+ if ((currentPageNumber - 1) != pageReferences.size())
+ throw new RuntimeException("The page " + pageReferences.size() +
+ " was requested but the document has only " + (currentPageNumber - 1) + " pages.");
+ pdf.close();
+ try {
+ addSharedObjectsToBody();
+ // add the root to the body
+ PdfIndirectReference rootRef = root.writePageTree();
+ // make the catalog-object and add it to the body
+ PdfDictionary catalog = getCatalog(rootRef);
+ // if there is XMP data to add: add it
+ if (xmpMetadata != null) {
+ PdfStream xmp = new PdfStream(xmpMetadata);
+ xmp.put(PdfName.TYPE, PdfName.METADATA);
+ xmp.put(PdfName.SUBTYPE, PdfName.XML);
+ catalog.put(PdfName.METADATA, body.add(xmp).getIndirectReference());
+ }
+ // make pdfx conformant
+ PdfDictionary info = getInfo();
+ if (pdfxConformance != PDFXNONE) {
+ if (info.get(PdfName.GTS_PDFXVERSION) == null) {
+ if (pdfxConformance == PDFX1A2001) {
+ info.put(PdfName.GTS_PDFXVERSION, new PdfString("PDF/X-1:2001"));
+ info.put(new PdfName("GTS_PDFXConformance"), new PdfString("PDF/X-1a:2001"));
+ }
+ else if (pdfxConformance == PDFX32002)
+ info.put(PdfName.GTS_PDFXVERSION, new PdfString("PDF/X-3:2002"));
+ }
+ if (info.get(PdfName.TITLE) == null) {
+ info.put(PdfName.TITLE, new PdfString("Pdf document"));
+ }
+ if (info.get(PdfName.CREATOR) == null) {
+ info.put(PdfName.CREATOR, new PdfString("Unknown"));
+ }
+ if (info.get(PdfName.TRAPPED) == null) {
+ info.put(PdfName.TRAPPED, new PdfName("False"));
+ }
+ getExtraCatalog();
+ if (extraCatalog.get(PdfName.OUTPUTINTENTS) == null) {
+ PdfDictionary out = new PdfDictionary(PdfName.OUTPUTINTENT);
+ out.put(PdfName.OUTPUTCONDITION, new PdfString("SWOP CGATS TR 001-1995"));
+ out.put(PdfName.OUTPUTCONDITIONIDENTIFIER, new PdfString("CGATS TR 001"));
+ out.put(PdfName.REGISTRYNAME, new PdfString("http://www.color.org"));
+ out.put(PdfName.INFO, new PdfString(""));
+ out.put(PdfName.S, PdfName.GTS_PDFX);
+ extraCatalog.put(PdfName.OUTPUTINTENTS, new PdfArray(out));
+ }
+ }
+ if (extraCatalog != null) {
+ catalog.mergeDifferent(extraCatalog);
+ }
+ PdfIndirectObject indirectCatalog = addToBody(catalog, false);
+ // add the info-object to the body
+ PdfIndirectObject infoObj = addToBody(info, false);
+ PdfIndirectReference encryption = null;
+ PdfObject fileID = null;
+ body.flushObjStm();
+ if (crypto != null) {
+ PdfIndirectObject encryptionObject = addToBody(crypto.getEncryptionDictionary(), false);
+ encryption = encryptionObject.getIndirectReference();
+ fileID = crypto.getFileID();
+ }
+ else
+ fileID = PdfEncryption.createInfoId(PdfEncryption.createDocumentId());
+
+ // write the cross-reference table of the body
+ body.writeCrossReferenceTable(os, indirectCatalog.getIndirectReference(),
+ infoObj.getIndirectReference(), encryption, fileID, prevxref);
+
+ // make the trailer
+ if (fullCompression) {
+ os.write(getISOBytes("startxref\n"));
+ os.write(getISOBytes(String.valueOf(body.offset())));
+ os.write(getISOBytes("\n%%EOF\n"));
+ }
+ else {
+ PdfTrailer trailer = new PdfTrailer(body.size(),
+ body.offset(),
+ indirectCatalog.getIndirectReference(),
+ infoObj.getIndirectReference(),
+ encryption,
+ fileID, prevxref);
+ trailer.toPdf(this, os);
+ }
+ super.close();
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+ }
+
+ // methods
+
+ /**
+ * Sometimes it is necessary to know where the just added
+ *
+ * The colorspace is applied immediately when creating templates and at the page
+ * end for the main document content.
+ * @param key the name of the colorspace. It can be
+ * If set before opening the document it will also set the pdf version to 1.5.
+ */
+ public void setFullCompression() {
+ this.fullCompression = true;
+ setPdfVersion(VERSION_1_5);
+ }
+
+ /**
+ * Gets the Optional Content Properties Dictionary. Each call fills the dictionary with the current layer
+ * state. It's advisable to only call this method right before close and do any modifications
+ * at that time.
+ * @return the Optional Content Properties Dictionary
+ */
+ public PdfOCProperties getOCProperties() {
+ fillOCProperties(true);
+ return OCProperties;
+ }
+
+ /**
+ * Sets a collection of optional content groups whose states are intended to follow
+ * a "radio button" paradigm. That is, the state of at most one optional
+ * content group in the array should be ON at a time: if one group is turned
+ * ON, all others must be turned OFF.
+ * @param group the radio group
+ */
+ public void addOCGRadioGroup(ArrayList group) {
+ PdfArray ar = new PdfArray();
+ for (int k = 0; k < group.size(); ++k) {
+ PdfLayer layer = (PdfLayer)group.get(k);
+ if (layer.getTitle() == null)
+ ar.add(layer.getRef());
+ }
+ if (ar.size() == 0)
+ return;
+ OCGRadioGroup.add(ar);
+ }
+
+ /**
+ * Sets the the thumbnail image for the current page.
+ * @param image the image
+ * @throws PdfException on error
+ * @throws DocumentException or error
+ */
+ public void setThumbnail(Image image) throws PdfException, DocumentException {
+ pdf.setThumbnail(image);
+ }
+
+ /**
+ * A UserUnit is a value that defines the default user space unit.
+ * The minimum UserUnit is 1 (1 unit = 1/72 inch).
+ * The maximum UserUnit is 75,000.
+ * Remark that this userunit only works starting with PDF1.6!
+ * @return Returns the userunit.
+ */
+ public float getUserunit() {
+ return userunit;
+ }
+ /**
+ * A UserUnit is a value that defines the default user space unit.
+ * The minimum UserUnit is 1 (1 unit = 1/72 inch).
+ * The maximum UserUnit is 75,000.
+ * Remark that this userunit only works starting with PDF1.6!
+ * @param userunit The userunit to set.
+ * @throws DocumentException
+ */
+ public void setUserunit(float userunit) throws DocumentException {
+ if (userunit < 1f || userunit > 75000f) throw new DocumentException("UserUnit should be a value between 1 and 75000.");
+ this.userunit = userunit;
+ setPdfVersion(VERSION_1_6);
+ }
+
+ /**
+ * Sets XMP Metadata.
+ * @param xmpMetadata The xmpMetadata to set.
+ */
+ public void setXmpMetadata(byte[] xmpMetadata) {
+ this.xmpMetadata = xmpMetadata;
+ }
+
+ /**
+ * Creates XMP Metadata based on the metadata in the PdfDocument.
+ */
+ public void createXmpMetadata() {
+ setXmpMetadata(pdf.createXmpMetadata());
+ }
+
+ /**
+ * Releases the memory used by a template by writing it to the output. The template
+ * can still be added to any content but changes to the template itself won't have
+ * any effect.
+ * @param tp the template to release
+ * @throws IOException on error
+ */
+ public void releaseTemplate(PdfTemplate tp) throws IOException {
+ PdfIndirectReference ref = tp.getIndirectReference();
+ Object[] objs = (Object[])formXObjects.get(ref);
+ if (objs == null || objs[1] == null)
+ return;
+ PdfTemplate template = (PdfTemplate)objs[1];
+ if (template.getIndirectReference() instanceof PRIndirectReference)
+ return;
+ if (template.getType() == PdfTemplate.TYPE_TEMPLATE) {
+ addToBody(template.getFormXObject(), template.getIndirectReference());
+ objs[1] = null;
+ }
+ }
+
+ /**
+ * Mark this document for tagging. It must be called before open.
+ */
+ public void setTagged() {
+ if (open)
+ throw new IllegalArgumentException("Tagging must be set before opening the document.");
+ tagged = true;
+ }
+
+ /**
+ * Check if the document is marked for tagging.
+ * @return
+ * Example usage:
+ *
+ *
+ * Example usage:
+ *
+ *
+ * This method blocks until the two bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next two bytes of this stream, interpreted as a signed
+ * 16-bit number.
+ * @exception EOFException if this stream reaches the end before reading
+ * two bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public final short readShortLE() throws IOException {
+ int ch1 = this.read();
+ int ch2 = this.read();
+ if ((ch1 | ch2) < 0)
+ throw new EOFException();
+ return (short)((ch2 << 8) + (ch1 << 0));
+ }
+
+ public int readUnsignedShort() throws IOException {
+ int ch1 = this.read();
+ int ch2 = this.read();
+ if ((ch1 | ch2) < 0)
+ throw new EOFException();
+ return (ch1 << 8) + ch2;
+ }
+
+ /**
+ * Reads an unsigned 16-bit number from this stream in little-endian order.
+ * This method reads
+ * two bytes from the stream, starting at the current stream pointer.
+ * If the bytes read, in order, are
+ *
+ * This method blocks until the two bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next two bytes of this stream, interpreted as an
+ * unsigned 16-bit integer.
+ * @exception EOFException if this stream reaches the end before reading
+ * two bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public final int readUnsignedShortLE() throws IOException {
+ int ch1 = this.read();
+ int ch2 = this.read();
+ if ((ch1 | ch2) < 0)
+ throw new EOFException();
+ return (ch2 << 8) + (ch1 << 0);
+ }
+
+ public char readChar() throws IOException {
+ int ch1 = this.read();
+ int ch2 = this.read();
+ if ((ch1 | ch2) < 0)
+ throw new EOFException();
+ return (char)((ch1 << 8) + ch2);
+ }
+
+ /**
+ * Reads a Unicode character from this stream in little-endian order.
+ * This method reads two
+ * bytes from the stream, starting at the current stream pointer.
+ * If the bytes read, in order, are
+ *
+ * This method blocks until the two bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next two bytes of this stream as a Unicode character.
+ * @exception EOFException if this stream reaches the end before reading
+ * two bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public final char readCharLE() throws IOException {
+ int ch1 = this.read();
+ int ch2 = this.read();
+ if ((ch1 | ch2) < 0)
+ throw new EOFException();
+ return (char)((ch2 << 8) + (ch1 << 0));
+ }
+
+ public int readInt() throws IOException {
+ int ch1 = this.read();
+ int ch2 = this.read();
+ int ch3 = this.read();
+ int ch4 = this.read();
+ if ((ch1 | ch2 | ch3 | ch4) < 0)
+ throw new EOFException();
+ return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
+ }
+
+ /**
+ * Reads a signed 32-bit integer from this stream in little-endian order.
+ * This method reads 4
+ * bytes from the stream, starting at the current stream pointer.
+ * If the bytes read, in order, are
+ * This method blocks until the four bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next four bytes of this stream, interpreted as an
+ *
+ * This method blocks until the four bytes are read, the end of the
+ * stream is detected, or an exception is thrown.
+ *
+ * @return the next four bytes of this stream, interpreted as a
+ *
+ * The general systax is:
+ * You can have multiple ranges separated by commas ','. The '!' modifier removes the
+ * range from what is already selected. The range changes are incremental, that is,
+ * numbers are added or deleted as the range appears. The start or the end, but not both, can be ommited.
+ */
+public class SequenceList {
+ protected static final int COMMA = 1;
+ protected static final int MINUS = 2;
+ protected static final int NOT = 3;
+ protected static final int TEXT = 4;
+ protected static final int NUMBER = 5;
+ protected static final int END = 6;
+ protected static final char EOT = '\uffff';
+
+ private static final int FIRST = 0;
+ private static final int DIGIT = 1;
+ private static final int OTHER = 2;
+ private static final int DIGIT2 = 3;
+ private static final String NOT_OTHER = "-,!0123456789";
+
+ protected char text[];
+ protected int ptr;
+ protected int number;
+ protected String other;
+
+ protected int low;
+ protected int high;
+ protected boolean odd;
+ protected boolean even;
+ protected boolean inverse;
+
+ protected SequenceList(String range) {
+ ptr = 0;
+ text = range.toCharArray();
+ }
+
+ protected char nextChar() {
+ while (true) {
+ if (ptr >= text.length)
+ return EOT;
+ char c = text[ptr++];
+ if (c > ' ')
+ return c;
+ }
+ }
+
+ protected void putBack() {
+ --ptr;
+ if (ptr < 0)
+ ptr = 0;
+ }
+
+ protected int getType() {
+ StringBuffer buf = new StringBuffer();
+ int state = FIRST;
+ while (true) {
+ char c = nextChar();
+ if (c == EOT) {
+ if (state == DIGIT) {
+ number = Integer.parseInt(other = buf.toString());
+ return NUMBER;
+ }
+ else if (state == OTHER) {
+ other = buf.toString().toLowerCase();
+ return TEXT;
+ }
+ return END;
+ }
+ switch (state) {
+ case FIRST:
+ switch (c) {
+ case '!':
+ return NOT;
+ case '-':
+ return MINUS;
+ case ',':
+ return COMMA;
+ }
+ buf.append(c);
+ if (c >= '0' && c <= '9')
+ state = DIGIT;
+ else
+ state = OTHER;
+ break;
+ case DIGIT:
+ if (c >= '0' && c <= '9')
+ buf.append(c);
+ else {
+ putBack();
+ number = Integer.parseInt(other = buf.toString());
+ return NUMBER;
+ }
+ break;
+ case OTHER:
+ if (NOT_OTHER.indexOf(c) < 0)
+ buf.append(c);
+ else {
+ putBack();
+ other = buf.toString().toLowerCase();
+ return TEXT;
+ }
+ break;
+ }
+ }
+ }
+
+ private void otherProc() {
+ if (other.equals("odd") || other.equals("o")) {
+ odd = true;
+ even = false;
+ }
+ else if (other.equals("even") || other.equals("e")) {
+ odd = false;
+ even = true;
+ }
+ }
+
+ protected boolean getAttributes() {
+ low = -1;
+ high = -1;
+ odd = even = inverse = false;
+ int state = OTHER;
+ while (true) {
+ int type = getType();
+ if (type == END || type == COMMA) {
+ if (state == DIGIT)
+ high = low;
+ return (type == END);
+ }
+ switch (state) {
+ case OTHER:
+ switch (type) {
+ case NOT:
+ inverse = true;
+ break;
+ case MINUS:
+ state = DIGIT2;
+ break;
+ default:
+ if (type == NUMBER) {
+ low = number;
+ state = DIGIT;
+ }
+ else
+ otherProc();
+ break;
+ }
+ break;
+ case DIGIT:
+ switch (type) {
+ case NOT:
+ inverse = true;
+ state = OTHER;
+ high = low;
+ break;
+ case MINUS:
+ state = DIGIT2;
+ break;
+ default:
+ high = low;
+ state = OTHER;
+ otherProc();
+ break;
+ }
+ break;
+ case DIGIT2:
+ switch (type) {
+ case NOT:
+ inverse = true;
+ state = OTHER;
+ break;
+ case MINUS:
+ break;
+ case NUMBER:
+ high = number;
+ state = OTHER;
+ break;
+ default:
+ state = OTHER;
+ otherProc();
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Generates a list of numbers from a string.
+ * @param ranges the comma separated ranges
+ * @param maxNumber the maximum number in the range
+ * @return a list with the numbers as
+ * The list structure is composed by a number of HashMap, keyed by strings, one HashMap
+ * for each bookmark.
+ * The element values are all strings with the exception of the key "Kids" that has
+ * another list for the child bookmarks.
+ *
+ * All the bookmarks have a "Title" with the
+ * bookmark title and optionally a "Style" that can be "bold", "italic" or a
+ * combination of both. They can also have a "Color" key with a value of three
+ * floats separated by spaces. The key "Open" can have the values "true" or "false" and
+ * signals the open status of the children. It's "true" by default.
+ *
+ * The actions and the parameters can be:
+ *
+ *
+ *
+ * The parser can:
+ *
+ *
+ * The code is based on
+ * http://www.javaworld.com/javaworld/javatips/javatip128/ with some extra
+ * code from XERCES to recognize the encoding.
+ */
+public class SimpleXMLParser {
+ private static final HashMap fIANA2JavaMap = new HashMap();
+ private static final HashMap entityMap = new HashMap();
+
+ private static int popMode(Stack st) {
+ if(!st.empty())
+ return ((Integer)st.pop()).intValue();
+ else
+ return PRE;
+ }
+
+ private final static int
+ TEXT = 1,
+ ENTITY = 2,
+ OPEN_TAG = 3,
+ CLOSE_TAG = 4,
+ START_TAG = 5,
+ ATTRIBUTE_LVALUE = 6,
+ ATTRIBUTE_EQUAL = 9,
+ ATTRIBUTE_RVALUE = 10,
+ QUOTE = 7,
+ IN_TAG = 8,
+ SINGLE_TAG = 12,
+ COMMENT = 13,
+ DONE = 11,
+ DOCTYPE = 14,
+ PRE = 15,
+ CDATA = 16;
+
+ private SimpleXMLParser() {
+ }
+
+ /**
+ * Parses the XML document firing the events to the handler.
+ * @param doc the document handler
+ * @param in the document. The encoding is deduced from the stream. The stream is not closed
+ * @throws IOException on error
+ */
+ public static void parse(SimpleXMLDocHandler doc, InputStream in) throws IOException {
+ byte b4[] = new byte[4];
+ int count = in.read(b4);
+ if (count != 4)
+ throw new IOException("Insufficient length.");
+ String encoding = getEncodingName(b4);
+ String decl = null;
+ if (encoding.equals("UTF-8")) {
+ StringBuffer sb = new StringBuffer();
+ int c;
+ while ((c = in.read()) != -1) {
+ if (c == '>')
+ break;
+ sb.append((char)c);
+ }
+ decl = sb.toString();
+ }
+ else if (encoding.equals("CP037")) {
+ ByteArrayOutputStream bi = new ByteArrayOutputStream();
+ int c;
+ while ((c = in.read()) != -1) {
+ if (c == 0x6e) // that's '>' in ebcdic
+ break;
+ bi.write(c);
+ }
+ decl = new String(bi.toByteArray(), "CP037");
+ }
+ if (decl != null) {
+ decl = getDeclaredEncoding(decl);
+ if (decl != null)
+ encoding = decl;
+ }
+ parse(doc, new InputStreamReader(in, getJavaEncoding(encoding)));
+ }
+
+ private static String getDeclaredEncoding(String decl) {
+ if (decl == null)
+ return null;
+ int idx = decl.indexOf("encoding");
+ if (idx < 0)
+ return null;
+ int idx1 = decl.indexOf('"', idx);
+ int idx2 = decl.indexOf('\'', idx);
+ if (idx1 == idx2)
+ return null;
+ if ((idx1 < 0 && idx2 > 0) || (idx2 > 0 && idx2 < idx1)) {
+ int idx3 = decl.indexOf('\'', idx2 + 1);
+ if (idx3 < 0)
+ return null;
+ return decl.substring(idx2 + 1, idx3);
+ }
+ if ((idx2 < 0 && idx1 > 0) || (idx1 > 0 && idx1 < idx2)) {
+ int idx3 = decl.indexOf('"', idx1 + 1);
+ if (idx3 < 0)
+ return null;
+ return decl.substring(idx1 + 1, idx3);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the java encoding from the IANA encoding. If the encoding cannot be found
+ * it returns the input.
+ * @param iana the IANA encoding
+ * @return the java encoding
+ */
+ public static String getJavaEncoding(String iana) {
+ String IANA = iana.toUpperCase();
+ String jdec = (String)fIANA2JavaMap.get(IANA);
+ if (jdec == null)
+ jdec = iana;
+ return jdec;
+ }
+
+ public static void parse(SimpleXMLDocHandler doc,Reader r) throws IOException {
+ parse(doc, null, r, false);
+ }
+
+ /**
+ * Parses the XML document firing the events to the handler.
+ * @param doc the document handler
+ * @param r the document. The encoding is already resolved. The reader is not closed
+ * @throws IOException on error
+ */
+ public static void parse(SimpleXMLDocHandler doc, SimpleXMLDocHandlerComment comment, Reader r, boolean html) throws IOException {
+ BufferedReader reader;
+ if (r instanceof BufferedReader)
+ reader = (BufferedReader)r;
+ else
+ reader = new BufferedReader(r);
+ Stack st = new Stack();
+ int depth = 0;
+ int mode = PRE;
+ int c = 0;
+ int quotec = '"';
+ depth = 0;
+ StringBuffer sb = new StringBuffer();
+ StringBuffer etag = new StringBuffer();
+ String tagName = null;
+ String lvalue = null;
+ String rvalue = null;
+ HashMap attrs = null;
+ st = new Stack();
+ doc.startDocument();
+ int line=1, col=0;
+ boolean eol = false;
+ if (html)
+ mode = TEXT;
+ int pushBack = -1;
+ while(true) {
+ if (pushBack != -1) {
+ c = pushBack;
+ pushBack = -1;
+ }
+ else
+ c = reader.read();
+ if (c == -1)
+ break;
+
+ // We need to map \r, \r\n, and \n to \n
+ // See XML spec section 2.11
+ if(c == '\n' && eol) {
+ eol = false;
+ continue;
+ } else if(eol) {
+ eol = false;
+ } else if(c == '\n') {
+ line++;
+ col=0;
+ } else if(c == '\r') {
+ eol = true;
+ c = '\n';
+ line++;
+ col=0;
+ } else {
+ col++;
+ }
+
+ if(mode == DONE) {
+ doc.endDocument();
+ return;
+
+ // We are between tags collecting text.
+ } else if(mode == TEXT) {
+ if(c == '<') {
+ st.push(new Integer(mode));
+ mode = START_TAG;
+ if(sb.length() > 0) {
+ doc.text(sb.toString());
+ sb.setLength(0);
+ }
+ } else if(c == '&') {
+ st.push(new Integer(mode));
+ mode = ENTITY;
+ etag.setLength(0);
+ } else
+ sb.append((char)c);
+
+ // we are processing a closing tag: e.g.
+ } else if(mode == CLOSE_TAG) {
+ if(c == '>') {
+ mode = popMode(st);
+ tagName = sb.toString();
+ if (html)
+ tagName = tagName.toLowerCase();
+ sb.setLength(0);
+ depth--;
+ if(!html && depth==0)
+ mode = DONE;
+ doc.endElement(tagName);
+ } else {
+ if (!Character.isWhitespace((char)c))
+ sb.append((char)c);
+ }
+
+ // we are processing CDATA
+ } else if(mode == CDATA) {
+ if(c == '>'
+ && sb.toString().endsWith("]]")) {
+ sb.setLength(sb.length()-2);
+ doc.text(sb.toString());
+ sb.setLength(0);
+ mode = popMode(st);
+ } else
+ sb.append((char)c);
+
+ // we are processing a comment. We are inside
+ // the looking for the -->.
+ } else if(mode == COMMENT) {
+ if(c == '>'
+ && sb.toString().endsWith("--")) {
+ if (comment != null) {
+ sb.setLength(sb.length() - 2);
+ comment.comment(sb.toString());
+ }
+ sb.setLength(0);
+ mode = popMode(st);
+ } else
+ sb.append((char)c);
+
+ // We are outside the root tag element
+ } else if(mode == PRE) {
+ if(c == '<') {
+ mode = TEXT;
+ st.push(new Integer(mode));
+ mode = START_TAG;
+ }
+
+ // We are inside one of these ... ?>
+ // or one of these
+ } else if(mode == DOCTYPE) {
+ if(c == '>') {
+ mode = popMode(st);
+ if(mode == TEXT) mode = PRE;
+ }
+
+ // we have just seen a < and
+ // are wondering what we are looking at
+ //
+ * The key is the code and the value is an
+ *
+ * An example:
+ *
+ *
+ * It is based in the JAI codec.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class BmpImage {
+
+ // BMP variables
+ private InputStream inputStream;
+ private long bitmapFileSize;
+ private long bitmapOffset;
+ private long compression;
+ private long imageSize;
+ private byte palette[];
+ private int imageType;
+ private int numBands;
+ private boolean isBottomUp;
+ private int bitsPerPixel;
+ private int redMask, greenMask, blueMask, alphaMask;
+ public HashMap properties = new HashMap();
+ private long xPelsPerMeter;
+ private long yPelsPerMeter;
+ // BMP Image types
+ private static final int VERSION_2_1_BIT = 0;
+ private static final int VERSION_2_4_BIT = 1;
+ private static final int VERSION_2_8_BIT = 2;
+ private static final int VERSION_2_24_BIT = 3;
+
+ private static final int VERSION_3_1_BIT = 4;
+ private static final int VERSION_3_4_BIT = 5;
+ private static final int VERSION_3_8_BIT = 6;
+ private static final int VERSION_3_24_BIT = 7;
+
+ private static final int VERSION_3_NT_16_BIT = 8;
+ private static final int VERSION_3_NT_32_BIT = 9;
+
+ private static final int VERSION_4_1_BIT = 10;
+ private static final int VERSION_4_4_BIT = 11;
+ private static final int VERSION_4_8_BIT = 12;
+ private static final int VERSION_4_16_BIT = 13;
+ private static final int VERSION_4_24_BIT = 14;
+ private static final int VERSION_4_32_BIT = 15;
+
+ // Color space types
+ private static final int LCS_CALIBRATED_RGB = 0;
+ private static final int LCS_sRGB = 1;
+ private static final int LCS_CMYK = 2;
+
+ // Compression Types
+ private static final int BI_RGB = 0;
+ private static final int BI_RLE8 = 1;
+ private static final int BI_RLE4 = 2;
+ private static final int BI_BITFIELDS = 3;
+
+ int width;
+ int height;
+
+ BmpImage(InputStream is, boolean noHeader, int size) throws IOException {
+ bitmapFileSize = size;
+ bitmapOffset = 0;
+ process(is, noHeader);
+ }
+
+ /** Reads a BMP from an url.
+ * @param url the url
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(URL url) throws IOException {
+ InputStream is = null;
+ try {
+ is = url.openStream();
+ Image img = getImage(is);
+ img.setUrl(url);
+ return img;
+ }
+ finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ /** Reads a BMP from a stream. The stream is not closed.
+ * @param is the stream
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(InputStream is) throws IOException {
+ return getImage(is, false, 0);
+ }
+
+ /** Reads a BMP from a stream. The stream is not closed.
+ * The BMP may not have a header and be considered as a plain DIB.
+ * @param is the stream
+ * @param noHeader true to process a plain DIB
+ * @param size the size of the DIB. Not used for a BMP
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(InputStream is, boolean noHeader, int size) throws IOException {
+ BmpImage bmp = new BmpImage(is, noHeader, size);
+ try {
+ Image img = bmp.getImage();
+ img.setDpi((int)((double)bmp.xPelsPerMeter * 0.0254), (int)((double)bmp.yPelsPerMeter * 0.0254));
+ img.setOriginalType(Image.ORIGINAL_BMP);
+ return img;
+ }
+ catch (BadElementException be) {
+ throw new ExceptionConverter(be);
+ }
+ }
+
+ /** Reads a BMP from a file.
+ * @param file the file
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(String file) throws IOException {
+ return getImage(Image.toURL(file));
+ }
+
+ /** Reads a BMP from a byte array.
+ * @param data the byte array
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(byte data[]) throws IOException {
+ InputStream is = null;
+ try {
+ is = new ByteArrayInputStream(data);
+ Image img = getImage(is);
+ img.setOriginalData(data);
+ return img;
+ }
+ finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+
+ protected void process(InputStream stream, boolean noHeader) throws IOException {
+ if (noHeader || stream instanceof BufferedInputStream) {
+ inputStream = stream;
+ } else {
+ inputStream = new BufferedInputStream(stream);
+ }
+ if (!noHeader) {
+ // Start File Header
+ if (!(readUnsignedByte(inputStream) == 'B' &&
+ readUnsignedByte(inputStream) == 'M')) {
+ throw new
+ RuntimeException("Invalid magic value for BMP file.");
+ }
+
+ // Read file size
+ bitmapFileSize = readDWord(inputStream);
+
+ // Read the two reserved fields
+ readWord(inputStream);
+ readWord(inputStream);
+
+ // Offset to the bitmap from the beginning
+ bitmapOffset = readDWord(inputStream);
+
+ // End File Header
+ }
+ // Start BitmapCoreHeader
+ long size = readDWord(inputStream);
+
+ if (size == 12) {
+ width = readWord(inputStream);
+ height = readWord(inputStream);
+ } else {
+ width = readLong(inputStream);
+ height = readLong(inputStream);
+ }
+
+ int planes = readWord(inputStream);
+ bitsPerPixel = readWord(inputStream);
+
+ properties.put("color_planes", new Integer(planes));
+ properties.put("bits_per_pixel", new Integer(bitsPerPixel));
+
+ // As BMP always has 3 rgb bands, except for Version 5,
+ // which is bgra
+ numBands = 3;
+ if (bitmapOffset == 0)
+ bitmapOffset = size;
+ if (size == 12) {
+ // Windows 2.x and OS/2 1.x
+ properties.put("bmp_version", "BMP v. 2.x");
+
+ // Classify the image type
+ if (bitsPerPixel == 1) {
+ imageType = VERSION_2_1_BIT;
+ } else if (bitsPerPixel == 4) {
+ imageType = VERSION_2_4_BIT;
+ } else if (bitsPerPixel == 8) {
+ imageType = VERSION_2_8_BIT;
+ } else if (bitsPerPixel == 24) {
+ imageType = VERSION_2_24_BIT;
+ }
+
+ // Read in the palette
+ int numberOfEntries = (int)((bitmapOffset-14-size) / 3);
+ int sizeOfPalette = numberOfEntries*3;
+ if (bitmapOffset == size) {
+ switch (imageType) {
+ case VERSION_2_1_BIT:
+ sizeOfPalette = 2 * 3;
+ break;
+ case VERSION_2_4_BIT:
+ sizeOfPalette = 16 * 3;
+ break;
+ case VERSION_2_8_BIT:
+ sizeOfPalette = 256 * 3;
+ break;
+ case VERSION_2_24_BIT:
+ sizeOfPalette = 0;
+ break;
+ }
+ bitmapOffset = size + sizeOfPalette;
+ }
+ palette = new byte[sizeOfPalette];
+ inputStream.read(palette, 0, sizeOfPalette);
+ properties.put("palette", palette);
+ } else {
+
+ compression = readDWord(inputStream);
+ imageSize = readDWord(inputStream);
+ xPelsPerMeter = readLong(inputStream);
+ yPelsPerMeter = readLong(inputStream);
+ long colorsUsed = readDWord(inputStream);
+ long colorsImportant = readDWord(inputStream);
+
+ switch((int)compression) {
+ case BI_RGB:
+ properties.put("compression", "BI_RGB");
+ break;
+
+ case BI_RLE8:
+ properties.put("compression", "BI_RLE8");
+ break;
+
+ case BI_RLE4:
+ properties.put("compression", "BI_RLE4");
+ break;
+
+ case BI_BITFIELDS:
+ properties.put("compression", "BI_BITFIELDS");
+ break;
+ }
+
+ properties.put("x_pixels_per_meter", new Long(xPelsPerMeter));
+ properties.put("y_pixels_per_meter", new Long(yPelsPerMeter));
+ properties.put("colors_used", new Long(colorsUsed));
+ properties.put("colors_important", new Long(colorsImportant));
+
+ if (size == 40) {
+ // Windows 3.x and Windows NT
+ switch((int)compression) {
+
+ case BI_RGB: // No compression
+ case BI_RLE8: // 8-bit RLE compression
+ case BI_RLE4: // 4-bit RLE compression
+
+ if (bitsPerPixel == 1) {
+ imageType = VERSION_3_1_BIT;
+ } else if (bitsPerPixel == 4) {
+ imageType = VERSION_3_4_BIT;
+ } else if (bitsPerPixel == 8) {
+ imageType = VERSION_3_8_BIT;
+ } else if (bitsPerPixel == 24) {
+ imageType = VERSION_3_24_BIT;
+ } else if (bitsPerPixel == 16) {
+ imageType = VERSION_3_NT_16_BIT;
+ redMask = 0x7C00;
+ greenMask = 0x3E0;
+ blueMask = 0x1F;
+ properties.put("red_mask", new Integer(redMask));
+ properties.put("green_mask", new Integer(greenMask));
+ properties.put("blue_mask", new Integer(blueMask));
+ } else if (bitsPerPixel == 32) {
+ imageType = VERSION_3_NT_32_BIT;
+ redMask = 0x00FF0000;
+ greenMask = 0x0000FF00;
+ blueMask = 0x000000FF;
+ properties.put("red_mask", new Integer(redMask));
+ properties.put("green_mask", new Integer(greenMask));
+ properties.put("blue_mask", new Integer(blueMask));
+ }
+
+ // Read in the palette
+ int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
+ int sizeOfPalette = numberOfEntries*4;
+ if (bitmapOffset == size) {
+ switch (imageType) {
+ case VERSION_3_1_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4;
+ break;
+ case VERSION_3_4_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4;
+ break;
+ case VERSION_3_8_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4;
+ break;
+ default:
+ sizeOfPalette = 0;
+ break;
+ }
+ bitmapOffset = size + sizeOfPalette;
+ }
+ palette = new byte[sizeOfPalette];
+ inputStream.read(palette, 0, sizeOfPalette);
+ properties.put("palette", palette);
+
+ properties.put("bmp_version", "BMP v. 3.x");
+ break;
+
+ case BI_BITFIELDS:
+
+ if (bitsPerPixel == 16) {
+ imageType = VERSION_3_NT_16_BIT;
+ } else if (bitsPerPixel == 32) {
+ imageType = VERSION_3_NT_32_BIT;
+ }
+
+ // BitsField encoding
+ redMask = (int)readDWord(inputStream);
+ greenMask = (int)readDWord(inputStream);
+ blueMask = (int)readDWord(inputStream);
+
+ properties.put("red_mask", new Integer(redMask));
+ properties.put("green_mask", new Integer(greenMask));
+ properties.put("blue_mask", new Integer(blueMask));
+
+ if (colorsUsed != 0) {
+ // there is a palette
+ sizeOfPalette = (int)colorsUsed*4;
+ palette = new byte[sizeOfPalette];
+ inputStream.read(palette, 0, sizeOfPalette);
+ properties.put("palette", palette);
+ }
+
+ properties.put("bmp_version", "BMP v. 3.x NT");
+ break;
+
+ default:
+ throw new
+ RuntimeException("Invalid compression specified in BMP file.");
+ }
+ } else if (size == 108) {
+ // Windows 4.x BMP
+
+ properties.put("bmp_version", "BMP v. 4.x");
+
+ // rgb masks, valid only if comp is BI_BITFIELDS
+ redMask = (int)readDWord(inputStream);
+ greenMask = (int)readDWord(inputStream);
+ blueMask = (int)readDWord(inputStream);
+ // Only supported for 32bpp BI_RGB argb
+ alphaMask = (int)readDWord(inputStream);
+ long csType = readDWord(inputStream);
+ int redX = readLong(inputStream);
+ int redY = readLong(inputStream);
+ int redZ = readLong(inputStream);
+ int greenX = readLong(inputStream);
+ int greenY = readLong(inputStream);
+ int greenZ = readLong(inputStream);
+ int blueX = readLong(inputStream);
+ int blueY = readLong(inputStream);
+ int blueZ = readLong(inputStream);
+ long gammaRed = readDWord(inputStream);
+ long gammaGreen = readDWord(inputStream);
+ long gammaBlue = readDWord(inputStream);
+
+ if (bitsPerPixel == 1) {
+ imageType = VERSION_4_1_BIT;
+ } else if (bitsPerPixel == 4) {
+ imageType = VERSION_4_4_BIT;
+ } else if (bitsPerPixel == 8) {
+ imageType = VERSION_4_8_BIT;
+ } else if (bitsPerPixel == 16) {
+ imageType = VERSION_4_16_BIT;
+ if ((int)compression == BI_RGB) {
+ redMask = 0x7C00;
+ greenMask = 0x3E0;
+ blueMask = 0x1F;
+ }
+ } else if (bitsPerPixel == 24) {
+ imageType = VERSION_4_24_BIT;
+ } else if (bitsPerPixel == 32) {
+ imageType = VERSION_4_32_BIT;
+ if ((int)compression == BI_RGB) {
+ redMask = 0x00FF0000;
+ greenMask = 0x0000FF00;
+ blueMask = 0x000000FF;
+ }
+ }
+
+ properties.put("red_mask", new Integer(redMask));
+ properties.put("green_mask", new Integer(greenMask));
+ properties.put("blue_mask", new Integer(blueMask));
+ properties.put("alpha_mask", new Integer(alphaMask));
+
+ // Read in the palette
+ int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
+ int sizeOfPalette = numberOfEntries*4;
+ if (bitmapOffset == size) {
+ switch (imageType) {
+ case VERSION_4_1_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4;
+ break;
+ case VERSION_4_4_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4;
+ break;
+ case VERSION_4_8_BIT:
+ sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4;
+ break;
+ default:
+ sizeOfPalette = 0;
+ break;
+ }
+ bitmapOffset = size + sizeOfPalette;
+ }
+ palette = new byte[sizeOfPalette];
+ inputStream.read(palette, 0, sizeOfPalette);
+
+ if (palette != null || palette.length != 0) {
+ properties.put("palette", palette);
+ }
+
+ switch((int)csType) {
+ case LCS_CALIBRATED_RGB:
+ // All the new fields are valid only for this case
+ properties.put("color_space", "LCS_CALIBRATED_RGB");
+ properties.put("redX", new Integer(redX));
+ properties.put("redY", new Integer(redY));
+ properties.put("redZ", new Integer(redZ));
+ properties.put("greenX", new Integer(greenX));
+ properties.put("greenY", new Integer(greenY));
+ properties.put("greenZ", new Integer(greenZ));
+ properties.put("blueX", new Integer(blueX));
+ properties.put("blueY", new Integer(blueY));
+ properties.put("blueZ", new Integer(blueZ));
+ properties.put("gamma_red", new Long(gammaRed));
+ properties.put("gamma_green", new Long(gammaGreen));
+ properties.put("gamma_blue", new Long(gammaBlue));
+
+ // break;
+ throw new
+ RuntimeException("Not implemented yet.");
+
+ case LCS_sRGB:
+ // Default Windows color space
+ properties.put("color_space", "LCS_sRGB");
+ break;
+
+ case LCS_CMYK:
+ properties.put("color_space", "LCS_CMYK");
+ // break;
+ throw new
+ RuntimeException("Not implemented yet.");
+ }
+
+ } else {
+ properties.put("bmp_version", "BMP v. 5.x");
+ throw new
+ RuntimeException("BMP version 5 not implemented yet.");
+ }
+ }
+
+ if (height > 0) {
+ // bottom up image
+ isBottomUp = true;
+ } else {
+ // top down image
+ isBottomUp = false;
+ height = Math.abs(height);
+ }
+ // When number of bitsPerPixel is <= 8, we use IndexColorModel.
+ if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
+
+ numBands = 1;
+
+
+ // Create IndexColorModel from the palette.
+ byte r[], g[], b[];
+ int sizep;
+ if (imageType == VERSION_2_1_BIT ||
+ imageType == VERSION_2_4_BIT ||
+ imageType == VERSION_2_8_BIT) {
+
+ sizep = palette.length/3;
+
+ if (sizep > 256) {
+ sizep = 256;
+ }
+
+ int off;
+ r = new byte[sizep];
+ g = new byte[sizep];
+ b = new byte[sizep];
+ for (int i=0; i
+ * It is based in part in the JAI codec.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PngImage {
+/** Some PNG specific values. */
+ public static final int[] PNGID = {137, 80, 78, 71, 13, 10, 26, 10};
+
+/** A PNG marker. */
+ public static final String IHDR = "IHDR";
+
+/** A PNG marker. */
+ public static final String PLTE = "PLTE";
+
+/** A PNG marker. */
+ public static final String IDAT = "IDAT";
+
+/** A PNG marker. */
+ public static final String IEND = "IEND";
+
+/** A PNG marker. */
+ public static final String tRNS = "tRNS";
+
+/** A PNG marker. */
+ public static final String pHYs = "pHYs";
+
+/** A PNG marker. */
+ public static final String gAMA = "gAMA";
+
+/** A PNG marker. */
+ public static final String cHRM = "cHRM";
+
+/** A PNG marker. */
+ public static final String sRGB = "sRGB";
+
+/** A PNG marker. */
+ public static final String iCCP = "iCCP";
+
+ private static final int TRANSFERSIZE = 4096;
+ private static final int PNG_FILTER_NONE = 0;
+ private static final int PNG_FILTER_SUB = 1;
+ private static final int PNG_FILTER_UP = 2;
+ private static final int PNG_FILTER_AVERAGE = 3;
+ private static final int PNG_FILTER_PAETH = 4;
+ private static final PdfName intents[] = {PdfName.PERCEPTUAL,
+ PdfName.RELATIVECALORIMETRIC,PdfName.SATURATION,PdfName.ABSOLUTECALORIMETRIC};
+
+ InputStream is;
+ DataInputStream dataStream;
+ int width;
+ int height;
+ int bitDepth;
+ int colorType;
+ int compressionMethod;
+ int filterMethod;
+ int interlaceMethod;
+ PdfDictionary additional = new PdfDictionary();
+ byte image[];
+ byte smask[];
+ byte trans[];
+ NewByteArrayOutputStream idat = new NewByteArrayOutputStream();
+ int dpiX;
+ int dpiY;
+ float XYRatio;
+ boolean genBWMask;
+ boolean palShades;
+ int transRedGray = -1;
+ int transGreen = -1;
+ int transBlue = -1;
+ int inputBands;
+ int bytesPerPixel; // number of bytes per input pixel
+ byte colorTable[];
+ float gamma = 1f;
+ boolean hasCHRM = false;
+ float xW, yW, xR, yR, xG, yG, xB, yB;
+ PdfName intent;
+ ICC_Profile icc_profile;
+
+
+
+ /** Creates a new instance of PngImage */
+ PngImage(InputStream is) {
+ this.is = is;
+ }
+
+ /** Reads a PNG from an url.
+ * @param url the url
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(URL url) throws IOException {
+ InputStream is = null;
+ try {
+ is = url.openStream();
+ Image img = getImage(is);
+ img.setUrl(url);
+ return img;
+ }
+ finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ /** Reads a PNG from a stream.
+ * @param is the stream
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(InputStream is) throws IOException {
+ PngImage png = new PngImage(is);
+ return png.getImage();
+ }
+
+ /** Reads a PNG from a file.
+ * @param file the file
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(String file) throws IOException {
+ return getImage(Image.toURL(file));
+ }
+
+ /** Reads a PNG from a byte array.
+ * @param data the byte array
+ * @throws IOException on error
+ * @return the image
+ */
+ public static Image getImage(byte data[]) throws IOException {
+ InputStream is = null;
+ try {
+ is = new ByteArrayInputStream(data);
+ Image img = getImage(is);
+ img.setOriginalData(data);
+ return img;
+ }
+ finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ boolean checkMarker(String s) {
+ if (s.length() != 4)
+ return false;
+ for (int k = 0; k < 4; ++k) {
+ char c = s.charAt(k);
+ if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z'))
+ return false;
+ }
+ return true;
+ }
+
+ void readPng() throws IOException {
+ for (int i = 0; i < PNGID.length; i++) {
+ if (PNGID[i] != is.read()) {
+ throw new IOException("File is not a valid PNG.");
+ }
+ }
+ byte buffer[] = new byte[TRANSFERSIZE];
+ while (true) {
+ int len = getInt(is);
+ String marker = getString(is);
+ if (len < 0 || !checkMarker(marker))
+ throw new IOException("Corrupted PNG file.");
+ if (IDAT.equals(marker)) {
+ int size;
+ while (len != 0) {
+ size = is.read(buffer, 0, Math.min(len, TRANSFERSIZE));
+ if (size < 0)
+ return;
+ idat.write(buffer, 0, size);
+ len -= size;
+ }
+ }
+ else if (tRNS.equals(marker)) {
+ switch (colorType) {
+ case 0:
+ if (len >= 2) {
+ len -= 2;
+ int gray = getWord(is);
+ if (bitDepth == 16)
+ transRedGray = gray;
+ else
+ additional.put(PdfName.MASK, new PdfLiteral("["+gray+" "+gray+"]"));
+ }
+ break;
+ case 2:
+ if (len >= 6) {
+ len -= 6;
+ int red = getWord(is);
+ int green = getWord(is);
+ int blue = getWord(is);
+ if (bitDepth == 16) {
+ transRedGray = red;
+ transGreen = green;
+ transBlue = blue;
+ }
+ else
+ additional.put(PdfName.MASK, new PdfLiteral("["+red+" "+red+" "+green+" "+green+" "+blue+" "+blue+"]"));
+ }
+ break;
+ case 3:
+ if (len > 0) {
+ trans = new byte[len];
+ for (int k = 0; k < len; ++k)
+ trans[k] = (byte)is.read();
+ len = 0;
+ }
+ break;
+ }
+ Image.skip(is, len);
+ }
+ else if (IHDR.equals(marker)) {
+ width = getInt(is);
+ height = getInt(is);
+
+ bitDepth = is.read();
+ colorType = is.read();
+ compressionMethod = is.read();
+ filterMethod = is.read();
+ interlaceMethod = is.read();
+ }
+ else if (PLTE.equals(marker)) {
+ if (colorType == 3) {
+ PdfArray colorspace = new PdfArray();
+ colorspace.add(PdfName.INDEXED);
+ colorspace.add(getColorspace());
+ colorspace.add(new PdfNumber(len / 3 - 1));
+ ByteBuffer colortable = new ByteBuffer();
+ while ((len--) > 0) {
+ colortable.append_i(is.read());
+ }
+ colorspace.add(new PdfString(colorTable = colortable.toByteArray()));
+ additional.put(PdfName.COLORSPACE, colorspace);
+ }
+ else {
+ Image.skip(is, len);
+ }
+ }
+ else if (pHYs.equals(marker)) {
+ int dx = getInt(is);
+ int dy = getInt(is);
+ int unit = is.read();
+ if (unit == 1) {
+ dpiX = (int)((float)dx * 0.0254f);
+ dpiY = (int)((float)dy * 0.0254f);
+ }
+ else {
+ if (dy != 0)
+ XYRatio = (float)dx / (float)dy;
+ }
+ }
+ else if (cHRM.equals(marker)) {
+ xW = (float)getInt(is) / 100000f;
+ yW = (float)getInt(is) / 100000f;
+ xR = (float)getInt(is) / 100000f;
+ yR = (float)getInt(is) / 100000f;
+ xG = (float)getInt(is) / 100000f;
+ yG = (float)getInt(is) / 100000f;
+ xB = (float)getInt(is) / 100000f;
+ yB = (float)getInt(is) / 100000f;
+ hasCHRM = !(Math.abs(xW)<0.0001f||Math.abs(yW)<0.0001f||Math.abs(xR)<0.0001f||Math.abs(yR)<0.0001f||Math.abs(xG)<0.0001f||Math.abs(yG)<0.0001f||Math.abs(xB)<0.0001f||Math.abs(yB)<0.0001f);
+ }
+ else if (sRGB.equals(marker)) {
+ int ri = is.read();
+ intent = intents[ri];
+ gamma = 2.2f;
+ xW = 0.3127f;
+ yW = 0.329f;
+ xR = 0.64f;
+ yR = 0.33f;
+ xG = 0.3f;
+ yG = 0.6f;
+ xB = 0.15f;
+ yB = 0.06f;
+ hasCHRM = true;
+ }
+ else if (gAMA.equals(marker)) {
+ int gm = getInt(is);
+ if (gm != 0) {
+ gamma = 100000f / (float)gm;
+ if (!hasCHRM) {
+ xW = 0.3127f;
+ yW = 0.329f;
+ xR = 0.64f;
+ yR = 0.33f;
+ xG = 0.3f;
+ yG = 0.6f;
+ xB = 0.15f;
+ yB = 0.06f;
+ hasCHRM = true;
+ }
+ }
+ }
+ else if (iCCP.equals(marker)) {
+ do {
+ --len;
+ } while (is.read() != 0);
+ is.read();
+ --len;
+ byte icccom[] = new byte[len];
+ int p = 0;
+ while (len > 0) {
+ int r = is.read(icccom, p, len);
+ if (r < 0)
+ throw new IOException("Premature end of file.");
+ p += r;
+ len -= r;
+ }
+ byte iccp[] = PdfReader.FlateDecode(icccom, true);
+ icccom = null;
+ try {
+ icc_profile = ICC_Profile.getInstance(iccp);
+ }
+ catch (Exception e) {
+ icc_profile = null;
+ }
+ }
+ else if (IEND.equals(marker)) {
+ break;
+ }
+ else {
+ Image.skip(is, len);
+ }
+ Image.skip(is, 4);
+ }
+ }
+
+ PdfObject getColorspace() {
+ if (icc_profile != null) {
+ if ((colorType & 2) == 0)
+ return PdfName.DEVICEGRAY;
+ else
+ return PdfName.DEVICERGB;
+ }
+ if (gamma == 1f && !hasCHRM) {
+ if ((colorType & 2) == 0)
+ return PdfName.DEVICEGRAY;
+ else
+ return PdfName.DEVICERGB;
+ }
+ else {
+ PdfArray array = new PdfArray();
+ PdfDictionary dic = new PdfDictionary();
+ if ((colorType & 2) == 0) {
+ if (gamma == 1f)
+ return PdfName.DEVICEGRAY;
+ array.add(PdfName.CALGRAY);
+ dic.put(PdfName.GAMMA, new PdfNumber(gamma));
+ dic.put(PdfName.WHITEPOINT, new PdfLiteral("[1 1 1]"));
+ array.add(dic);
+ }
+ else {
+ PdfObject wp = new PdfLiteral("[1 1 1]");
+ array.add(PdfName.CALRGB);
+ if (gamma != 1f) {
+ PdfArray gm = new PdfArray();
+ PdfNumber n = new PdfNumber(gamma);
+ gm.add(n);
+ gm.add(n);
+ gm.add(n);
+ dic.put(PdfName.GAMMA, gm);
+ }
+ if (hasCHRM) {
+ float z = yW*((xG-xB)*yR-(xR-xB)*yG+(xR-xG)*yB);
+ float YA = yR*((xG-xB)*yW-(xW-xB)*yG+(xW-xG)*yB)/z;
+ float XA = YA*xR/yR;
+ float ZA = YA*((1-xR)/yR-1);
+ float YB = -yG*((xR-xB)*yW-(xW-xB)*yR+(xW-xR)*yB)/z;
+ float XB = YB*xG/yG;
+ float ZB = YB*((1-xG)/yG-1);
+ float YC = yB*((xR-xG)*yW-(xW-xG)*yW+(xW-xR)*yG)/z;
+ float XC = YC*xB/yB;
+ float ZC = YC*((1-xB)/yB-1);
+ float XW = XA+XB+XC;
+ float YW = 1;//YA+YB+YC;
+ float ZW = ZA+ZB+ZC;
+ PdfArray wpa = new PdfArray();
+ wpa.add(new PdfNumber(XW));
+ wpa.add(new PdfNumber(YW));
+ wpa.add(new PdfNumber(ZW));
+ wp = wpa;
+ PdfArray matrix = new PdfArray();
+ matrix.add(new PdfNumber(XA));
+ matrix.add(new PdfNumber(YA));
+ matrix.add(new PdfNumber(ZA));
+ matrix.add(new PdfNumber(XB));
+ matrix.add(new PdfNumber(YB));
+ matrix.add(new PdfNumber(ZB));
+ matrix.add(new PdfNumber(XC));
+ matrix.add(new PdfNumber(YC));
+ matrix.add(new PdfNumber(ZC));
+ dic.put(PdfName.MATRIX, matrix);
+ }
+ dic.put(PdfName.WHITEPOINT, wp);
+ array.add(dic);
+ }
+ return array;
+ }
+ }
+
+ Image getImage() throws IOException {
+ readPng();
+ try {
+ int pal0 = 0;
+ int palIdx = 0;
+ palShades = false;
+ if (trans != null) {
+ for (int k = 0; k < trans.length; ++k) {
+ int n = trans[k] & 0xff;
+ if (n == 0) {
+ ++pal0;
+ palIdx = k;
+ }
+ if (n != 0 && n != 255) {
+ palShades = true;
+ break;
+ }
+ }
+ }
+ if ((colorType & 4) != 0)
+ palShades = true;
+ genBWMask = (!palShades && (pal0 > 1 || transRedGray >= 0));
+ if (!palShades && !genBWMask && pal0 == 1) {
+ additional.put(PdfName.MASK, new PdfLiteral("["+palIdx+" "+palIdx+"]"));
+ }
+ boolean needDecode = (interlaceMethod == 1) || (bitDepth == 16) || ((colorType & 4) != 0) || palShades || genBWMask;
+ switch (colorType) {
+ case 0:
+ inputBands = 1;
+ break;
+ case 2:
+ inputBands = 3;
+ break;
+ case 3:
+ inputBands = 1;
+ break;
+ case 4:
+ inputBands = 2;
+ break;
+ case 6:
+ inputBands = 4;
+ break;
+ }
+ if (needDecode)
+ decodeIdat();
+ int components = inputBands;
+ if ((colorType & 4) != 0)
+ --components;
+ int bpc = bitDepth;
+ if (bpc == 16)
+ bpc = 8;
+ Image img;
+ if (image != null)
+ img = Image.getInstance(width, height, components, bpc, image);
+ else {
+ img = new ImgRaw(width, height, components, bpc, idat.toByteArray());
+ img.setDeflated(true);
+ PdfDictionary decodeparms = new PdfDictionary();
+ decodeparms.put(PdfName.BITSPERCOMPONENT, new PdfNumber(bitDepth));
+ decodeparms.put(PdfName.PREDICTOR, new PdfNumber(15));
+ decodeparms.put(PdfName.COLUMNS, new PdfNumber(width));
+ decodeparms.put(PdfName.COLORS, new PdfNumber((colorType == 3 || (colorType & 2) == 0) ? 1 : 3));
+ additional.put(PdfName.DECODEPARMS, decodeparms);
+ }
+ if (additional.get(PdfName.COLORSPACE) == null)
+ additional.put(PdfName.COLORSPACE, getColorspace());
+ if (intent != null)
+ additional.put(PdfName.INTENT, intent);
+ if (additional.size() > 0)
+ img.setAdditional(additional);
+ if (icc_profile != null)
+ img.tagICC(icc_profile);
+ if (palShades) {
+ Image im2 = Image.getInstance(width, height, 1, 8, smask);
+ im2.makeMask();
+ img.setImageMask(im2);
+ }
+ if (genBWMask) {
+ Image im2 = Image.getInstance(width, height, 1, 1, smask);
+ im2.makeMask();
+ img.setImageMask(im2);
+ }
+ img.setDpi(dpiX, dpiY);
+ img.setXYRatio(XYRatio);
+ img.setOriginalType(Image.ORIGINAL_PNG);
+ return img;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ void decodeIdat() {
+ int nbitDepth = bitDepth;
+ if (nbitDepth == 16)
+ nbitDepth = 8;
+ int size = -1;
+ bytesPerPixel = (bitDepth == 16) ? 2 : 1;
+ switch (colorType) {
+ case 0:
+ size = (nbitDepth * width + 7) / 8 * height;
+ break;
+ case 2:
+ size = width * 3 * height;
+ bytesPerPixel *= 3;
+ break;
+ case 3:
+ if (interlaceMethod == 1)
+ size = (nbitDepth * width + 7) / 8 * height;
+ bytesPerPixel = 1;
+ break;
+ case 4:
+ size = width * height;
+ bytesPerPixel *= 2;
+ break;
+ case 6:
+ size = width * 3 * height;
+ bytesPerPixel *= 4;
+ break;
+ }
+ if (size >= 0)
+ image = new byte[size];
+ if (palShades)
+ smask = new byte[width * height];
+ else if (genBWMask)
+ smask = new byte[(width + 7) / 8 * height];
+ ByteArrayInputStream bai = new ByteArrayInputStream(idat.getBuf(), 0, idat.size());
+ InputStream infStream = new InflaterInputStream(bai, new Inflater());
+ dataStream = new DataInputStream(infStream);
+
+ if (interlaceMethod != 1) {
+ decodePass(0, 0, 1, 1, width, height);
+ }
+ else {
+ decodePass(0, 0, 8, 8, (width + 7)/8, (height + 7)/8);
+ decodePass(4, 0, 8, 8, (width + 3)/8, (height + 7)/8);
+ decodePass(0, 4, 4, 8, (width + 3)/4, (height + 3)/8);
+ decodePass(2, 0, 4, 4, (width + 1)/4, (height + 3)/4);
+ decodePass(0, 2, 2, 4, (width + 1)/2, (height + 1)/4);
+ decodePass(1, 0, 2, 2, width/2, (height + 1)/2);
+ decodePass(0, 1, 1, 2, width, height/2);
+ }
+
+ }
+
+ void decodePass( int xOffset, int yOffset,
+ int xStep, int yStep,
+ int passWidth, int passHeight) {
+ if ((passWidth == 0) || (passHeight == 0)) {
+ return;
+ }
+
+ int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8;
+ byte[] curr = new byte[bytesPerRow];
+ byte[] prior = new byte[bytesPerRow];
+
+ // Decode the (sub)image row-by-row
+ int srcY, dstY;
+ for (srcY = 0, dstY = yOffset;
+ srcY < passHeight;
+ srcY++, dstY += yStep) {
+ // Read the filter type byte and a row of data
+ int filter = 0;
+ try {
+ filter = dataStream.read();
+ dataStream.readFully(curr, 0, bytesPerRow);
+ } catch (Exception e) {
+ // empty on purpose
+ }
+
+ switch (filter) {
+ case PNG_FILTER_NONE:
+ break;
+ case PNG_FILTER_SUB:
+ decodeSubFilter(curr, bytesPerRow, bytesPerPixel);
+ break;
+ case PNG_FILTER_UP:
+ decodeUpFilter(curr, prior, bytesPerRow);
+ break;
+ case PNG_FILTER_AVERAGE:
+ decodeAverageFilter(curr, prior, bytesPerRow, bytesPerPixel);
+ break;
+ case PNG_FILTER_PAETH:
+ decodePaethFilter(curr, prior, bytesPerRow, bytesPerPixel);
+ break;
+ default:
+ // Error -- uknown filter type
+ throw new RuntimeException("PNG filter unknown.");
+ }
+
+ processPixels(curr, xOffset, xStep, dstY, passWidth);
+
+ // Swap curr and prior
+ byte[] tmp = prior;
+ prior = curr;
+ curr = tmp;
+ }
+ }
+
+ void processPixels(byte curr[], int xOffset, int step, int y, int width) {
+ int srcX, dstX;
+
+ int out[] = getPixel(curr);
+ int sizes = 0;
+ switch (colorType) {
+ case 0:
+ case 3:
+ case 4:
+ sizes = 1;
+ break;
+ case 2:
+ case 6:
+ sizes = 3;
+ break;
+ }
+ if (image != null) {
+ dstX = xOffset;
+ int yStride = (sizes*this.width*(bitDepth == 16 ? 8 : bitDepth)+ 7)/8;
+ for (srcX = 0; srcX < width; srcX++) {
+ setPixel(image, out, inputBands * srcX, sizes, dstX, y, bitDepth, yStride);
+ dstX += step;
+ }
+ }
+ if (palShades) {
+ if ((colorType & 4) != 0) {
+ if (bitDepth == 16) {
+ for (int k = 0; k < width; ++k)
+ out[k * inputBands + sizes] >>>= 8;
+ }
+ int yStride = this.width;
+ dstX = xOffset;
+ for (srcX = 0; srcX < width; srcX++) {
+ setPixel(smask, out, inputBands * srcX + sizes, 1, dstX, y, 8, yStride);
+ dstX += step;
+ }
+ }
+ else { //colorType 3
+ int yStride = this.width;
+ int v[] = new int[1];
+ dstX = xOffset;
+ for (srcX = 0; srcX < width; srcX++) {
+ int idx = out[srcX];
+ if (idx < trans.length)
+ v[0] = trans[idx];
+ setPixel(smask, v, 0, 1, dstX, y, 8, yStride);
+ dstX += step;
+ }
+ }
+ }
+ else if (genBWMask) {
+ switch (colorType) {
+ case 3: {
+ int yStride = (this.width + 7) / 8;
+ int v[] = new int[1];
+ dstX = xOffset;
+ for (srcX = 0; srcX < width; srcX++) {
+ int idx = out[srcX];
+ if (idx < trans.length)
+ v[0] = (trans[idx] == 0 ? 1 : 0);
+ setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
+ dstX += step;
+ }
+ break;
+ }
+ case 0: {
+ int yStride = (this.width + 7) / 8;
+ int v[] = new int[1];
+ dstX = xOffset;
+ for (srcX = 0; srcX < width; srcX++) {
+ int g = out[srcX];
+ v[0] = (g == transRedGray ? 1 : 0);
+ setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
+ dstX += step;
+ }
+ break;
+ }
+ case 2: {
+ int yStride = (this.width + 7) / 8;
+ int v[] = new int[1];
+ dstX = xOffset;
+ for (srcX = 0; srcX < width; srcX++) {
+ int markRed = inputBands * srcX;
+ v[0] = (out[markRed] == transRedGray && out[markRed + 1] == transGreen
+ && out[markRed + 2] == transBlue ? 1 : 0);
+ setPixel(smask, v, 0, 1, dstX, y, 1, yStride);
+ dstX += step;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ static int getPixel(byte image[], int x, int y, int bitDepth, int bytesPerRow) {
+ if (bitDepth == 8) {
+ int pos = bytesPerRow * y + x;
+ return image[pos] & 0xff;
+ }
+ else {
+ int pos = bytesPerRow * y + x / (8 / bitDepth);
+ int v = image[pos] >> (8 - bitDepth * (x % (8 / bitDepth))- bitDepth);
+ return v & ((1 << bitDepth) - 1);
+ }
+ }
+
+ static void setPixel(byte image[], int data[], int offset, int size, int x, int y, int bitDepth, int bytesPerRow) {
+ if (bitDepth == 8) {
+ int pos = bytesPerRow * y + size * x;
+ for (int k = 0; k < size; ++k)
+ image[pos + k] = (byte)data[k + offset];
+ }
+ else if (bitDepth == 16) {
+ int pos = bytesPerRow * y + size * x;
+ for (int k = 0; k < size; ++k)
+ image[pos + k] = (byte)(data[k + offset] >>> 8);
+ }
+ else {
+ int pos = bytesPerRow * y + x / (8 / bitDepth);
+ int v = data[offset] << (8 - bitDepth * (x % (8 / bitDepth))- bitDepth);
+ image[pos] |= v;
+ }
+ }
+
+ int[] getPixel(byte curr[]) {
+ switch (bitDepth) {
+ case 8: {
+ int out[] = new int[curr.length];
+ for (int k = 0; k < out.length; ++k)
+ out[k] = curr[k] & 0xff;
+ return out;
+ }
+ case 16: {
+ int out[] = new int[curr.length / 2];
+ for (int k = 0; k < out.length; ++k)
+ out[k] = ((curr[k * 2] & 0xff) << 8) + (curr[k * 2 + 1] & 0xff);
+ return out;
+ }
+ default: {
+ int out[] = new int[curr.length * 8 / bitDepth];
+ int idx = 0;
+ int passes = 8 / bitDepth;
+ int mask = (1 << bitDepth) - 1;
+ for (int k = 0; k < curr.length; ++k) {
+ for (int j = passes - 1; j >= 0; --j) {
+ out[idx++] = (curr[k] >>> (bitDepth * j)) & mask;
+ }
+ }
+ return out;
+ }
+ }
+ }
+
+ private static void decodeSubFilter(byte[] curr, int count, int bpp) {
+ for (int i = bpp; i < count; i++) {
+ int val;
+
+ val = curr[i] & 0xff;
+ val += curr[i - bpp] & 0xff;
+
+ curr[i] = (byte)val;
+ }
+ }
+
+ private static void decodeUpFilter(byte[] curr, byte[] prev,
+ int count) {
+ for (int i = 0; i < count; i++) {
+ int raw = curr[i] & 0xff;
+ int prior = prev[i] & 0xff;
+
+ curr[i] = (byte)(raw + prior);
+ }
+ }
+
+ private static void decodeAverageFilter(byte[] curr, byte[] prev,
+ int count, int bpp) {
+ int raw, priorPixel, priorRow;
+
+ for (int i = 0; i < bpp; i++) {
+ raw = curr[i] & 0xff;
+ priorRow = prev[i] & 0xff;
+
+ curr[i] = (byte)(raw + priorRow/2);
+ }
+
+ for (int i = bpp; i < count; i++) {
+ raw = curr[i] & 0xff;
+ priorPixel = curr[i - bpp] & 0xff;
+ priorRow = prev[i] & 0xff;
+
+ curr[i] = (byte)(raw + (priorPixel + priorRow)/2);
+ }
+ }
+
+ private static int paethPredictor(int a, int b, int c) {
+ int p = a + b - c;
+ int pa = Math.abs(p - a);
+ int pb = Math.abs(p - b);
+ int pc = Math.abs(p - c);
+
+ if ((pa <= pb) && (pa <= pc)) {
+ return a;
+ } else if (pb <= pc) {
+ return b;
+ } else {
+ return c;
+ }
+ }
+
+ private static void decodePaethFilter(byte[] curr, byte[] prev,
+ int count, int bpp) {
+ int raw, priorPixel, priorRow, priorRowPixel;
+
+ for (int i = 0; i < bpp; i++) {
+ raw = curr[i] & 0xff;
+ priorRow = prev[i] & 0xff;
+
+ curr[i] = (byte)(raw + priorRow);
+ }
+
+ for (int i = bpp; i < count; i++) {
+ raw = curr[i] & 0xff;
+ priorPixel = curr[i - bpp] & 0xff;
+ priorRow = prev[i] & 0xff;
+ priorRowPixel = prev[i - bpp] & 0xff;
+
+ curr[i] = (byte)(raw + paethPredictor(priorPixel,
+ priorRow,
+ priorRowPixel));
+ }
+ }
+
+ static class NewByteArrayOutputStream extends ByteArrayOutputStream {
+ public byte[] getBuf() {
+ return buf;
+ }
+ }
+
+/**
+ * Gets an A TIFF IFD consists of a set of TIFFField tags. Methods are
+ * provided to query the set of tags and to obtain the raw field
+ * array. In addition, convenience methods are provided for acquiring
+ * the values of tags that contain a single value that fits into a
+ * byte, int, long, float, or double.
+ *
+ * Every TIFF file is made up of one or more public IFDs that are
+ * joined in a linked list, rooted in the file header. A file may
+ * also contain so-called private IFDs that are referenced from
+ * tag data and do not appear in the main list.
+ *
+ * This class is not a committed part of the JAI API. It may
+ * be removed or changed in future releases of JAI.
+ *
+ * @see TIFFField
+ */
+public class TIFFDirectory extends Object implements Serializable {
+
+ /** A boolean storing the endianness of the stream. */
+ boolean isBigEndian;
+
+ /** The number of entries in the IFD. */
+ int numEntries;
+
+ /** An array of TIFFFields. */
+ TIFFField[] fields;
+
+ /** A Hashtable indexing the fields by tag number. */
+ Hashtable fieldIndex = new Hashtable();
+
+ /** The offset of this IFD. */
+ long IFDOffset = 8;
+
+ /** The offset of the next IFD. */
+ long nextIFDOffset = 0;
+
+ /** The default constructor. */
+ TIFFDirectory() {}
+
+ private static boolean isValidEndianTag(int endian) {
+ return ((endian == 0x4949) || (endian == 0x4d4d));
+ }
+
+ /**
+ * Constructs a TIFFDirectory from a SeekableStream.
+ * The directory parameter specifies which directory to read from
+ * the linked list present in the stream; directory 0 is normally
+ * read but it is possible to store multiple images in a single
+ * TIFF file by maintaing multiple directories.
+ *
+ * @param stream a SeekableStream to read from.
+ * @param directory the index of the directory to read.
+ */
+ public TIFFDirectory(RandomAccessFileOrArray stream, int directory)
+ throws IOException {
+
+ long global_save_offset = stream.getFilePointer();
+ long ifd_offset;
+
+ // Read the TIFF header
+ stream.seek(0L);
+ int endian = stream.readUnsignedShort();
+ if (!isValidEndianTag(endian)) {
+ throw new
+ IllegalArgumentException("Bad endianness tag (not 0x4949 or 0x4d4d).");
+ }
+ isBigEndian = (endian == 0x4d4d);
+
+ int magic = readUnsignedShort(stream);
+ if (magic != 42) {
+ throw new
+ IllegalArgumentException("Bad magic number, should be 42.");
+ }
+
+ // Get the initial ifd offset as an unsigned int (using a long)
+ ifd_offset = readUnsignedInt(stream);
+
+ for (int i = 0; i < directory; i++) {
+ if (ifd_offset == 0L) {
+ throw new
+ IllegalArgumentException("Directory number too large.");
+ }
+
+ stream.seek(ifd_offset);
+ int entries = readUnsignedShort(stream);
+ stream.skip(12*entries);
+
+ ifd_offset = readUnsignedInt(stream);
+ }
+
+ stream.seek(ifd_offset);
+ initialize(stream);
+ stream.seek(global_save_offset);
+ }
+
+ /**
+ * Constructs a TIFFDirectory by reading a SeekableStream.
+ * The ifd_offset parameter specifies the stream offset from which
+ * to begin reading; this mechanism is sometimes used to store
+ * private IFDs within a TIFF file that are not part of the normal
+ * sequence of IFDs.
+ *
+ * @param stream a SeekableStream to read from.
+ * @param ifd_offset the long byte offset of the directory.
+ * @param directory the index of the directory to read beyond the
+ * one at the current stream offset; zero indicates the IFD
+ * at the current offset.
+ */
+ public TIFFDirectory(RandomAccessFileOrArray stream, long ifd_offset, int directory)
+ throws IOException {
+
+ long global_save_offset = stream.getFilePointer();
+ stream.seek(0L);
+ int endian = stream.readUnsignedShort();
+ if (!isValidEndianTag(endian)) {
+ throw new
+ IllegalArgumentException("Bad endianness tag (not 0x4949 or 0x4d4d).");
+ }
+ isBigEndian = (endian == 0x4d4d);
+
+ // Seek to the first IFD.
+ stream.seek(ifd_offset);
+
+ // Seek to desired IFD if necessary.
+ int dirNum = 0;
+ while(dirNum < directory) {
+ // Get the number of fields in the current IFD.
+ int numEntries = readUnsignedShort(stream);
+
+ // Skip to the next IFD offset value field.
+ stream.seek(ifd_offset + 12*numEntries);
+
+ // Read the offset to the next IFD beyond this one.
+ ifd_offset = readUnsignedInt(stream);
+
+ // Seek to the next IFD.
+ stream.seek(ifd_offset);
+
+ // Increment the directory.
+ dirNum++;
+ }
+
+ initialize(stream);
+ stream.seek(global_save_offset);
+ }
+
+ private static final int[] sizeOfType = {
+ 0, // 0 = n/a
+ 1, // 1 = byte
+ 1, // 2 = ascii
+ 2, // 3 = short
+ 4, // 4 = long
+ 8, // 5 = rational
+ 1, // 6 = sbyte
+ 1, // 7 = undefined
+ 2, // 8 = sshort
+ 4, // 9 = slong
+ 8, // 10 = srational
+ 4, // 11 = float
+ 8 // 12 = double
+ };
+
+ private void initialize(RandomAccessFileOrArray stream) throws IOException {
+ long nextTagOffset = 0L;
+ long maxOffset = stream.length();
+ int i, j;
+
+ IFDOffset = stream.getFilePointer();
+
+ numEntries = readUnsignedShort(stream);
+ fields = new TIFFField[numEntries];
+
+ for (i = 0; (i < numEntries) && (nextTagOffset < maxOffset); i++) {
+ int tag = readUnsignedShort(stream);
+ int type = readUnsignedShort(stream);
+ int count = (int)(readUnsignedInt(stream));
+ boolean processTag = true;
+
+ // The place to return to to read the next tag
+ nextTagOffset = stream.getFilePointer() + 4;
+
+ try {
+ // If the tag data can't fit in 4 bytes, the next 4 bytes
+ // contain the starting offset of the data
+ if (count*sizeOfType[type] > 4) {
+ long valueOffset = readUnsignedInt(stream);
+
+ // bounds check offset for EOF
+ if (valueOffset < maxOffset) {
+ stream.seek(valueOffset);
+ }
+ else {
+ // bad offset pointer .. skip tag
+ processTag = false;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException ae) {
+ // if the data type is unknown we should skip this TIFF Field
+ processTag = false;
+ }
+
+ if (processTag) {
+ fieldIndex.put(new Integer(tag), new Integer(i));
+ Object obj = null;
+
+ switch (type) {
+ case TIFFField.TIFF_BYTE:
+ case TIFFField.TIFF_SBYTE:
+ case TIFFField.TIFF_UNDEFINED:
+ case TIFFField.TIFF_ASCII:
+ byte[] bvalues = new byte[count];
+ stream.readFully(bvalues, 0, count);
+
+ if (type == TIFFField.TIFF_ASCII) {
+
+ // Can be multiple strings
+ int index = 0, prevIndex = 0;
+ ArrayList v = new ArrayList();
+
+ while (index < count) {
+
+ while ((index < count) && (bvalues[index++] != 0));
+
+ // When we encountered zero, means one string has ended
+ v.add(new String(bvalues, prevIndex,
+ (index - prevIndex)) );
+ prevIndex = index;
+ }
+
+ count = v.size();
+ String strings[] = new String[count];
+ for (int c = 0 ; c < count; c++) {
+ strings[c] = (String)v.get(c);
+ }
+
+ obj = strings;
+ } else {
+ obj = bvalues;
+ }
+
+ break;
+
+ case TIFFField.TIFF_SHORT:
+ char[] cvalues = new char[count];
+ for (j = 0; j < count; j++) {
+ cvalues[j] = (char)(readUnsignedShort(stream));
+ }
+ obj = cvalues;
+ break;
+
+ case TIFFField.TIFF_LONG:
+ long[] lvalues = new long[count];
+ for (j = 0; j < count; j++) {
+ lvalues[j] = readUnsignedInt(stream);
+ }
+ obj = lvalues;
+ break;
+
+ case TIFFField.TIFF_RATIONAL:
+ long[][] llvalues = new long[count][2];
+ for (j = 0; j < count; j++) {
+ llvalues[j][0] = readUnsignedInt(stream);
+ llvalues[j][1] = readUnsignedInt(stream);
+ }
+ obj = llvalues;
+ break;
+
+ case TIFFField.TIFF_SSHORT:
+ short[] svalues = new short[count];
+ for (j = 0; j < count; j++) {
+ svalues[j] = readShort(stream);
+ }
+ obj = svalues;
+ break;
+
+ case TIFFField.TIFF_SLONG:
+ int[] ivalues = new int[count];
+ for (j = 0; j < count; j++) {
+ ivalues[j] = readInt(stream);
+ }
+ obj = ivalues;
+ break;
+
+ case TIFFField.TIFF_SRATIONAL:
+ int[][] iivalues = new int[count][2];
+ for (j = 0; j < count; j++) {
+ iivalues[j][0] = readInt(stream);
+ iivalues[j][1] = readInt(stream);
+ }
+ obj = iivalues;
+ break;
+
+ case TIFFField.TIFF_FLOAT:
+ float[] fvalues = new float[count];
+ for (j = 0; j < count; j++) {
+ fvalues[j] = readFloat(stream);
+ }
+ obj = fvalues;
+ break;
+
+ case TIFFField.TIFF_DOUBLE:
+ double[] dvalues = new double[count];
+ for (j = 0; j < count; j++) {
+ dvalues[j] = readDouble(stream);
+ }
+ obj = dvalues;
+ break;
+
+ default:
+ break;
+ }
+
+ fields[i] = new TIFFField(tag, type, count, obj);
+ }
+
+ stream.seek(nextTagOffset);
+ }
+
+ // Read the offset of the next IFD.
+ nextIFDOffset = readUnsignedInt(stream);
+ }
+
+ /** Returns the number of directory entries. */
+ public int getNumEntries() {
+ return numEntries;
+ }
+
+ /**
+ * Returns the value of a given tag as a TIFFField,
+ * or null if the tag is not present.
+ */
+ public TIFFField getField(int tag) {
+ Integer i = (Integer)fieldIndex.get(new Integer(tag));
+ if (i == null) {
+ return null;
+ } else {
+ return fields[i.intValue()];
+ }
+ }
+
+ /**
+ * Returns true if a tag appears in the directory.
+ */
+ public boolean isTagPresent(int tag) {
+ return fieldIndex.containsKey(new Integer(tag));
+ }
+
+ /**
+ * Returns an ordered array of ints indicating the tag
+ * values.
+ */
+ public int[] getTags() {
+ int[] tags = new int[fieldIndex.size()];
+ Enumeration e = fieldIndex.keys();
+ int i = 0;
+
+ while (e.hasMoreElements()) {
+ tags[i++] = ((Integer)e.nextElement()).intValue();
+ }
+
+ return tags;
+ }
+
+ /**
+ * Returns an array of TIFFFields containing all the fields
+ * in this directory.
+ */
+ public TIFFField[] getFields() {
+ return fields;
+ }
+
+ /**
+ * Returns the value of a particular index of a given tag as a
+ * byte. The caller is responsible for ensuring that the tag is
+ * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or
+ * TIFF_UNDEFINED.
+ */
+ public byte getFieldAsByte(int tag, int index) {
+ Integer i = (Integer)fieldIndex.get(new Integer(tag));
+ byte [] b = (fields[i.intValue()]).getAsBytes();
+ return b[index];
+ }
+
+ /**
+ * Returns the value of index 0 of a given tag as a
+ * byte. The caller is responsible for ensuring that the tag is
+ * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or
+ * TIFF_UNDEFINED.
+ */
+ public byte getFieldAsByte(int tag) {
+ return getFieldAsByte(tag, 0);
+ }
+
+ /**
+ * Returns the value of a particular index of a given tag as a
+ * long. The caller is responsible for ensuring that the tag is
+ * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED,
+ * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG.
+ */
+ public long getFieldAsLong(int tag, int index) {
+ Integer i = (Integer)fieldIndex.get(new Integer(tag));
+ return (fields[i.intValue()]).getAsLong(index);
+ }
+
+ /**
+ * Returns the value of index 0 of a given tag as a
+ * long. The caller is responsible for ensuring that the tag is
+ * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED,
+ * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG.
+ */
+ public long getFieldAsLong(int tag) {
+ return getFieldAsLong(tag, 0);
+ }
+
+ /**
+ * Returns the value of a particular index of a given tag as a
+ * float. The caller is responsible for ensuring that the tag is
+ * present and has numeric type (all but TIFF_UNDEFINED and
+ * TIFF_ASCII).
+ */
+ public float getFieldAsFloat(int tag, int index) {
+ Integer i = (Integer)fieldIndex.get(new Integer(tag));
+ return fields[i.intValue()].getAsFloat(index);
+ }
+
+ /**
+ * Returns the value of index 0 of a given tag as a float. The
+ * caller is responsible for ensuring that the tag is present and
+ * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII).
+ */
+ public float getFieldAsFloat(int tag) {
+ return getFieldAsFloat(tag, 0);
+ }
+
+ /**
+ * Returns the value of a particular index of a given tag as a
+ * double. The caller is responsible for ensuring that the tag is
+ * present and has numeric type (all but TIFF_UNDEFINED and
+ * TIFF_ASCII).
+ */
+ public double getFieldAsDouble(int tag, int index) {
+ Integer i = (Integer)fieldIndex.get(new Integer(tag));
+ return fields[i.intValue()].getAsDouble(index);
+ }
+
+ /**
+ * Returns the value of index 0 of a given tag as a double. The
+ * caller is responsible for ensuring that the tag is present and
+ * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII).
+ */
+ public double getFieldAsDouble(int tag) {
+ return getFieldAsDouble(tag, 0);
+ }
+
+ // Methods to read primitive data types from the stream
+
+ private short readShort(RandomAccessFileOrArray stream)
+ throws IOException {
+ if (isBigEndian) {
+ return stream.readShort();
+ } else {
+ return stream.readShortLE();
+ }
+ }
+
+ private int readUnsignedShort(RandomAccessFileOrArray stream)
+ throws IOException {
+ if (isBigEndian) {
+ return stream.readUnsignedShort();
+ } else {
+ return stream.readUnsignedShortLE();
+ }
+ }
+
+ private int readInt(RandomAccessFileOrArray stream)
+ throws IOException {
+ if (isBigEndian) {
+ return stream.readInt();
+ } else {
+ return stream.readIntLE();
+ }
+ }
+
+ private long readUnsignedInt(RandomAccessFileOrArray stream)
+ throws IOException {
+ if (isBigEndian) {
+ return stream.readUnsignedInt();
+ } else {
+ return stream.readUnsignedIntLE();
+ }
+ }
+
+ private long readLong(RandomAccessFileOrArray stream)
+ throws IOException {
+ if (isBigEndian) {
+ return stream.readLong();
+ } else {
+ return stream.readLongLE();
+ }
+ }
+
+ private float readFloat(RandomAccessFileOrArray stream)
+ throws IOException {
+ if (isBigEndian) {
+ return stream.readFloat();
+ } else {
+ return stream.readFloatLE();
+ }
+ }
+
+ private double readDouble(RandomAccessFileOrArray stream)
+ throws IOException {
+ if (isBigEndian) {
+ return stream.readDouble();
+ } else {
+ return stream.readDoubleLE();
+ }
+ }
+
+ private static int readUnsignedShort(RandomAccessFileOrArray stream,
+ boolean isBigEndian)
+ throws IOException {
+ if (isBigEndian) {
+ return stream.readUnsignedShort();
+ } else {
+ return stream.readUnsignedShortLE();
+ }
+ }
+
+ private static long readUnsignedInt(RandomAccessFileOrArray stream,
+ boolean isBigEndian)
+ throws IOException {
+ if (isBigEndian) {
+ return stream.readUnsignedInt();
+ } else {
+ return stream.readUnsignedIntLE();
+ }
+ }
+
+ // Utilities
+
+ /**
+ * Returns the number of image directories (subimages) stored in a
+ * given TIFF file, represented by a The TIFF file format is described in more detail in the
+ * comments for the TIFFDescriptor class.
+ *
+ * A field in a TIFF Image File Directory (IFD). A field is defined
+ * as a sequence of values of identical data type. TIFF 6.0 defines
+ * 12 data types, which are mapped internally onto the Java datatypes
+ * byte, int, long, float, and double.
+ *
+ * This class is not a committed part of the JAI API. It may
+ * be removed or changed in future releases of JAI.
+ *
+ * @see TIFFDirectory
+ */
+public class TIFFField extends Object implements Comparable, Serializable {
+
+ /** Flag for 8 bit unsigned integers. */
+ public static final int TIFF_BYTE = 1;
+
+ /** Flag for null-terminated ASCII strings. */
+ public static final int TIFF_ASCII = 2;
+
+ /** Flag for 16 bit unsigned integers. */
+ public static final int TIFF_SHORT = 3;
+
+ /** Flag for 32 bit unsigned integers. */
+ public static final int TIFF_LONG = 4;
+
+ /** Flag for pairs of 32 bit unsigned integers. */
+ public static final int TIFF_RATIONAL = 5;
+
+ /** Flag for 8 bit signed integers. */
+ public static final int TIFF_SBYTE = 6;
+
+ /** Flag for 8 bit uninterpreted bytes. */
+ public static final int TIFF_UNDEFINED = 7;
+
+ /** Flag for 16 bit signed integers. */
+ public static final int TIFF_SSHORT = 8;
+
+ /** Flag for 32 bit signed integers. */
+ public static final int TIFF_SLONG = 9;
+
+ /** Flag for pairs of 32 bit signed integers. */
+ public static final int TIFF_SRATIONAL = 10;
+
+ /** Flag for 32 bit IEEE floats. */
+ public static final int TIFF_FLOAT = 11;
+
+ /** Flag for 64 bit IEEE doubles. */
+ public static final int TIFF_DOUBLE = 12;
+
+ /** The tag number. */
+ int tag;
+
+ /** The tag type. */
+ int type;
+
+ /** The number of data items present in the field. */
+ int count;
+
+ /** The field data. */
+ Object data;
+
+ /** The default constructor. */
+ TIFFField() {}
+
+ /**
+ * Constructs a TIFFField with arbitrary data. The data
+ * parameter must be an array of a Java type appropriate for the
+ * type of the TIFF field. Since there is no available 32-bit
+ * unsigned datatype, long is used. The mapping between types is
+ * as follows:
+ *
+ * For data in TIFF_BYTE format, the application must take
+ * care when promoting the data to longer integral types
+ * to avoid sign extension.
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_BYTE, TIFF_SBYTE, or TIFF_UNDEFINED.
+ */
+ public byte[] getAsBytes() {
+ return (byte[])data;
+ }
+
+ /**
+ * Returns TIFF_SHORT data as an array of chars (unsigned 16-bit
+ * integers).
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_SHORT.
+ */
+ public char[] getAsChars() {
+ return (char[])data;
+ }
+
+ /**
+ * Returns TIFF_SSHORT data as an array of shorts (signed 16-bit
+ * integers).
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_SSHORT.
+ */
+ public short[] getAsShorts() {
+ return (short[])data;
+ }
+
+ /**
+ * Returns TIFF_SLONG data as an array of ints (signed 32-bit
+ * integers).
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_SLONG.
+ */
+ public int[] getAsInts() {
+ return (int[])data;
+ }
+
+ /**
+ * Returns TIFF_LONG data as an array of longs (signed 64-bit
+ * integers).
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_LONG.
+ */
+ public long[] getAsLongs() {
+ return (long[])data;
+ }
+
+ /**
+ * Returns TIFF_FLOAT data as an array of floats.
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_FLOAT.
+ */
+ public float[] getAsFloats() {
+ return (float[])data;
+ }
+
+ /**
+ * Returns TIFF_DOUBLE data as an array of doubles.
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_DOUBLE.
+ */
+ public double[] getAsDoubles() {
+ return (double[])data;
+ }
+
+ /**
+ * Returns TIFF_SRATIONAL data as an array of 2-element arrays of ints.
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_SRATIONAL.
+ */
+ public int[][] getAsSRationals() {
+ return (int[][])data;
+ }
+
+ /**
+ * Returns TIFF_RATIONAL data as an array of 2-element arrays of longs.
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_RATTIONAL.
+ */
+ public long[][] getAsRationals() {
+ return (long[][])data;
+ }
+
+ /**
+ * Returns data in TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT,
+ * TIFF_SSHORT, or TIFF_SLONG format as an int.
+ *
+ * TIFF_BYTE and TIFF_UNDEFINED data are treated as unsigned;
+ * that is, no sign extension will take place and the returned
+ * value will be in the range [0, 255]. TIFF_SBYTE data will
+ * be returned in the range [-128, 127].
+ *
+ * A ClassCastException will be thrown if the field is not of
+ * type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT,
+ * TIFF_SSHORT, or TIFF_SLONG.
+ */
+ public int getAsInt(int index) {
+ switch (type) {
+ case TIFF_BYTE: case TIFF_UNDEFINED:
+ return ((byte[])data)[index] & 0xff;
+ case TIFF_SBYTE:
+ return ((byte[])data)[index];
+ case TIFF_SHORT:
+ return ((char[])data)[index] & 0xffff;
+ case TIFF_SSHORT:
+ return ((short[])data)[index];
+ case TIFF_SLONG:
+ return ((int[])data)[index];
+ default:
+ throw new ClassCastException();
+ }
+ }
+
+ /**
+ * Returns data in TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT,
+ * TIFF_SSHORT, TIFF_SLONG, or TIFF_LONG format as a long.
+ *
+ * TIFF_BYTE and TIFF_UNDEFINED data are treated as unsigned;
+ * that is, no sign extension will take place and the returned
+ * value will be in the range [0, 255]. TIFF_SBYTE data will
+ * be returned in the range [-128, 127].
+ *
+ * A ClassCastException will be thrown if the field is not of
+ * type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT,
+ * TIFF_SSHORT, TIFF_SLONG, or TIFF_LONG.
+ */
+ public long getAsLong(int index) {
+ switch (type) {
+ case TIFF_BYTE: case TIFF_UNDEFINED:
+ return ((byte[])data)[index] & 0xff;
+ case TIFF_SBYTE:
+ return ((byte[])data)[index];
+ case TIFF_SHORT:
+ return ((char[])data)[index] & 0xffff;
+ case TIFF_SSHORT:
+ return ((short[])data)[index];
+ case TIFF_SLONG:
+ return ((int[])data)[index];
+ case TIFF_LONG:
+ return ((long[])data)[index];
+ default:
+ throw new ClassCastException();
+ }
+ }
+
+ /**
+ * Returns data in any numerical format as a float. Data in
+ * TIFF_SRATIONAL or TIFF_RATIONAL format are evaluated by
+ * dividing the numerator into the denominator using
+ * double-precision arithmetic and then truncating to single
+ * precision. Data in TIFF_SLONG, TIFF_LONG, or TIFF_DOUBLE
+ * format may suffer from truncation.
+ *
+ * A ClassCastException will be thrown if the field is
+ * of type TIFF_UNDEFINED or TIFF_ASCII.
+ */
+ public float getAsFloat(int index) {
+ switch (type) {
+ case TIFF_BYTE:
+ return ((byte[])data)[index] & 0xff;
+ case TIFF_SBYTE:
+ return ((byte[])data)[index];
+ case TIFF_SHORT:
+ return ((char[])data)[index] & 0xffff;
+ case TIFF_SSHORT:
+ return ((short[])data)[index];
+ case TIFF_SLONG:
+ return ((int[])data)[index];
+ case TIFF_LONG:
+ return ((long[])data)[index];
+ case TIFF_FLOAT:
+ return ((float[])data)[index];
+ case TIFF_DOUBLE:
+ return (float)((double[])data)[index];
+ case TIFF_SRATIONAL:
+ int[] ivalue = getAsSRational(index);
+ return (float)((double)ivalue[0]/ivalue[1]);
+ case TIFF_RATIONAL:
+ long[] lvalue = getAsRational(index);
+ return (float)((double)lvalue[0]/lvalue[1]);
+ default:
+ throw new ClassCastException();
+ }
+ }
+
+ /**
+ * Returns data in any numerical format as a float. Data in
+ * TIFF_SRATIONAL or TIFF_RATIONAL format are evaluated by
+ * dividing the numerator into the denominator using
+ * double-precision arithmetic.
+ *
+ * A ClassCastException will be thrown if the field is of
+ * type TIFF_UNDEFINED or TIFF_ASCII.
+ */
+ public double getAsDouble(int index) {
+ switch (type) {
+ case TIFF_BYTE:
+ return ((byte[])data)[index] & 0xff;
+ case TIFF_SBYTE:
+ return ((byte[])data)[index];
+ case TIFF_SHORT:
+ return ((char[])data)[index] & 0xffff;
+ case TIFF_SSHORT:
+ return ((short[])data)[index];
+ case TIFF_SLONG:
+ return ((int[])data)[index];
+ case TIFF_LONG:
+ return ((long[])data)[index];
+ case TIFF_FLOAT:
+ return ((float[])data)[index];
+ case TIFF_DOUBLE:
+ return ((double[])data)[index];
+ case TIFF_SRATIONAL:
+ int[] ivalue = getAsSRational(index);
+ return (double)ivalue[0]/ivalue[1];
+ case TIFF_RATIONAL:
+ long[] lvalue = getAsRational(index);
+ return (double)lvalue[0]/lvalue[1];
+ default:
+ throw new ClassCastException();
+ }
+ }
+
+ /**
+ * Returns a TIFF_ASCII data item as a String.
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_ASCII.
+ */
+ public String getAsString(int index) {
+ return ((String[])data)[index];
+ }
+
+ /**
+ * Returns a TIFF_SRATIONAL data item as a two-element array
+ * of ints.
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_SRATIONAL.
+ */
+ public int[] getAsSRational(int index) {
+ return ((int[][])data)[index];
+ }
+
+ /**
+ * Returns a TIFF_RATIONAL data item as a two-element array
+ * of ints.
+ *
+ * A ClassCastException will be thrown if the field is not
+ * of type TIFF_RATIONAL.
+ */
+ public long[] getAsRational(int index) {
+ return ((long[][])data)[index];
+ }
+
+ /**
+ * Compares this Note: this class has a natural ordering that is inconsistent
+ * with Title: Description: Copyright: Copyright (c) 2006 Company:
+ * In the first step, only in1, in2,in3 and tag are used.
+ * After the collections of the index entries, pagenumbers are used.
+ *
+ * Note that if even if a page is not written this method is still called.
+ * It is preferable to use
+ * Note that this method is called with the page number equal to the last
+ * page plus one.
+ *
+ * @param writer
+ * the
+ *
+ *
+ *
+ *
+ *
+ *
+ * It is usefull to pinpoint the Search for all possible partial matches of word starting
+ * at index an update interletter values. In other words, it
+ * does something like: But it is done in an efficient way since the patterns are
+ * stored in a ternary tree. In fact, this is the whole purpose
+ * of having the tree: doing this search without having to test
+ * every single pattern. The number of patterns for languages
+ * such as English range from 4000 to 10000. Thus, doing thousands
+ * of string comparisons for each word to hyphenate would be
+ * really slow without the tree. The tradeoff is memory, but
+ * using a ternary tree instead of a trie, almost halves the
+ * the memory used by Lout or TeX. It's also faster than using
+ * a hash table A ternary search tree is a hibrid between a binary tree and
+ * a digital search tree (trie). Keys are limited to strings.
+ * A data value of type char is stored in each leaf node.
+ * It can be used as an index (or pointer) to the data.
+ * Branches that only contain one key are compressed to one node
+ * by storing a pointer to the trailer substring of the key.
+ * This class is intended to serve as base class or helper class
+ * to implement Dictionary collections or the like. Ternary trees
+ * have some nice properties as the following: the tree can be
+ * traversed in sorted order, partial matches (wildcard) can be
+ * implemented, retrieval of all keys within a given distance
+ * from the target, etc. The storage requirements are higher than
+ * a binary tree but a lot less than a trie. Performance is
+ * comparable with a hash table, sometimes it outperforms a hash
+ * function (most of the time can determine a miss faster than a hash). The main purpose of this java port is to serve as a base for
+ * implementing TeX's hyphenation algorithm (see The TeXBook,
+ * appendix H). Each language requires from 5000 to 15000 hyphenation
+ * patterns which will be keys in this tree. The strings patterns
+ * are usually small (from 2 to 5 characters), but each char in the
+ * tree is stored in a node. Thus memory usage is the main concern.
+ * We will sacrify 'elegance' to keep memory requirenments to the
+ * minimum. Using java's char type as pointer (yes, I know pointer
+ * it is a forbidden word in java) we can keep the size of the node
+ * to be just 8 bytes (3 pointers and the data char). This gives
+ * room for about 65000 nodes. In my tests the english patterns
+ * took 7694 nodes and the german patterns 10055 nodes,
+ * so I think we are safe. All said, this is a map with strings as keys and char as value.
+ * Pretty limited!. It can be extended to a general map by
+ * using the string representation of an object and using the
+ * char value as an index to an array that contains the object
+ * values. The character stored in this node: splitchar.
+ * Two special values are reserved: This shouldn't be a problem if we give the usual semantics to
+ * strings since 0xFFFF is garanteed not to be an Unicode character.
+ * Do not use it directly
+ *
+ * ONLY FOR USE WITH THE RtfWriter NOT with the RtfWriter2.
+ *
+ * Parts of this Class were contributed by Steffen Stundzig. Many thanks for the
+ * improvements.
+ * Updates by Benoit WIART
+ * @param cell The
+ * Do not use it directly
+ *
+ * ONLY FOR USE WITH THE RtfWriter NOT with the RtfWriter2.
+ *
+ * Parts of this Class were contributed by Steffen Stundzig. Many thanks for the
+ * improvements.
+ * Code added by c
+ * @deprecated Please move to the RtfWriter2 and associated classes.
+ */
+public class RtfRow {
+ /** Table border solid */
+ public static final byte[] tableBorder = "brdrs".getBytes();
+ /** Table border width */
+ public static final byte[] tableBorderWidth = "brdrw".getBytes();
+ /** Table border color */
+ public static final byte[] tableBorderColor = "brdrcf".getBytes();
+
+ /** Table row defaults */
+ private static final byte[] rowBegin = "trowd".getBytes();
+ /** End of table row */
+ private static final byte[] rowEnd = "row".getBytes();
+ /** Table row autofit */
+ private static final byte[] rowAutofit = "trautofit1".getBytes();
+ private static final byte[] graphLeft = "trgaph".getBytes();
+ /** Row border left */
+ private static final byte[] rowBorderLeft = "trbrdrl".getBytes();
+ /** Row border right */
+ private static final byte[] rowBorderRight = "trbrdrr".getBytes();
+ /** Row border top */
+ private static final byte[] rowBorderTop = "trbrdrt".getBytes();
+ /** Row border bottom */
+ private static final byte[] rowBorderBottom = "trbrdrb".getBytes();
+ /** Row border horiz inline */
+ private static final byte[] rowBorderInlineHorizontal = "trbrdrh".getBytes();
+ /** Row border bottom */
+ private static final byte[] rowBorderInlineVertical = "trbrdrv".getBytes();
+ /** Default cell spacing left */
+ private static final byte[] rowSpacingLeft = "trspdl".getBytes();
+ /** Default cell spacing right */
+ private static final byte[] rowSpacingRight = "trspdr".getBytes();
+ /** Default cell spacing top */
+ private static final byte[] rowSpacingTop = "trspdt".getBytes();
+ /** Default cell spacing bottom */
+ private static final byte[] rowSpacingBottom = "trspdb".getBytes();
+ /** Default cell spacing format left */
+ private static final byte[] rowSpacingLeftStyle = "trspdfl3".getBytes();
+ /** Default cell spacing format right */
+ private static final byte[] rowSpacingRightStyle = "trspdfr3".getBytes();
+ /** Default cell spacing format top */
+ private static final byte[] rowSpacingTopStyle = "trspdft3".getBytes();
+ /** Default cell spacing format bottom */
+ private static final byte[] rowSpacingBottomStyle = "trspdfb3".getBytes();
+ /** Default cell padding left */
+ private static final byte[] rowPaddingLeft = "trpaddl".getBytes();
+ /** Default cell padding right */
+ private static final byte[] rowPaddingRight = "trpaddr".getBytes();
+ /** Default cell padding format left */
+ private static final byte[] rowPaddingLeftStyle = "trpaddfl3".getBytes();
+ /** Default cell padding format right */
+ private static final byte[] rowPaddingRightStyle = "trpaddfr3".getBytes();
+ /** Row width format */
+ private static final byte[] rowWidthStyle = "trftsWidth3".getBytes();
+ /** Row width */
+ private static final byte[] rowWidth = "trwWidth".getBytes();
+ /**
+ * Table row header. This row should appear at the top of every
+ * page the current table appears on.
+ */
+ private static final byte[] rowHeader = "trhdr".getBytes();
+ /**
+ * Table row keep together. This row cannot be split by a page break.
+ * This property is assumed to be off unless the control word is
+ * present.
+ */
+ private static final byte[] rowKeep = "trkeep".getBytes();
+ /** Table alignment left */
+ private static final byte[] rowAlignLeft = "trql".getBytes();
+ /** Table alignment center */
+ private static final byte[] rowAlignCenter = "trqc".getBytes();
+ /** Table alignment right */
+ private static final byte[] rowAlignRight = "trqr".getBytes();
+
+ /** List of
+ * All the parameters are taken from the
+ * Do not use it directly, except if you want to write a
+ * @param table A
+ * if the element is a
+ * The
+ * A
+ * Example:
+ *
+ * LIMITATIONS
+ * The content of the font table, color table, information group, content, header, footer are merged into the final
+ *
+ * If you set this parameter to true (= default), the parser will open the
+ * Document object when the start-root-tag is encounterd and close it when
+ * the end-root-tag is met. If you set it to false, you have to open and
+ * close the Document object yourself.
+ *
+ * @param controlOpenClose
+ * set this to false if you plan to open/close the Document
+ * yourself
+ */
+
+ public void setControlOpenClose(boolean controlOpenClose) {
+ this.controlOpenClose = controlOpenClose;
+ }
+
+ /**
+ * This method gets called when a start tag is encountered.
+ *
+ * @param uri
+ * the Uniform Resource Identifier
+ * @param lname
+ * the local name (without prefix), or the empty string if
+ * Namespace processing is not being performed.
+ * @param name
+ * the name of the tag that is encountered
+ * @param attrs
+ * the list of attributes
+ */
+
+ public void startElement(String uri, String lname, String name,
+ Attributes attrs) {
+
+ Properties attributes = new Properties();
+ if (attrs != null) {
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String attribute = attrs.getQName(i);
+ attributes.setProperty(attribute, attrs.getValue(i));
+ }
+ }
+ handleStartingTags(name, attributes);
+ }
+
+ /**
+ * This method deals with the starting tags.
+ *
+ * @param name
+ * the name of the tag
+ * @param attributes
+ * the list of attributes
+ */
+
+ public void handleStartingTags(String name, Properties attributes) {
+ // System.err.println("Start: " + name);
+ if (ignore || ElementTags.IGNORE.equals(name)) {
+ ignore = true;
+ return;
+ }
+
+ // maybe there is some meaningful data that wasn't between tags
+ if (currentChunk != null) {
+ TextElementArray current;
+ try {
+ current = (TextElementArray) stack.pop();
+ } catch (EmptyStackException ese) {
+ if (bf == null) {
+ current = new Paragraph("", new Font());
+ }
+ else {
+ current = new Paragraph("", new Font(this.bf));
+ }
+ }
+ current.add(currentChunk);
+ stack.push(current);
+ currentChunk = null;
+ }
+
+ // chunks
+ if (Chunk.isTag(name)) {
+ currentChunk = new Chunk(attributes);
+ if (bf != null) {
+ currentChunk.setFont(new Font(this.bf));
+ }
+ return;
+ }
+
+ // symbols
+ if (Entities.isTag(name)) {
+ Font f = new Font();
+ if (currentChunk != null) {
+ handleEndingTags(ElementTags.CHUNK);
+ f = currentChunk.font();
+ }
+ currentChunk = Entities.get(attributes.getProperty(ElementTags.ID),
+ f);
+ return;
+ }
+
+ // phrases
+ if (Phrase.isTag(name)) {
+ stack.push(new Phrase(attributes));
+ return;
+ }
+
+ // anchors
+ if (Anchor.isTag(name)) {
+ stack.push(new Anchor(attributes));
+ return;
+ }
+
+ // paragraphs and titles
+ if (Paragraph.isTag(name) || Section.isTitle(name)) {
+ stack.push(new Paragraph(attributes));
+ return;
+ }
+
+ // lists
+ if (List.isTag(name)) {
+ stack.push(new List(attributes));
+ return;
+ }
+
+ // listitems
+ if (ListItem.isTag(name)) {
+ stack.push(new ListItem(attributes));
+ return;
+ }
+
+ // cells
+ if (Cell.isTag(name)) {
+ stack.push(new Cell(attributes));
+ return;
+ }
+
+ // tables
+ if (Table.isTag(name)) {
+ Table table = new Table(attributes);
+ float widths[] = table.getProportionalWidths();
+ for (int i = 0; i < widths.length; i++) {
+ if (widths[i] == 0) {
+ widths[i] = 100.0f / (float) widths.length;
+ }
+ }
+ try {
+ table.setWidths(widths);
+ } catch (BadElementException bee) {
+ // this shouldn't happen
+ throw new ExceptionConverter(bee);
+ }
+ stack.push(table);
+ return;
+ }
+
+ // sections
+ if (Section.isTag(name)) {
+ Element previous = (Element) stack.pop();
+ Section section;
+ try {
+ section = ((Section) previous).addSection(attributes);
+ } catch (ClassCastException cce) {
+ throw new ExceptionConverter(cce);
+ }
+ stack.push(previous);
+ stack.push(section);
+ return;
+ }
+
+ // chapters
+ if (Chapter.isTag(name)) {
+ String value; // changed after a suggestion by Serge S. Vasiljev
+ if ((value = (String) attributes.remove(ElementTags.NUMBER)) != null) {
+ chapters = Integer.parseInt(value);
+ } else {
+ chapters++;
+ }
+ Chapter chapter = new Chapter(attributes, chapters);
+ stack.push(chapter);
+ return;
+ }
+
+ // images
+ if (Image.isTag(name)) {
+ try {
+ Image img = Image.getInstance(attributes);
+ Object current;
+ try {
+ // if there is an element on the stack...
+ current = stack.pop();
+ // ...and it's a Chapter or a Section, the Image can be
+ // added directly
+ if (current instanceof Chapter
+ || current instanceof Section
+ || current instanceof Cell) {
+ ((TextElementArray) current).add(img);
+ stack.push(current);
+ return;
+ }
+ // ...if not, the Image is wrapped in a Chunk before it's
+ // added
+ else {
+ Stack newStack = new Stack();
+ try {
+ while (!(current instanceof Chapter
+ || current instanceof Section || current instanceof Cell)) {
+ newStack.push(current);
+ if (current instanceof Anchor) {
+ img.setAnnotation(new Annotation(0, 0, 0,
+ 0, ((Anchor) current).reference()));
+ }
+ current = stack.pop();
+ }
+ ((TextElementArray) current).add(img);
+ stack.push(current);
+ } catch (EmptyStackException ese) {
+ document.add(img);
+ }
+ while (!newStack.empty()) {
+ stack.push(newStack.pop());
+ }
+ return;
+ }
+ } catch (EmptyStackException ese) {
+ // if there is no element on the stack, the Image is added
+ // to the document
+ try {
+ document.add(img);
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ return;
+ }
+ } catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ // annotations
+ if (Annotation.isTag(name)) {
+ Annotation annotation = new Annotation(attributes);
+ TextElementArray current;
+ try {
+ try {
+ current = (TextElementArray) stack.pop();
+ try {
+ current.add(annotation);
+ } catch (Exception e) {
+ document.add(annotation);
+ }
+ stack.push(current);
+ } catch (EmptyStackException ese) {
+ document.add(annotation);
+ }
+ return;
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ // newlines
+ if (isNewline(name)) {
+ TextElementArray current;
+ try {
+ current = (TextElementArray) stack.pop();
+ current.add(Chunk.NEWLINE);
+ stack.push(current);
+ } catch (EmptyStackException ese) {
+ if (currentChunk == null) {
+ try {
+ document.add(Chunk.NEWLINE);
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ } else {
+ currentChunk.append("\n");
+ }
+ }
+ return;
+ }
+
+ // newpage
+ if (isNewpage(name)) {
+ TextElementArray current;
+ try {
+ current = (TextElementArray) stack.pop();
+ Chunk newPage = new Chunk("");
+ newPage.setNewPage();
+ if (bf != null) {
+ newPage.setFont(new Font(this.bf));
+ }
+ current.add(newPage);
+ stack.push(current);
+ } catch (EmptyStackException ese) {
+ try {
+ document.newPage();
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+ return;
+ }
+
+ // newpage
+ if (ElementTags.HORIZONTALRULE.equals(name)) {
+ TextElementArray current;
+ Graphic hr = new Graphic();
+ hr.setHorizontalLine(1.0f, 100.0f);
+ try {
+ current = (TextElementArray) stack.pop();
+ current.add(hr);
+ stack.push(current);
+ } catch (EmptyStackException ese) {
+ try {
+ document.add(hr);
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+ return;
+ }
+
+ // documentroot
+ if (isDocumentRoot(name)) {
+ String key;
+ String value;
+ // pagesize and orientation specific code suggested by Samuel Gabriel
+ // Updated by Ricardo Coutinho. Only use if set in html!
+ Rectangle pageSize = null;
+ String orientation = null;
+ for (Iterator i = attributes.keySet().iterator(); i.hasNext();) {
+ key = (String) i.next();
+ value = attributes.getProperty(key);
+ try {
+ // margin specific code suggested by Reza Nasiri
+ if (ElementTags.LEFT.equalsIgnoreCase(key))
+ leftMargin = Float.valueOf(value + "f").floatValue();
+ if (ElementTags.RIGHT.equalsIgnoreCase(key))
+ rightMargin = Float.valueOf(value + "f").floatValue();
+ if (ElementTags.TOP.equalsIgnoreCase(key))
+ topMargin = Float.valueOf(value + "f").floatValue();
+ if (ElementTags.BOTTOM.equalsIgnoreCase(key))
+ bottomMargin = Float.valueOf(value + "f").floatValue();
+ } catch (Exception ex) {
+ throw new ExceptionConverter(ex);
+ }
+ if (ElementTags.PAGE_SIZE.equals(key)) {
+ try {
+ String pageSizeName = value;
+ Field pageSizeField = PageSize.class
+ .getField(pageSizeName);
+ pageSize = (Rectangle) pageSizeField.get(null);
+ } catch (Exception ex) {
+ throw new ExceptionConverter(ex);
+ }
+ } else if (ElementTags.ORIENTATION.equals(key)) {
+ try {
+ String pageSizeName = value;
+ if ("landscape".equals(value)) {
+ orientation = "landscape";
+ }
+ } catch (Exception ex) {
+ throw new ExceptionConverter(ex);
+ }
+ } else {
+ try {
+ document.add(new Meta(key, value));
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+ }
+ if(pageSize != null) {
+ if ("landscape".equals(orientation)) {
+ pageSize = pageSize.rotate();
+ }
+ document.setPageSize(pageSize);
+ }
+ document.setMargins(leftMargin, rightMargin, topMargin,
+ bottomMargin);
+
+ if (controlOpenClose)
+ document.open();
+ }
+
+ }
+
+ /**
+ * This method gets called when ignorable white space encountered.
+ *
+ * @param ch
+ * an array of characters
+ * @param start
+ * the start position in the array
+ * @param length
+ * the number of characters to read from the array
+ */
+
+ public void ignorableWhitespace(char[] ch, int start, int length) {
+ // do nothing: we handle white space ourselves in the characters method
+ }
+
+ /**
+ * This method gets called when characters are encountered.
+ *
+ * @param ch
+ * an array of characters
+ * @param start
+ * the start position in the array
+ * @param length
+ * the number of characters to read from the array
+ */
+
+ public void characters(char[] ch, int start, int length) {
+
+ if (ignore)
+ return;
+
+ String content = new String(ch, start, length);
+ // System.err.println("'" + content + "'");
+
+ if (content.trim().length() == 0) {
+ return;
+ }
+
+ StringBuffer buf = new StringBuffer();
+ int len = content.length();
+ char character;
+ boolean newline = false;
+ for (int i = 0; i < len; i++) {
+ switch (character = content.charAt(i)) {
+ case ' ':
+ if (!newline) {
+ buf.append(character);
+ }
+ break;
+ case '\n':
+ if (i > 0) {
+ newline = true;
+ buf.append(' ');
+ }
+ break;
+ case '\r':
+ break;
+ case '\t':
+ break;
+ default:
+ newline = false;
+ buf.append(character);
+ }
+ }
+ if (currentChunk == null) {
+ if (bf == null) {
+ currentChunk = new Chunk(buf.toString());
+ }
+ else {
+ currentChunk = new Chunk(buf.toString(), new Font(this.bf));
+ }
+ } else {
+ currentChunk.append(buf.toString());
+ }
+ }
+
+ private BaseFont bf = null;
+
+ /**
+ * Sets the font that has to be used.
+ * @param bf
+ */
+ public void setBaseFont(BaseFont bf) {
+ this.bf = bf;
+ }
+
+ /**
+ * This method gets called when an end tag is encountered.
+ *
+ * @param uri
+ * the Uniform Resource Identifier
+ * @param lname
+ * the local name (without prefix), or the empty string if
+ * Namespace processing is not being performed.
+ * @param name
+ * the name of the tag that ends
+ */
+
+ public void endElement(String uri, String lname, String name) {
+ handleEndingTags(name);
+ }
+
+ /**
+ * This method deals with the starting tags.
+ *
+ * @param name
+ * the name of the tag
+ */
+
+ public void handleEndingTags(String name) {
+
+ // System.err.println("Stop: " + name);
+
+ if (ElementTags.IGNORE.equals(name)) {
+ ignore = false;
+ return;
+ }
+ if (ignore)
+ return;
+ // tags that don't have any content
+ if (isNewpage(name) || Annotation.isTag(name) || Image.isTag(name)
+ || isNewline(name)) {
+ return;
+ }
+
+ try {
+ // titles of sections and chapters
+ if (Section.isTitle(name)) {
+ Paragraph current = (Paragraph) stack.pop();
+ if (currentChunk != null) {
+ current.add(currentChunk);
+ currentChunk = null;
+ }
+ Section previous = (Section) stack.pop();
+ previous.setTitle(current);
+ stack.push(previous);
+ return;
+ }
+
+ // all other endtags
+ if (currentChunk != null) {
+ TextElementArray current;
+ try {
+ current = (TextElementArray) stack.pop();
+ } catch (EmptyStackException ese) {
+ current = new Paragraph();
+ }
+ current.add(currentChunk);
+ stack.push(current);
+ currentChunk = null;
+ }
+
+ // chunks
+ if (Chunk.isTag(name)) {
+ return;
+ }
+
+ // phrases, anchors, lists, tables
+ if (Phrase.isTag(name) || Anchor.isTag(name) || List.isTag(name)
+ || Paragraph.isTag(name)) {
+ Element current = (Element) stack.pop();
+ try {
+ TextElementArray previous = (TextElementArray) stack.pop();
+ previous.add(current);
+ stack.push(previous);
+ } catch (EmptyStackException ese) {
+ document.add(current);
+ }
+ return;
+ }
+
+ // listitems
+ if (ListItem.isTag(name)) {
+ ListItem listItem = (ListItem) stack.pop();
+ List list = (List) stack.pop();
+ list.add(listItem);
+ stack.push(list);
+ }
+
+ // tables
+ if (Table.isTag(name)) {
+ Table table = (Table) stack.pop();
+ try {
+ TextElementArray previous = (TextElementArray) stack.pop();
+ previous.add(table);
+ stack.push(previous);
+ } catch (EmptyStackException ese) {
+ document.add(table);
+ }
+ return;
+ }
+
+ // rows
+ if (Row.isTag(name)) {
+ ArrayList cells = new ArrayList();
+ int columns = 0;
+ Table table;
+ Cell cell;
+ while (true) {
+ Element element = (Element) stack.pop();
+ if (element.type() == Element.CELL) {
+ cell = (Cell) element;
+ columns += cell.colspan();
+ cells.add(cell);
+ } else {
+ table = (Table) element;
+ break;
+ }
+ }
+ if (table.columns() < columns) {
+ table.addColumns(columns - table.columns());
+ }
+ Collections.reverse(cells);
+ String width;
+ float[] cellWidths = new float[columns];
+ boolean[] cellNulls = new boolean[columns];
+ for (int i = 0; i < columns; i++) {
+ cellWidths[i] = 0;
+ cellNulls[i] = true;
+ }
+ float total = 0;
+ int j = 0;
+ for (Iterator i = cells.iterator(); i.hasNext();) {
+ cell = (Cell) i.next();
+ if ((width = cell.cellWidth()) == null) {
+ if (cell.colspan() == 1 && cellWidths[j] == 0) {
+ try {
+ cellWidths[j] = 100f / columns;
+ total += cellWidths[j];
+ } catch (Exception e) {
+ // empty on purpose
+ }
+ } else if (cell.colspan() == 1) {
+ cellNulls[j] = false;
+ }
+ } else if (cell.colspan() == 1 && width.endsWith("%")) {
+ try {
+ cellWidths[j] = Float.valueOf(
+ width.substring(0, width.length() - 1)
+ + "f").floatValue();
+ total += cellWidths[j];
+ } catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ j += cell.colspan();
+ table.addCell(cell);
+ }
+ float widths[] = table.getProportionalWidths();
+ if (widths.length == columns) {
+ float left = 0.0f;
+ for (int i = 0; i < columns; i++) {
+ if (cellNulls[i] && widths[i] != 0) {
+ left += widths[i];
+ cellWidths[i] = widths[i];
+ }
+ }
+ if (100.0 >= total) {
+ for (int i = 0; i < widths.length; i++) {
+ if (cellWidths[i] == 0 && widths[i] != 0) {
+ cellWidths[i] = (widths[i] / left)
+ * (100.0f - total);
+ }
+ }
+ }
+ table.setWidths(cellWidths);
+ }
+ stack.push(table);
+ }
+
+ // cells
+ if (Cell.isTag(name)) {
+ return;
+ }
+
+ // sections
+ if (Section.isTag(name)) {
+ stack.pop();
+ return;
+ }
+
+ // chapters
+ if (Chapter.isTag(name)) {
+ document.add((Element) stack.pop());
+ return;
+ }
+
+ // the documentroot
+ if (isDocumentRoot(name)) {
+ try {
+ while (true) {
+ Element element = (Element) stack.pop();
+ try {
+ TextElementArray previous = (TextElementArray) stack
+ .pop();
+ previous.add(element);
+ stack.push(previous);
+ } catch (EmptyStackException es) {
+ document.add(element);
+ }
+ }
+ } catch (EmptyStackException ese) {
+ // empty on purpose
+ }
+ if (controlOpenClose)
+ document.close();
+ return;
+ }
+ } catch (DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ /**
+ * Checks if a certain tag corresponds with the newpage-tag.
+ *
+ * @param tag
+ * a presumed tagname
+ * @return
+ * An
+ * Example:
+ *
+ * o option in control file
+ *
+ * This command prints the data file to be printed, treating the data as
+ * standard Postscript input.
+ *
+ * J option in control file
+ *
+ * This command sets the job name to be printed on the banner page. The name
+ * of the job must be 99 or fewer octets. It can be omitted. The job name is
+ * conventionally used to display the name of the file or files which were
+ * "printed". It will be ignored unless the print banner command ('L') is
+ * also used.
+ *
+ * p - Print file with 'pr' format
+ *
+ * This command causes the data file to be printed with a heading, page
+ * numbers, and pagination. The heading should include the date and time
+ * that printing was started, the title, and a page number identifier
+ * followed by the page number. The title is the name of file as specified
+ * by the 'N' command, unless the 'T' command (title) has been given. After
+ * a page of text has been printed, a new page is started with a new page
+ * number. (There is no way to specify the length of the page.)
+ *
+ * L option in control file
+ *
+ * This command causes the banner page to be printed. The user name can be
+ * omitted. The class name for banner page and job name for banner page
+ * commands must precede this command in the control file to be effective.
+ *
+ * This command starts the printing process if it not already running.
+ *
+ * This command deletes the print jobs from the specified queue which are
+ * listed as the other operands. If only the agent is given, the command is
+ * to delete the currently active job. Unless the agent is "root", it is not
+ * possible to delete a job which is not owned by the user. This is also the
+ * case for specifying user names instead of numbers. That is, agent "root"
+ * can delete jobs by user name but no other agents can.
+ * ");
+ sb.append((String) it.next());
+ sb.append(" iText version: "
+ + Document.getVersion() + " java.version: "
+ + properties.getProperty("java.version") + " java.vendor: "
+ + properties.getProperty("java.vendor") + " java.home: " + properties.getProperty("java.home")
+ + " java.freeMemory: "
+ + String.valueOf(runtime.freeMemory()) + " bytes" + " java.totalMemory: "
+ + String.valueOf(runtime.totalMemory()) + " bytes" + " user.home: " + properties.getProperty("user.home")
+ + " os.name: " + properties.getProperty("os.name")
+ + " os.arch: " + properties.getProperty("os.arch")
+ + " os.version: " + properties.getProperty("os.version")
+ + " ");
+ sb.append(reader.getCropBox(page).height() + "*"
+ + reader.getCropBox(page).width() + " ");
+ sb.append("PDF Version: " + reader.getPdfVersion() + " ");
+ sb.append("Number of pages: " + reader.getNumberOfPages()
+ + " ");
+ sb.append("Number of PDF objects: " + reader.getXrefSize()
+ + " ");
+ sb.append("File length: " + reader.getFileLength() + " ");
+ sb.append("Encrypted= " + reader.isEncrypted() + " ");
+ if (pdfinfo.get("Title") != null) {
+ sb.append("Title= " + pdfinfo.get("Title") + " ");
+ }
+ if (pdfinfo.get("Author") != null) {
+ sb.append("Author= " + pdfinfo.get("Author") + " ");
+ }
+ if (pdfinfo.get("Subject") != null) {
+ sb.append("Subject= " + pdfinfo.get("Subject") + " ");
+ }
+ if (pdfinfo.get("Producer") != null) {
+ sb.append("Producer= " + pdfinfo.get("Producer") + " ");
+ }
+ if (pdfinfo.get("ModDate") != null) {
+ sb.append("ModDate= " +
+ PdfDate.decode(pdfinfo.get("ModDate").toString()).getTime() +
+ " ");
+ }
+ if (pdfinfo.get("CreationDate") != null) {
+ sb.append("CreationDate= " +
+ PdfDate.decode(pdfinfo.get("CreationDate").toString()).getTime() +
+ " ");
+ }
+ sb.append("");
+ jLabel1.setText(sb.toString());
+ }
+ catch (IOException ex) {
+ jLabel1.setText("");
+ }
+ }
+ }
+
+ public void propertyChange(PropertyChangeEvent evt) {
+ filename = evt.getPropertyName();
+ if (filename.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) {
+ File file = (File) evt.getNewValue();
+ if (file != null) {
+ this.createTextFromPDF(file);
+ this.repaint();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/arguments/OptionArgument.java b/src/main/java/com/lowagie/tools/arguments/OptionArgument.java
new file mode 100644
index 0000000..28d4eda
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/arguments/OptionArgument.java
@@ -0,0 +1,210 @@
+/*
+ * $Id: OptionArgument.java,v 1.8 2006/05/30 09:13:00 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.arguments;
+
+import java.awt.event.ActionEvent;
+import java.util.Iterator;
+import java.util.TreeMap;
+
+import javax.swing.JComboBox;
+import javax.swing.JOptionPane;
+
+import com.lowagie.tools.plugins.AbstractTool;
+
+/**
+ * Argument that can be one of several options.
+ */
+public class OptionArgument extends ToolArgument {
+
+ /**
+ * An Entry that can be chosen as option.
+ */
+ public class Entry {
+ /** Describes the option. */
+ private Object description;
+ /** Holds the actual value of the option. */
+ private Object value;
+ /**
+ * Constructs an entry.
+ * @param value the value of the entry (that wil be identical to the description)
+ */
+ public Entry(Object value) {
+ this.value = value;
+ this.description = value;
+ }
+ /**
+ * Constructs an entry.
+ * @param description the description of the entry
+ * @param value the value of the entry
+ */
+ public Entry(Object description, Object value) {
+ this.description = description;
+ this.value = value;
+ }
+ /**
+ * String representation of the Entry.
+ * @return a description of the entry
+ */
+ public String toString() {
+ return description.toString();
+ }
+ /**
+ * Gets the value of the String.
+ * @return the toString of the value
+ */
+ public String getValueToString() {
+ return value.toString();
+ }
+ /**
+ * @return Returns the description.
+ */
+ public Object getDescription() {
+ return description;
+ }
+ /**
+ * @param description The description to set.
+ */
+ public void setDescription(Object description) {
+ this.description = description;
+ }
+ /**
+ * @return Returns the value.
+ */
+ public Object getValue() {
+ return value;
+ }
+ /**
+ * @param value The value to set.
+ */
+ public void setValue(Object value) {
+ this.value = value;
+ }
+ }
+
+ private TreeMap options = new TreeMap();
+
+ /**
+ * Constructs an OptionArgument.
+ * @param tool the tool that needs this argument
+ * @param name the name of the argument
+ * @param description the description of the argument
+ */
+ public OptionArgument(AbstractTool tool, String name, String description) {
+ super(tool, name, description, Entry.class.getName());
+ }
+
+ /**
+ * Adds an Option.
+ * @param description the description of the option
+ * @param value the value of the option
+ */
+ public void addOption(Object description, Object value) {
+ options.put(value.toString(), new Entry(description, value));
+ }
+
+ /**
+ * Gets the argument as an object.
+ * @return an object
+ * @throws InstantiationException
+ */
+ public Object getArgument() throws InstantiationException {
+ if (value == null) return null;
+ try {
+ return ((Entry)options.get(value)).getValue();
+ } catch (Exception e) {
+ throw new InstantiationException(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.arguments.ToolArgument#getUsage()
+ */
+ public String getUsage() {
+ StringBuffer buf = new StringBuffer(super.getUsage());
+ buf.append(" possible options:\n");
+ Entry entry;
+ for (Iterator i = options.values().iterator(); i.hasNext(); ) {
+ entry = (Entry)i.next();
+ buf.append(" - ");
+ buf.append(entry.getValueToString());
+ buf.append(": ");
+ buf.append(entry.toString());
+ buf.append("\n");
+ }
+ return buf.toString();
+ }
+
+ /**
+ * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+ */
+ public void actionPerformed(ActionEvent evt) {
+ Object[] message = new Object[2];
+ message[0] = "Choose one of the following options:";
+ JComboBox cb = new JComboBox();
+ for(Iterator i = options.values().iterator(); i.hasNext(); ) {
+ cb.addItem(i.next());
+ }
+ message[1] = cb;
+ int result = JOptionPane.showOptionDialog(
+ tool.getInternalFrame(),
+ message,
+ description,
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.QUESTION_MESSAGE,
+ null,
+ null,
+ null
+ );
+ if (result == 0) {
+ Entry entry = (Entry)cb.getSelectedItem();
+ setValue(entry.getValueToString());
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/arguments/PageSelectionTableDialog.java b/src/main/java/com/lowagie/tools/arguments/PageSelectionTableDialog.java
new file mode 100644
index 0000000..cf14442
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/arguments/PageSelectionTableDialog.java
@@ -0,0 +1,243 @@
+package com.lowagie.tools.arguments;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.table.*;
+import java.awt.BorderLayout;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+/**
+ * Title: Description: Copyright: Copyright (c) 2005 Company: " + name + "";
+ break;
+ case 1:
+ name = "Weidth " + name + "";
+ break;
+ case 2:
+ name = "Height " + name + "";
+ break;
+ case 3:
+ name = "Rotation " + name + "";
+ break;
+
+ default:
+ name = "- " + name + "";
+ break;
+ }
+ return name;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/arguments/PdfFilter.java b/src/main/java/com/lowagie/tools/arguments/PdfFilter.java
new file mode 100644
index 0000000..5b0b8b4
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/arguments/PdfFilter.java
@@ -0,0 +1,77 @@
+/*
+ * $Id: PdfFilter.java,v 1.2 2005/10/24 06:33:31 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.arguments;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+/**
+ * Filters PDF files in a JFileChooser.
+ */
+public class PdfFilter extends FileFilter {
+
+ /**
+ * @see javax.swing.filechooser.FileFilter#accept(java.io.File)
+ */
+ public boolean accept(File f) {
+ if (f.isDirectory()) return true;
+ if (f.getName().toLowerCase().endsWith(".pdf")) return true;
+ return false;
+ }
+
+ /**
+ * @see javax.swing.filechooser.FileFilter#getDescription()
+ */
+ public String getDescription() {
+ return "*.pdf PDF files";
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/arguments/TableMap.java b/src/main/java/com/lowagie/tools/arguments/TableMap.java
new file mode 100644
index 0000000..58308ee
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/arguments/TableMap.java
@@ -0,0 +1,89 @@
+package com.lowagie.tools.arguments;
+/*
+ * @(#)TableMap.java 1.4 97/12/17
+ *
+ * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of Sun
+ * Microsystems, Inc. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Sun.
+ *
+ * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
+ * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+/**
+ * In a chain of data manipulators some behaviour is common. TableMap
+ * provides most of this behavour and can be subclassed by filters
+ * that only need to override a handful of specific methods. TableMap
+ * implements TableModel by routing all requests to its model, and
+ * TableModelListener by routing all events to its listeners. Inserting
+ * a TableMap which has not been subclassed into a chain of table filters
+ * should have no effect.
+ *
+ * @version 1.4 12/17/97
+ * @author Philip Milne */
+
+import javax.swing.table.*;
+import javax.swing.event.TableModelListener;
+import javax.swing.event.TableModelEvent;
+
+public class TableMap extends AbstractTableModel implements TableModelListener
+{
+ protected TableModel model;
+
+ public TableModel getModel() {
+ return model;
+ }
+
+ public void setModel(TableModel model) {
+ this.model = model;
+ model.addTableModelListener(this);
+ }
+
+ // By default, Implement TableModel by forwarding all messages
+ // to the model.
+
+ public Object getValueAt(int aRow, int aColumn) {
+ return model.getValueAt(aRow, aColumn);
+ }
+
+ public void setValueAt(Object aValue, int aRow, int aColumn) {
+ model.setValueAt(aValue, aRow, aColumn);
+ }
+
+ public int getRowCount() {
+ return (model == null) ? 0 : model.getRowCount();
+ }
+
+ public int getColumnCount() {
+ return (model == null) ? 0 : model.getColumnCount();
+ }
+
+ public String getColumnName(int aColumn) {
+ return model.getColumnName(aColumn);
+ }
+
+ public Class getColumnClass(int aColumn) {
+ return model.getColumnClass(aColumn);
+ }
+
+ public boolean isCellEditable(int row, int column) {
+ return model.isCellEditable(row, column);
+ }
+//
+// Implementation of the TableModelListener interface,
+//
+
+ // By default forward all events to all the listeners.
+ public void tableChanged(TableModelEvent e) {
+ fireTableChanged(e);
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/arguments/TableSorter.java b/src/main/java/com/lowagie/tools/arguments/TableSorter.java
new file mode 100644
index 0000000..a921516
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/arguments/TableSorter.java
@@ -0,0 +1,367 @@
+/** Code contributed by Anonymous; looks like code that was reused from another application. */
+package com.lowagie.tools.arguments;
+
+import java.awt.event.InputEvent;
+
+// Imports for picking up mouse events from the JTable.
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+/*
+ * @(#)TableSorter.java 1.5 97/12/17
+ *
+ * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of Sun
+ * Microsystems, Inc. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Sun.
+ *
+ * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
+ * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+/**
+ * A sorter for TableModels. The sorter has a model (conforming to TableModel)
+ * and itself implements TableModel. TableSorter does not store or copy
+ * the data in the TableModel, instead it maintains an array of
+ * integers which it keeps the same size as the number of rows in its
+ * model. When the model changes it notifies the sorter that something
+ * has changed eg. "rowsAdded" so that its internal array of integers
+ * can be reallocated. As requests are made of the sorter (like
+ * getValueAt(row, col) it redirects them to its model via the mapping
+ * array. That way the TableSorter appears to hold another copy of the table
+ * with the rows in a different order. The sorting algorthm used is stable
+ * which means that it does not move around rows when its comparison
+ * function returns 0 to denote that they are equivalent.
+ *
+ * @version 1.5 12/17/97
+ * @author Philip Milne
+ */
+import java.util.*;
+
+import javax.swing.JTable;
+import javax.swing.event.TableModelEvent;
+import javax.swing.table.JTableHeader;
+import javax.swing.table.TableColumnModel;
+import javax.swing.table.TableModel;
+
+public class TableSorter
+ extends TableMap {
+ int[] indexes;
+ Vector sortingColumns = new Vector();
+ boolean ascending = true;
+ int compares;
+
+ public TableSorter() {
+ indexes = new int[0]; // For consistency.
+ }
+
+ public TableSorter(TableModel model) {
+ setModel(model);
+ }
+
+ public void setModel(TableModel model) {
+ super.setModel(model);
+ reallocateIndexes();
+ }
+
+ public int compareRowsByColumn(int row1, int row2, int column) {
+ Class type = model.getColumnClass(column);
+ TableModel data = model;
+
+ // Check for nulls
+ Object o1 = data.getValueAt(row1, column);
+ Object o2 = data.getValueAt(row2, column);
+
+ // If both values are null return 0
+ if ( (o1 == null) && (o2 == null)) {
+ return 0;
+ }
+ else if (o1 == null) { // Define null less than everything.
+
+ return -1;
+ }
+ else if (o2 == null) {
+ return 1;
+ }
+
+ /* We copy all returned values from the getValue call in case
+ an optimised model is reusing one object to return many values.
+ The Number subclasses in the JDK are immutable and so will not be used in
+ this way but other subclasses of Number might want to do this to save
+ space and avoid unnecessary heap allocation.
+ */
+ if (type.getSuperclass() == java.lang.Number.class) {
+ Number n1 = (Number) data.getValueAt(row1, column);
+ double d1 = n1.doubleValue();
+ Number n2 = (Number) data.getValueAt(row2, column);
+ double d2 = n2.doubleValue();
+
+ if (d1 < d2) {
+ return -1;
+ }
+ else if (d1 > d2) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ else if (type == java.util.Date.class) {
+ Date d1 = (Date) data.getValueAt(row1, column);
+ long n1 = d1.getTime();
+ Date d2 = (Date) data.getValueAt(row2, column);
+ long n2 = d2.getTime();
+
+ if (n1 < n2) {
+ return -1;
+ }
+ else if (n1 > n2) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ else if (type == String.class) {
+ String s1 = (String) data.getValueAt(row1, column);
+ String s2 = (String) data.getValueAt(row2, column);
+ int result = s1.compareTo(s2);
+
+ if (result < 0) {
+ return -1;
+ }
+ else if (result > 0) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ else if (type == Boolean.class) {
+ Boolean bool1 = (Boolean) data.getValueAt(row1, column);
+ boolean b1 = bool1.booleanValue();
+ Boolean bool2 = (Boolean) data.getValueAt(row2, column);
+ boolean b2 = bool2.booleanValue();
+
+ if (b1 == b2) {
+ return 0;
+ }
+ else if (b1) { // Define false < true
+
+ return 1;
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ Object v1 = data.getValueAt(row1, column);
+ String s1 = v1.toString();
+ Object v2 = data.getValueAt(row2, column);
+ String s2 = v2.toString();
+ int result = s1.compareTo(s2);
+
+ if (result < 0) {
+ return -1;
+ }
+ else if (result > 0) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ }
+
+ public int compare(int row1, int row2) {
+ compares++;
+
+ for (int level = 0; level < sortingColumns.size(); level++) {
+ Integer column = (Integer) sortingColumns.elementAt(level);
+ int result = compareRowsByColumn(row1, row2, column.intValue());
+
+ if (result != 0) {
+ return ascending ? result : ( -result);
+ }
+ }
+
+ return 0;
+ }
+
+ public void reallocateIndexes() {
+ int rowCount = model.getRowCount();
+
+ // Set up a new array of indexes with the right number of elements
+ // for the new data model.
+ indexes = new int[rowCount];
+
+ // Initialise with the identity mapping.
+ for (int row = 0; row < rowCount; row++) {
+ indexes[row] = row;
+ }
+ }
+
+ public void tableChanged(TableModelEvent e) {
+ //System.out.println("Sorter: tableChanged");
+ reallocateIndexes();
+ super.tableChanged(e);
+ }
+
+ public void checkModel() {
+ if (indexes.length != model.getRowCount()) {
+ System.err.println("Sorter not informed of a change in model.");
+ }
+ }
+
+ public void sort(Object sender) {
+ checkModel();
+ compares = 0;
+
+ // n2sort();
+ // qsort(0, indexes.length-1);
+ shuttlesort( (int[]) indexes.clone(), indexes, 0, indexes.length);
+
+ //System.out.println("Compares: "+compares);
+ }
+
+ public void n2sort() {
+ for (int i = 0; i < getRowCount(); i++) {
+ for (int j = i + 1; j < getRowCount(); j++) {
+ if (compare(indexes[i], indexes[j]) == -1) {
+ swap(i, j);
+ }
+ }
+ }
+ }
+
+ // This is a home-grown implementation which we have not had time
+ // to research - it may perform poorly in some circumstances. It
+ // requires twice the space of an in-place algorithm and makes
+ // NlogN assigments shuttling the values between the two
+ // arrays. The number of compares appears to vary between N-1 and
+ // NlogN depending on the initial order but the main reason for
+ // using it here is that, unlike qsort, it is stable.
+ public void shuttlesort(int[] from, int[] to, int low, int high) {
+ if ( (high - low) < 2) {
+ return;
+ }
+
+ int middle = (low + high) / 2;
+ shuttlesort(to, from, low, middle);
+ shuttlesort(to, from, middle, high);
+
+ int p = low;
+ int q = middle;
+
+ /* This is an optional short-cut; at each recursive call,
+ check to see if the elements in this subset are already
+ ordered. If so, no further comparisons are needed; the
+ sub-array can just be copied. The array must be copied rather
+ than assigned otherwise sister calls in the recursion might
+ get out of sinc. When the number of elements is three they
+ are partitioned so that the first set, [low, mid), has one
+ element and and the second, [mid, high), has two. We skip the
+ optimisation when the number of elements is three or less as
+ the first compare in the normal merge will produce the same
+ sequence of steps. This optimisation seems to be worthwhile
+ for partially ordered lists but some analysis is needed to
+ find out how the performance drops to Nlog(N) as the initial
+ order diminishes - it may drop very quickly. */
+ if ( ( (high - low) >= 4) && (compare(from[middle - 1], from[middle]) <= 0)) {
+ for (int i = low; i < high; i++) {
+ to[i] = from[i];
+ }
+
+ return;
+ }
+
+ // A normal merge.
+ for (int i = low; i < high; i++) {
+ if ( (q >= high) || ( (p < middle) && (compare(from[p], from[q]) <= 0))) {
+ to[i] = from[p++];
+ }
+ else {
+ to[i] = from[q++];
+ }
+ }
+ }
+
+ public void swap(int i, int j) {
+ int tmp = indexes[i];
+ indexes[i] = indexes[j];
+ indexes[j] = tmp;
+ }
+
+ // The mapping only affects the contents of the data rows.
+ // Pass all requests to these rows through the mapping array: "indexes".
+ public Object getValueAt(int aRow, int aColumn) {
+ checkModel();
+
+ return model.getValueAt(indexes[aRow], aColumn);
+ }
+
+ public void setValueAt(Object aValue, int aRow, int aColumn) {
+ checkModel();
+ model.setValueAt(aValue, indexes[aRow], aColumn);
+ }
+
+ public void sortByColumn(int column) {
+ sortByColumn(column, true);
+ }
+
+ public void sortByColumn(int column, boolean ascending) {
+ this.ascending = ascending;
+ sortingColumns.removeAllElements();
+ sortingColumns.addElement(new Integer(column));
+ sort(this);
+ super.tableChanged(new TableModelEvent(this));
+ }
+
+ // There is no-where else to put this.
+ // Add a mouse listener to the Table to trigger a table sort
+ // when a column heading is clicked in the JTable.
+ public void addMouseListenerToHeaderInTable(JTable table) {
+ final TableSorter sorter = this;
+ final JTable tableView = table;
+ tableView.setColumnSelectionAllowed(false);
+
+ MouseAdapter listMouseListener = new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ TableColumnModel columnModel = tableView.getColumnModel();
+ int viewColumn = columnModel.getColumnIndexAtX(e.getX());
+ int column = tableView.convertColumnIndexToModel(viewColumn);
+
+ if ( (e.getClickCount() == 1) && (column != -1)) {
+ //System.out.println("Sorting ...");
+ int shiftPressed = e.getModifiers() & InputEvent.SHIFT_MASK;
+ boolean ascending = (shiftPressed == 0);
+ sorter.sortByColumn(column, ascending);
+ }
+ }
+ };
+
+ JTableHeader th = tableView.getTableHeader();
+ th.addMouseListener(listMouseListener);
+ }
+
+ public int getModelrow(int viewrow) {
+ return indexes[viewrow];
+ }
+
+ public int getjTablerow(int modelrow) {
+ int i = 0;
+ while (indexes[i] != modelrow) {
+ i++;
+ }
+ return i;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/arguments/ToolArgument.java b/src/main/java/com/lowagie/tools/arguments/ToolArgument.java
new file mode 100644
index 0000000..203c298
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/arguments/ToolArgument.java
@@ -0,0 +1,281 @@
+/*
+ * $Id: ToolArgument.java,v 1.6 2006/05/30 09:13:00 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.arguments;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.util.Vector;
+
+import javax.swing.JColorChooser;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.Image;
+import com.lowagie.tools.plugins.AbstractTool;
+
+/**
+ * This is an argument of one of the tools in the toolbox.
+ */
+public class ToolArgument
+ implements ActionListener, PropertyChangeListener {
+ /** reference to the internal frame */
+ protected AbstractTool tool;
+ /** describes the argument. */
+ protected String description;
+ /** short name for the argument. */
+ protected String name;
+ /** type of the argument. */
+ protected String classname;
+ /** value of the argument. */
+ protected String value = null;
+
+ /** Constructs a ToolArgument. */
+ public ToolArgument() {}
+
+ /**
+ * Constructs a ToolArgument.
+ * @param tool the tool that needs this argument
+ * @param name the name of the argument
+ * @param description the description of the argument
+ * @param classname the type of the argument
+ */
+ public ToolArgument(AbstractTool tool, String name, String description,
+ String classname) {
+ this.tool = tool;
+ this.name = name;
+ this.description = description;
+ this.classname = classname;
+ }
+
+ /**
+ * Gets the argument as an object.
+ * @return an object
+ * @throws InstantiationException
+ */
+ public Object getArgument() throws InstantiationException {
+ if (value == null) {
+ return null;
+ }
+ try {
+ if (String.class.getName().equals(classname)) {
+ return value;
+ }
+ if (Image.class.getName().equals(classname)) {
+ return Image.getInstance(value);
+ }
+ if (File.class.getName().equals(classname)) {
+ return new File(value);
+ }
+ if (Color.class.getName().equals(classname)) {
+ return Color.decode(value);
+ }
+ }
+ catch (Exception e) {
+ throw new InstantiationException(e.getMessage());
+ }
+ return value;
+ }
+
+ /**
+ * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+ */
+ public void actionPerformed(ActionEvent e) {
+ if (String.class.getName().equals(classname)) {
+ setValue(JOptionPane.showInputDialog(tool.getInternalFrame(),
+ "Enter a value for " + name + ":"));
+ }
+ if (Image.class.getName().equals(classname)) {
+ JFileChooser fc = new JFileChooser();
+ fc.showOpenDialog(tool.getInternalFrame());
+ setValue(fc.getSelectedFile().getAbsolutePath());
+ }
+ if (File.class.getName().equals(classname)) {
+ JFileChooser fc = new JFileChooser();
+ fc.showOpenDialog(tool.getInternalFrame());
+ setValue(fc.getSelectedFile().getAbsolutePath());
+ }
+ if (Color.class.getName().equals(classname)) {
+ Color initialColor = new Color(0xFF, 0xFF, 0xFF);
+ if (value != null) {
+ initialColor = Color.decode(value);
+ }
+ Color newColor = JColorChooser.showDialog(tool.getInternalFrame(),
+ "Choose Color", initialColor);
+ setValue("0x" +
+ Integer.toHexString( (newColor.getRed() << 16) |
+ (newColor.getGreen() << 8) |
+ (newColor.getBlue() << 0)).toUpperCase());
+ }
+ }
+
+ /**
+ * Give you a String that can be used in a usage description.
+ * @return a String
+ */
+ public String getUsage() {
+ StringBuffer buf = new StringBuffer(" ");
+ buf.append(name);
+ buf.append(" - ");
+ buf.append(description);
+ buf.append("\n");
+ return buf.toString();
+ }
+
+ /**
+ * @return Returns the classname.
+ */
+ public String getClassname() {
+ return classname;
+ }
+
+ /**
+ * @param classname The classname to set.
+ */
+ public void setClassname(String classname) {
+ this.classname = classname;
+ }
+
+ /**
+ * @return Returns the description.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * @param description The description to set.
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name The name to set.
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return Returns the value.
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * @param value The value to set.
+ */
+ public void setValue(String value) {
+ Object oldvalue = this.value;
+ this.value = value;
+ tool.valueHasChanged(this);
+ this.firePropertyChange(new PropertyChangeEvent(this, name,
+ oldvalue, this.value));
+ }
+ public void setValue(String value, String propertyname) {
+ Object oldvalue = this.value;
+ this.value = value;
+ tool.valueHasChanged(this);
+ this.firePropertyChange(new PropertyChangeEvent(this, propertyname,
+ oldvalue, this.value));
+ }
+ transient Vector propertyChangeListeners;
+ public synchronized void addPropertyChangeListener(PropertyChangeListener l) {
+
+ Vector v = propertyChangeListeners == null ? new Vector(2) :
+ (Vector) propertyChangeListeners.clone();
+ if (!v.contains(l)) {
+ v.addElement(l);
+ propertyChangeListeners = v;
+ }
+ }
+
+ public synchronized void removePropertyChangeListener(PropertyChangeListener
+ l) {
+ if (propertyChangeListeners != null && propertyChangeListeners.contains(l)) {
+ Vector v = (Vector) propertyChangeListeners.clone();
+ v.removeElement(l);
+ propertyChangeListeners = v;
+ }
+
+ }
+
+ protected void firePropertyChange(PropertyChangeEvent evt) {
+ if (propertyChangeListeners != null) {
+ Vector listeners = propertyChangeListeners;
+ int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ ( (PropertyChangeListener) listeners.elementAt(i)).propertyChange(evt);
+ }
+ }
+ }
+
+ /**
+ * This method gets called when a bound property is changed.
+ *
+ * @param evt A PropertyChangeEvent object describing the event source and the property that has
+ * changed.
+ * @todo Implement this java.beans.PropertyChangeListener method
+ */
+ public void propertyChange(PropertyChangeEvent evt) {
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/concat_pdf.java b/src/main/java/com/lowagie/tools/concat_pdf.java
new file mode 100644
index 0000000..e9929a0
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/concat_pdf.java
@@ -0,0 +1,96 @@
+/*
+ * $Id: concat_pdf.java,v 1.19 2006/04/28 16:26:38 psoares33 Exp $
+ * $Name: $
+ *
+ * This code is free software. It may only be copied or modified
+ * if you include the following copyright notice:
+ *
+ * This class by Mark Thompson. Copyright (c) 2002 Mark Thompson.
+ *
+ * This code 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.
+ *
+ * itext@lowagie.com
+ */
+
+/**
+ * This class demonstrates copying a PDF file using iText.
+ * @author Mark Thompson
+ */
+package com.lowagie.tools;
+
+import java.io.*;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.pdf.*;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Tool that can be used to concatenate existing PDF files.
+ */
+public class concat_pdf {
+
+ /**
+ * This class can be used to concatenate existing PDF files.
+ * (This was an example known as PdfCopy.java)
+ * @param args the command line arguments
+ */
+ public static void main(String args[]) {
+ if (args.length < 2) {
+ System.err.println("arguments: file1 [file2 ...] destfile");
+ }
+ else {
+ try {
+ int pageOffset = 0;
+ ArrayList master = new ArrayList();
+ int f = 0;
+ String outFile = args[args.length-1];
+ Document document = null;
+ PdfCopy writer = null;
+ while (f < args.length-1) {
+ // we create a reader for a certain document
+ PdfReader reader = new PdfReader(args[f]);
+ reader.consolidateNamedDestinations();
+ // we retrieve the total number of pages
+ int n = reader.getNumberOfPages();
+ List bookmarks = SimpleBookmark.getBookmark(reader);
+ if (bookmarks != null) {
+ if (pageOffset != 0)
+ SimpleBookmark.shiftPageNumbers(bookmarks, pageOffset, null);
+ master.addAll(bookmarks);
+ }
+ pageOffset += n;
+ System.out.println("There are " + n + " pages in " + args[f]);
+
+ if (f == 0) {
+ // step 1: creation of a document-object
+ document = new Document(reader.getPageSizeWithRotation(1));
+ // step 2: we create a writer that listens to the document
+ writer = new PdfCopy(document, new FileOutputStream(outFile));
+ // step 3: we open the document
+ document.open();
+ }
+ // step 4: we add content
+ PdfImportedPage page;
+ for (int i = 0; i < n; ) {
+ ++i;
+ page = writer.getImportedPage(reader, i);
+ writer.addPage(page);
+ System.out.println("Processed page " + i);
+ }
+ writer.freeReader(reader);
+ f++;
+ }
+ if (master.size() > 0)
+ writer.setOutlines(master);
+ // step 5: we close the document
+ document.close();
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/encrypt_pdf.java b/src/main/java/com/lowagie/tools/encrypt_pdf.java
new file mode 100644
index 0000000..e6a0215
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/encrypt_pdf.java
@@ -0,0 +1,99 @@
+/*
+ * $Id: encrypt_pdf.java,v 1.18 2004/12/14 10:59:06 blowagie Exp $
+ * $Name: $
+ *
+ * This code is free software. It may only be copied or modified
+ * if you include the following copyright notice:
+ *
+ * This class by Paulo Soares. Copyright (c) 2002 Paulo Soares.
+ *
+ * This code 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.
+ *
+ * itext@lowagie.com
+ */
+
+/**
+ * This class demonstrates how to encrypt a pdf file
+ * @author Paulo Soares
+ */
+package com.lowagie.tools;
+
+import com.lowagie.text.pdf.PdfEncryptor;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfWriter;
+
+import java.io.FileOutputStream;
+import java.util.HashMap;
+
+/**
+ * Encrypts a PDF document. It needs iText (http://www.lowagie.com/iText).
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class encrypt_pdf {
+
+ private final static int INPUT_FILE = 0;
+ private final static int OUTPUT_FILE = 1;
+ private final static int USER_PASSWORD = 2;
+ private final static int OWNER_PASSWORD = 3;
+ private final static int PERMISSIONS = 4;
+ private final static int STRENGTH = 5;
+ private final static int MOREINFO = 6;
+ private final static int permit[] = {
+ PdfWriter.AllowPrinting,
+ PdfWriter.AllowModifyContents,
+ PdfWriter.AllowCopy,
+ PdfWriter.AllowModifyAnnotations,
+ PdfWriter.AllowFillIn,
+ PdfWriter.AllowScreenReaders,
+ PdfWriter.AllowAssembly,
+ PdfWriter.AllowDegradedPrinting};
+
+ private static void usage() {
+ System.out.println("usage: input_file output_file user_password owner_password permissions 128|40 [new info string pairs]");
+ System.out.println("permissions is 8 digit long 0 or 1. Each digit has a particular security function:");
+ System.out.println();
+ System.out.println("AllowPrinting");
+ System.out.println("AllowModifyContents");
+ System.out.println("AllowCopy");
+ System.out.println("AllowModifyAnnotations");
+ System.out.println("AllowFillIn (128 bit only)");
+ System.out.println("AllowScreenReaders (128 bit only)");
+ System.out.println("AllowAssembly (128 bit only)");
+ System.out.println("AllowDegradedPrinting (128 bit only)");
+ System.out.println("Example permissions to copy and print would be: 10100000");
+ }
+
+ /**
+ * Encrypts a PDF document.
+ *
+ * @param args input_file output_file user_password owner_password permissions 128|40 [new info string pairs]
+ */
+ public static void main (String args[]) {
+ System.out.println("PDF document encryptor");
+ if (args.length <= STRENGTH || args[PERMISSIONS].length() != 8) {
+ usage();
+ return;
+ }
+ try {
+ int permissions = 0;
+ String p = args[PERMISSIONS];
+ for (int k = 0; k < p.length(); ++k) {
+ permissions |= (p.charAt(k) == '0' ? 0 : permit[k]);
+ }
+ System.out.println("Reading " + args[INPUT_FILE]);
+ PdfReader reader = new PdfReader(args[INPUT_FILE]);
+ System.out.println("Writing " + args[OUTPUT_FILE]);
+ HashMap moreInfo = new HashMap();
+ for (int k = MOREINFO; k < args.length - 1; k += 2)
+ moreInfo.put(args[k], args[k + 1]);
+ PdfEncryptor.encrypt(reader, new FileOutputStream(args[OUTPUT_FILE]),
+ args[USER_PASSWORD].getBytes(), args[OWNER_PASSWORD].getBytes(), permissions, args[STRENGTH].equals("128"), moreInfo);
+ System.out.println("Done.");
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/tools/handout_pdf.java b/src/main/java/com/lowagie/tools/handout_pdf.java
new file mode 100644
index 0000000..3ae590f
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/handout_pdf.java
@@ -0,0 +1,122 @@
+/*
+ * $Id: handout_pdf.java,v 1.17 2004/12/14 10:59:06 blowagie Exp $
+ * $Name: $
+ *
+ * This code is free software. It may only be copied or modified
+ * if you include the following copyright notice:
+ *
+ * This class by Bruno Lowagie. Copyright (c) 2002 Bruno Lowagie.
+ *
+ * This code 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.
+ *
+ * itext@lowagie.com
+ */
+
+/**
+ * This class demonstrates how to splot a PDF file using iText.
+ * @author Bruno Lowagie
+ */
+package com.lowagie.tools;
+
+import java.io.*;
+
+import com.lowagie.text.*;
+import com.lowagie.text.pdf.*;
+
+/**
+ * Takes an existing PDF file and makes handouts.
+ */
+public class handout_pdf extends java.lang.Object {
+
+ /**
+ * Makes handouts based on an existing PDF file.
+ * @param args the command line arguments
+ */
+ public static void main (String args[]) {
+ if (args.length != 3) {
+ System.err.println("arguments: srcfile destfile pages");
+ }
+ else {
+ try {
+ int pages = Integer.parseInt(args[2]);
+ if (pages < 2 || pages > 8) {
+ throw new DocumentException("You can't have " + pages + " pages on one page (minimum 2; maximum 8).");
+ }
+
+ float x1 = 30f;
+ float x2 = 280f;
+ float x3 = 320f;
+ float x4 = 565f;
+
+ float[] y1 = new float[pages];
+ float[] y2 = new float[pages];
+
+ float height = (778f - (20f * (pages - 1))) / pages;
+ y1[0] = 812f;
+ y2[0] = 812f - height;
+
+ for (int i = 1; i < pages; i++) {
+ y1[i] = y2[i - 1] - 20f;
+ y2[i] = y1[i] - height;
+ }
+
+ // we create a reader for a certain document
+ PdfReader reader = new PdfReader(args[0]);
+ // we retrieve the total number of pages
+ int n = reader.getNumberOfPages();
+ System.out.println("There are " + n + " pages in the original file.");
+
+ // step 1: creation of a document-object
+ Document document = new Document(PageSize.A4);
+ // step 2: we create a writer that listens to the document
+ PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(args[1]));
+ // step 3: we open the document
+ document.open();
+ PdfContentByte cb = writer.getDirectContent();
+ PdfImportedPage page;
+ int rotation;
+ int i = 0;
+ int p = 0;
+ // step 4: we add content
+ while (i < n) {
+ i++;
+ Rectangle rect = reader.getPageSizeWithRotation(i);
+ float factorx = (x2 - x1) / rect.width();
+ float factory = (y1[p] - y2[p]) / rect.height();
+ float factor = (factorx < factory ? factorx : factory);
+ float dx = (factorx == factor ? 0f : ((x2 - x1) - rect.width() * factor) / 2f);
+ float dy = (factory == factor ? 0f : ((y1[p] - y2[p]) - rect.height() * factor) / 2f);
+ page = writer.getImportedPage(reader, i);
+ rotation = reader.getPageRotation(i);
+ if (rotation == 90 || rotation == 270) {
+ cb.addTemplate(page, 0, -factor, factor, 0, x1 + dx, y2[p] + dy + rect.height() * factor);
+ }
+ else {
+ cb.addTemplate(page, factor, 0, 0, factor, x1 + dx, y2[p] + dy);
+ }
+ cb.setRGBColorStroke(0xC0, 0xC0, 0xC0);
+ cb.rectangle(x3 - 5f, y2[p] - 5f, x4 - x3 + 10f, y1[p] - y2[p] + 10f);
+ for (float l = y1[p] - 19; l > y2[p]; l -= 16) {
+ cb.moveTo(x3, l);
+ cb.lineTo(x4, l);
+ }
+ cb.rectangle(x1 + dx, y2[p] + dy, rect.width() * factor, rect.height() * factor);
+ cb.stroke();
+ System.out.println("Processed page " + i);
+ p++;
+ if (p == pages) {
+ p = 0;
+ document.newPage();
+ }
+ }
+ // step 5: we close the document
+ document.close();
+ }
+ catch(Exception e) {
+ System.err.println(e.getClass().getName() + ": " + e.getMessage());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/tools/plugins/AbstractTool.java b/src/main/java/com/lowagie/tools/plugins/AbstractTool.java
new file mode 100644
index 0000000..5e57892
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/AbstractTool.java
@@ -0,0 +1,341 @@
+/*
+ * $Id: AbstractTool.java,v 1.10 2006/05/30 09:13:19 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+
+import com.lowagie.tools.Executable;
+import com.lowagie.tools.ToolMenuItems;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Every iText tool has to implement this interface.
+ */
+public abstract class AbstractTool implements ToolMenuItems, ActionListener {
+
+ /** An array with the versions of the tool. */
+ public static ArrayList versionsarray = new ArrayList();
+
+ /** The internal frame of the tool. */
+ protected JInternalFrame internalFrame = null;
+ /** The list of arguments needed by the tool. */
+ protected ArrayList arguments = new ArrayList();
+ /** Execute menu options */
+ protected int menuoptions = MENU_EXECUTE;
+ /** a menu option */
+ public static final int MENU_EXECUTE = 1;
+ /** a menu option */
+ public static final int MENU_EXECUTE_SHOW = 2;
+ /** a menu option */
+ public static final int MENU_EXECUTE_PRINT = 4;
+ /** a menu option */
+ public static final int MENU_EXECUTE_PRINT_SILENT = 8;
+
+ /**
+ * Sets the arguments.
+ * @param arguments The arguments to set.
+ */
+ public void setArguments(ArrayList arguments) {
+ this.arguments = arguments;
+ }
+
+ /**
+ * Sets the arguments.
+ * @param args the arguments as String-array.
+ */
+ public void setArguments(String[] args) {
+ int counter = 0;
+ ToolArgument argument;
+ for (Iterator i = arguments.iterator(); i.hasNext(); ) {
+ argument = (ToolArgument) i.next();
+ if (args.length > counter) {
+ argument.setValue(args[counter]);
+ }
+ else {
+ break;
+ }
+ counter++;
+ }
+ }
+
+ /**
+ * Gets the arguments.
+ * @return Returns the arguments.
+ */
+ public ArrayList getArguments() {
+ return arguments;
+ }
+
+ /**
+ * Gets the value of a given argument.
+ * @param name the name of the argument
+ * @return the value of an argument as an Object.
+ * @throws InstantiationException
+ */
+ public Object getValue(String name) throws InstantiationException {
+ ToolArgument argument;
+ for (Iterator i = arguments.iterator(); i.hasNext(); ) {
+ argument = (ToolArgument) i.next();
+ if (name.equals(argument.getName())) {
+ return argument.getArgument();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the internal frame.
+ * @param internalFrame The internalFrame to set.
+ */
+ public void setInternalFrame(JInternalFrame internalFrame) {
+ this.internalFrame = internalFrame;
+ }
+
+ /**
+ * Returns the internal frame. Creates one if it's null.
+ * @return Returns the internalFrame.
+ */
+ public JInternalFrame getInternalFrame() {
+ if (internalFrame == null) {
+ createFrame();
+ }
+ return internalFrame;
+ }
+
+ /**
+ * Gets the menubar.
+ * @return a menubar for this tool
+ */
+ public JMenuBar getMenubar() {
+ JMenuBar menubar = new JMenuBar();
+ JMenu tool = new JMenu(TOOL);
+ tool.setMnemonic(KeyEvent.VK_F);
+ JMenuItem usage = new JMenuItem(USAGE);
+ usage.setMnemonic(KeyEvent.VK_U);
+ usage.addActionListener(this);
+ tool.add(usage);
+ JMenuItem args = new JMenuItem(ARGUMENTS);
+ args.setMnemonic(KeyEvent.VK_A);
+ args.addActionListener(this);
+ tool.add(args);
+ if ((menuoptions & MENU_EXECUTE) > 0) {
+ JMenuItem execute = new JMenuItem(EXECUTE);
+ execute.setMnemonic(KeyEvent.VK_E);
+ execute.addActionListener(this);
+ tool.add(execute);
+ }
+ if ((menuoptions & MENU_EXECUTE_SHOW) > 0) {
+ JMenuItem execute = new JMenuItem(EXECUTESHOW);
+ execute.addActionListener(this);
+ tool.add(execute);
+ }
+ if ((menuoptions & MENU_EXECUTE_PRINT) > 0) {
+ JMenuItem execute = new JMenuItem(EXECUTEPRINT);
+ execute.addActionListener(this);
+ tool.add(execute);
+ }
+ if ((menuoptions & MENU_EXECUTE_PRINT_SILENT) > 0) {
+ JMenuItem execute = new JMenuItem(EXECUTEPRINTSILENT);
+ execute.addActionListener(this);
+ tool.add(execute);
+ }
+ JMenuItem close = new JMenuItem(CLOSE);
+ close.setMnemonic(KeyEvent.VK_C);
+ close.addActionListener(this);
+ tool.add(close);
+ menubar.add(tool);
+ if (arguments.size() > 0) {
+ JMenu params = new JMenu(ARGUMENTS);
+ tool.setMnemonic(KeyEvent.VK_T);
+ JMenuItem item;
+ ToolArgument argument;
+ for (Iterator i = arguments.iterator(); i.hasNext(); ) {
+ argument = (ToolArgument)i.next();
+ item = new JMenuItem(argument.getName());
+ item.setToolTipText(argument.getDescription());
+ item.addActionListener(argument);
+ params.add(item);
+ }
+ menubar.add(params);
+ }
+ return menubar;
+ }
+
+ /**
+ * Gets the usage of the tool.
+ * @return a String describing how to use the tool.
+ */
+ public String getUsage() {
+ StringBuffer buf = new StringBuffer("java ");
+ buf.append(getClass().getName());
+ ToolArgument argument;
+ for (Iterator i = arguments.iterator(); i.hasNext(); ) {
+ argument = (ToolArgument) i.next();
+ buf.append(" ");
+ buf.append(argument.getName());
+ }
+ buf.append("\n");
+ for (Iterator i = arguments.iterator(); i.hasNext(); ) {
+ argument = (ToolArgument) i.next();
+ buf.append(argument.getUsage());
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Gets the current arguments of the tool.
+ * @return a String with the list of arguments and their values.
+ */
+ public String getArgs() {
+ StringBuffer buf = new StringBuffer("Current arguments:\n");
+ ToolArgument argument;
+ for (Iterator i = arguments.iterator(); i.hasNext(); ) {
+ argument = (ToolArgument) i.next();
+ buf.append(" ");
+ buf.append(argument.getName());
+ if (argument.getValue() == null) {
+ buf.append(" = null\n");
+ }
+ else {
+ buf.append(" = '");
+ buf.append(argument.getValue());
+ buf.append("'\n");
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+ */
+ public void actionPerformed(ActionEvent evt) {
+ if (CLOSE.equals(evt.getActionCommand())) {
+ System.out.println("=== " + getInternalFrame().getTitle() + " CLOSED ===");
+ internalFrame.dispose();
+ }
+ if (USAGE.equals(evt.getActionCommand())) {
+ JOptionPane.showMessageDialog(internalFrame, getUsage());
+ }
+ if (ARGUMENTS.equals(evt.getActionCommand())) {
+ JOptionPane.showMessageDialog(internalFrame, getArgs());
+ }
+ if (EXECUTE.equals(evt.getActionCommand())) {
+ this.execute();
+ }
+ if (EXECUTESHOW.equals(evt.getActionCommand())) {
+ this.execute();
+ try {
+ Executable.openDocument(getDestPathPDF());
+ } catch (Exception e) {
+ System.err.println(e.getMessage());
+ }
+ }
+ if (EXECUTEPRINT.equals(evt.getActionCommand())) {
+ this.execute();
+ try {
+ Executable.printDocument(getDestPathPDF());
+ } catch (Exception e) {
+ System.err.println(e.getMessage());
+ }
+ }
+ if (EXECUTEPRINTSILENT.equals(evt.getActionCommand())) {
+ this.execute();
+ try {
+ Executable.printDocumentSilent(getDestPathPDF());
+ } catch (Exception e) {
+ System.err.println(e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Gets the PDF file that should be generated (or null if the output isn't a PDF file).
+ * @return the PDF file that should be generated
+ * @throws InstantiationException
+ */
+ protected abstract File getDestPathPDF() throws InstantiationException;
+
+ /**
+ * Creates the internal frame.
+ */
+ protected abstract void createFrame();
+
+ /**
+ * Executes the tool (in most cases this generates a PDF file).
+ */
+ public abstract void execute();
+
+ /**
+ * Indicates that the value of an argument has changed.
+ * @param arg the argument that has changed
+ */
+ public abstract void valueHasChanged(ToolArgument arg);
+
+ /**
+ * Add the version of the plugin to the versions array.
+ * @param version the version to add.
+ */
+ protected static void addVersion(String version) {
+ version = version.substring(5, version.length() - 7);
+ version = version.substring(0, version.lastIndexOf(" "));
+ versionsarray.add(version);
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/Bookmarks2XML.java b/src/main/java/com/lowagie/tools/plugins/Bookmarks2XML.java
new file mode 100644
index 0000000..444a775
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/Bookmarks2XML.java
@@ -0,0 +1,148 @@
+/*
+ * $Id: Bookmarks2XML.java,v 1.1 2006/02/02 15:56:53 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Hans-Werner Hilse.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.List;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.SimpleBookmark;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Allows you to add bookmarks to an existing PDF file
+ */
+public class Bookmarks2XML extends AbstractTool {
+
+ static {
+ addVersion("$Id: Bookmarks2XML.java,v 1.1 2006/02/02 15:56:53 blowagie Exp $");
+ }
+
+ /**
+ * Constructs an Bookmarks2XML object.
+ */
+ public Bookmarks2XML() {
+ arguments.add(new FileArgument(this, "pdffile", "the PDF from which you want to extract bookmarks", false, new PdfFilter()));
+ arguments.add(new FileArgument(this, "xmlfile", "the resulting bookmarks file in XML", true));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Bookmarks2XML", true, true, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Bookmarks2XML OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("xmlfile") == null) throw new InstantiationException("You need to choose an xml file");
+ if (getValue("pdffile") == null) throw new InstantiationException("You need to choose a source PDF file");
+ PdfReader reader = new PdfReader(((File)getValue("pdffile")).getAbsolutePath());
+ reader.consolidateNamedDestinations();
+ List bookmarks = SimpleBookmark.getBookmark( reader );
+ // save them in XML format
+ FileOutputStream bmWriter = new FileOutputStream( (File)getValue("xmlfile") );
+ SimpleBookmark.exportToXML(bookmarks, bmWriter, "UTF-8", false);
+ bmWriter.close();
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Allows you to generate an index file in HTML containing Bookmarks to an existing PDF file.
+ * @param args
+ */
+ public static void main(String[] args) {
+ Bookmarks2XML tool = new Bookmarks2XML();
+ if (args.length < 2) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ throw new InstantiationException("There is no file to show.");
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/Burst.java b/src/main/java/com/lowagie/tools/plugins/Burst.java
new file mode 100644
index 0000000..1aca12a
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/Burst.java
@@ -0,0 +1,140 @@
+/*
+ * $Id: Burst.java,v 1.8 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * This code is free software. It may only be copied or modified
+ * if you include the following copyright notice:
+ *
+ * This class by Mark Thompson. Copyright (c) 2002 Mark Thompson.
+ *
+ * This code 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.
+ *
+ * itext-questions@list.sourceforge.net
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfImportedPage;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.LabelAccessory;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * This tool lets you split a PDF in several separate PDF files (1 per page).
+ */
+public class Burst extends AbstractTool {
+
+ static {
+ addVersion("$Id: Burst.java,v 1.8 2005/11/29 21:05:02 blowagie Exp $");
+ }
+
+ /**
+ * Constructs a Burst object.
+ */
+ public Burst() {
+ FileArgument f = new FileArgument(this, "srcfile", "The file you want to split", false, new PdfFilter());
+ f.setLabel(new LabelAccessory());
+ arguments.add(f);
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Burst", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Burst OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) throw new InstantiationException("You need to choose a sourcefile");
+ File src = (File)getValue("srcfile");
+ File directory = src.getParentFile();
+ String name = src.getName();
+ name = name.substring(0, name.lastIndexOf("."));
+ // we create a reader for a certain document
+ PdfReader reader = new PdfReader(src.getAbsolutePath());
+ // we retrieve the total number of pages
+ int n = reader.getNumberOfPages();
+ int digits = 1 + (n / 10);
+ System.out.println("There are " + n + " pages in the original file.");
+ Document document;
+ int pagenumber;
+ String filename;
+ for (int i = 0; i < n; i++) {
+ pagenumber = i + 1;
+ filename = String.valueOf(pagenumber);
+ while (filename.length() < digits) filename = "0" + filename;
+ filename = "_" + filename + ".pdf";
+ // step 1: creation of a document-object
+ document = new Document(reader.getPageSizeWithRotation(pagenumber));
+ // step 2: we create a writer that listens to the document
+ PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(new File(directory, name + filename)));
+ // step 3: we open the document
+ document.open();
+ PdfContentByte cb = writer.getDirectContent();
+ PdfImportedPage page = writer.getImportedPage(reader, pagenumber);
+ int rotation = reader.getPageRotation(pagenumber);
+ if (rotation == 90 || rotation == 270) {
+ cb.addTemplate(page, 0, -1f, 1f, 0, 0, reader.getPageSizeWithRotation(pagenumber).height());
+ }
+ else {
+ cb.addTemplate(page, 1f, 0, 0, 1f, 0, 0);
+ }
+ // step 5: we close the document
+ document.close();
+ }
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+
+ /**
+ * Divide PDF file into pages.
+ * @param args
+ */
+ public static void main(String[] args) {
+ Burst tool = new Burst();
+ if (args.length < 1) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ throw new InstantiationException("There is more than one destfile.");
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/Concat.java b/src/main/java/com/lowagie/tools/plugins/Concat.java
new file mode 100644
index 0000000..b6eb369
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/Concat.java
@@ -0,0 +1,185 @@
+/*
+ * $Id: Concat.java,v 1.7 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JInternalFrame;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.pdf.PdfCopy;
+import com.lowagie.text.pdf.PdfImportedPage;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.SimpleBookmark;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Concatenates two PDF files
+ */
+public class Concat extends AbstractTool {
+
+ static {
+ addVersion("$Id: Concat.java,v 1.7 2005/11/29 21:05:02 blowagie Exp $");
+ }
+ /**
+ * Constructs a Concat object.
+ */
+ public Concat() {
+ menuoptions = MENU_EXECUTE | MENU_EXECUTE_SHOW;
+ arguments.add(new FileArgument(this, "srcfile1", "The first PDF file", false, new PdfFilter()));
+ arguments.add(new FileArgument(this, "srcfile2", "The second PDF file", false, new PdfFilter()));
+ arguments.add(new FileArgument(this, "destfile", "The file to which the concatenated PDF has to be written", true, new PdfFilter()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Concatenate 2 PDF files", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Concat OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ String[] files = new String[2];
+ if (getValue("srcfile1") == null) throw new InstantiationException("You need to choose a first sourcefile");
+ files[0] = ((File)getValue("srcfile1")).getAbsolutePath();
+ if (getValue("srcfile2") == null) throw new InstantiationException("You need to choose a second sourcefile");
+ files[1] = ((File)getValue("srcfile2")).getAbsolutePath();
+ if (getValue("destfile") == null) throw new InstantiationException("You need to choose a destination file");
+ File pdf_file = (File)getValue("destfile");
+ int pageOffset = 0;
+ ArrayList master = new ArrayList();
+ Document document = null;
+ PdfCopy writer = null;
+ for (int i = 0; i < 2; i++) {
+ // we create a reader for a certain document
+ PdfReader reader = new PdfReader(files[i]);
+ reader.consolidateNamedDestinations();
+ // we retrieve the total number of pages
+ int n = reader.getNumberOfPages();
+ List bookmarks = SimpleBookmark.getBookmark(reader);
+ if (bookmarks != null) {
+ if (pageOffset != 0)
+ SimpleBookmark.shiftPageNumbers(bookmarks, pageOffset, null);
+ master.addAll(bookmarks);
+ }
+ pageOffset += n;
+ System.out.println("There are " + n + " pages in " + files[i]);
+ if (i == 0) {
+ // step 1: creation of a document-object
+ document = new Document(reader.getPageSizeWithRotation(1));
+ // step 2: we create a writer that listens to the document
+ writer = new PdfCopy(document, new FileOutputStream(pdf_file));
+ // step 3: we open the document
+ document.open();
+ }
+ // step 4: we add content
+ PdfImportedPage page;
+ for (int p = 0; p < n; ) {
+ ++p;
+ page = writer.getImportedPage(reader, p);
+ writer.addPage(page);
+ System.out.println("Processed page " + p);
+ }
+ }
+ if (master.size() > 0)
+ writer.setOutlines(master);
+ // step 5: we close the document
+ document.close();
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+
+ /**
+ * Concatenates two PDF files.
+ * @param args
+ */
+ public static void main(String[] args) {
+ Concat tool = new Concat();
+ if (args.length < 2) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile");
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/Decrypt.java b/src/main/java/com/lowagie/tools/plugins/Decrypt.java
new file mode 100644
index 0000000..a94b84f
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/Decrypt.java
@@ -0,0 +1,148 @@
+/*
+ * $Id: Decrypt.java,v 1.4 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfStamper;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Allows you to decrypt an existing PDF file.
+ */
+public class Decrypt extends AbstractTool {
+
+ static {
+ addVersion("$Id: Decrypt.java,v 1.4 2005/11/29 21:05:02 blowagie Exp $");
+ }
+
+
+ /**
+ * Constructs an Decrypt object.
+ */
+ public Decrypt() {
+ arguments.add(new FileArgument(this, "srcfile", "The file you want to decrypt", false, new PdfFilter()));
+ arguments.add(new FileArgument(this, "destfile", "The file to which the decrypted PDF has to be written", true, new PdfFilter()));
+ arguments.add(new ToolArgument(this, "ownerpassword", "The ownerpassword you want to add to the PDF file", String.class.getName()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Decrypt", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Decrypt OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) throw new InstantiationException("You need to choose a sourcefile");
+ if (getValue("destfile") == null) throw new InstantiationException("You need to choose a destination file");
+ byte[] ownerpassword = null;
+ if (getValue("ownerpassword") != null) {
+ ownerpassword = ((String)getValue("ownerpassword")).getBytes();
+ }
+ PdfReader reader = new PdfReader(((File)getValue("srcfile")).getAbsolutePath(), ownerpassword);
+ PdfStamper stamper = new PdfStamper(reader, new FileOutputStream((File)getValue("destfile")));
+ stamper.close();
+ }
+ catch(Exception e) {
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Decrypts an existing PDF file.
+ * @param args
+ */
+ public static void main(String[] args) {
+ Decrypt tool = new Decrypt();
+ if (args.length < 2) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile");
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/DvdCover.java b/src/main/java/com/lowagie/tools/plugins/DvdCover.java
new file mode 100644
index 0000000..718ec6d
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/DvdCover.java
@@ -0,0 +1,201 @@
+/*
+ * $Id: DvdCover.java,v 1.11 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.awt.Color;
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.Image;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.BaseFont;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.ImageArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * This is a simple tool that generates a cover for a DVD.
+ */
+public class DvdCover extends AbstractTool {
+
+ static {
+ addVersion("$Id: DvdCover.java,v 1.11 2005/11/29 21:05:02 blowagie Exp $");
+ }
+
+ /**
+ * Constructs a DvdCover object.
+ */
+ public DvdCover() {
+ menuoptions = MENU_EXECUTE | MENU_EXECUTE_SHOW | MENU_EXECUTE_PRINT;
+ arguments.add(new FileArgument(this, "destfile", "The file to which the PDF has to be written", true, new PdfFilter()));
+ arguments.add(new ToolArgument(this, "title", "The title of the DVD", String.class.getName()));
+ arguments.add(new ToolArgument(this, "backgroundcolor", "The backgroundcolor of the DVD Cover (for instance 0xFFFFFF)", Color.class.getName()));
+ arguments.add(new ImageArgument(this, "front", "The front image of the DVD Cover"));
+ arguments.add(new ImageArgument(this, "back", "The back image of the DVD Cover"));
+ arguments.add(new ImageArgument(this, "side", "The side image of the DVD Cover"));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Make your own DVD Cover", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== DvdCover OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ // step 1: creation of a document-object
+ Rectangle pageSize = new Rectangle(780, 525);
+ if (getValue("backgroundcolor") != null) pageSize.setBackgroundColor((Color)getValue("backgroundcolor"));
+ Document document = new Document(pageSize);
+ // step 2:
+ // we create a writer that listens to the document
+ // and directs a PDF-stream to a file
+ if (getValue("destfile") == null) throw new DocumentException("You must provide a destination file!");
+ PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream((File)getValue("destfile")));
+
+ // step 3: we open the document
+ document.open();
+
+ // step 4:
+ PdfContentByte cb = writer.getDirectContent();
+ if (getValue("title") != null) {
+ cb.setFontAndSize(BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, false), 24);
+ cb.beginText();
+ if (getValue("front") == null) {
+ cb.showTextAligned(Element.ALIGN_CENTER, (String)getValue("title"), 595f, 262f, 0f);
+ }
+ if (getValue("side") == null) {
+ cb.showTextAligned(Element.ALIGN_CENTER, (String)getValue("title"), 385f, 262f, 270f);
+ }
+ cb.endText();
+ }
+ cb.moveTo(370, 0);
+ cb.lineTo(370, 525);
+ cb.moveTo(410, 525);
+ cb.lineTo(410, 0);
+ cb.stroke();
+ if (getValue("front") != null) {
+ Image front = (Image)getValue("front");
+ front.scaleToFit(370, 525);
+ front.setAbsolutePosition(410f + (370f - front.scaledWidth()) / 2f, (525f - front.scaledHeight()) / 2f);
+ document.add(front);
+ }
+ if (getValue("back") != null) {
+ Image back = (Image)getValue("back");
+ back.scaleToFit(370, 525);
+ back.setAbsolutePosition((370f - back.scaledWidth()) / 2f, (525f - back.scaledHeight()) / 2f);
+ document.add(back);
+ }
+ if (getValue("side") != null) {
+ Image side = (Image)getValue("side");
+ side.scaleToFit(40, 525);
+ side.setAbsolutePosition(370 + (40f - side.scaledWidth()) / 2f, (525f - side.scaledHeight()) / 2f);
+ document.add(side);
+ }
+
+ // step 5: we close the document
+ document.close();
+ }
+ catch(Exception e) {
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Generates a DVD Cover in PDF.
+ * @param args an array containing [0] a filename [1] a title [2] a backgroundcolor [3] a front image [4] a back image [5] a side image
+ */
+ public static void main(String[] args) {
+ DvdCover tool = new DvdCover();
+ if (args.length == 0) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/tools/plugins/Encrypt.java b/src/main/java/com/lowagie/tools/plugins/Encrypt.java
new file mode 100644
index 0000000..7821759
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/Encrypt.java
@@ -0,0 +1,193 @@
+/*
+ * $Id: Encrypt.java,v 1.11 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.pdf.PdfEncryptor;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.tools.arguments.BitsetArgument;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.OptionArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Allows you to encrypt an existing PDF file.
+ */
+public class Encrypt extends AbstractTool {
+
+ static {
+ addVersion("$Id: Encrypt.java,v 1.11 2005/11/29 21:05:02 blowagie Exp $");
+ }
+ private final static int PERMISSIONS[] = {
+ PdfWriter.AllowPrinting,
+ PdfWriter.AllowModifyContents,
+ PdfWriter.AllowCopy,
+ PdfWriter.AllowModifyAnnotations,
+ PdfWriter.AllowFillIn,
+ PdfWriter.AllowScreenReaders,
+ PdfWriter.AllowAssembly,
+ PdfWriter.AllowDegradedPrinting};
+ private final static String PERMISSION_OPTIONS[] = {
+ "AllowPrinting",
+ "AllowModifyContents",
+ "AllowCopy",
+ "AllowModifyAnnotations",
+ "AllowFillIn (128 bit only)",
+ "AllowScreenReaders (128 bit only)",
+ "AllowAssembly (128 bit only)",
+ "AllowDegradedPrinting (128 bit only)"
+ };
+
+
+ /**
+ * Constructs an Encrypt object.
+ */
+ public Encrypt() {
+ arguments.add(new FileArgument(this, "srcfile", "The file you want to encrypt", false, new PdfFilter()));
+ arguments.add(new FileArgument(this, "destfile", "The file to which the encrypted PDF has to be written", true, new PdfFilter()));
+ arguments.add(new ToolArgument(this, "ownerpassword", "The ownerpassword you want to add to the PDF file", String.class.getName()));
+ arguments.add(new ToolArgument(this, "userpassword", "The userpassword you want to add to the PDF file", String.class.getName()));
+ arguments.add(new BitsetArgument(this, "permissions", "Permissions on the file", PERMISSION_OPTIONS));
+ OptionArgument oa = new OptionArgument(this, "strength", "Strength of the encryption");
+ oa.addOption("40 bit encryption", "40");
+ oa.addOption("128 bit encryption", "128");
+ arguments.add(oa);
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Encrypt", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Encrypt OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) throw new InstantiationException("You need to choose a sourcefile");
+ if (getValue("destfile") == null) throw new InstantiationException("You need to choose a destination file");
+ int permissions = 0;
+ String p = (String)getValue("permissions");
+ if (p != null) {
+ for (int k = 0; k < p.length(); ++k) {
+ permissions |= (p.charAt(k) == '0' ? 0 : PERMISSIONS[k]);
+ }
+ }
+ byte[] userpassword = null;
+ if (getValue("userpassword") != null) {
+ userpassword = ((String)getValue("userpassword")).getBytes();
+ }
+ byte[] ownerpassword = null;
+ if (getValue("ownerpassword") != null) {
+ ownerpassword = ((String)getValue("ownerpassword")).getBytes();
+ }
+ PdfReader reader = new PdfReader(((File)getValue("srcfile")).getAbsolutePath());
+ PdfEncryptor.encrypt(
+ reader,
+ new FileOutputStream((File)getValue("destfile")),
+ userpassword,
+ ownerpassword,
+ permissions,
+ "128".equals(getValue("strength"))
+ );
+ }
+ catch(Exception e) {
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Encrypts an existing PDF file.
+ * @param args
+ */
+ public static void main(String[] args) {
+ Encrypt tool = new Encrypt();
+ if (args.length < 2) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile");
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/ExtractAttachments.java b/src/main/java/com/lowagie/tools/plugins/ExtractAttachments.java
new file mode 100644
index 0000000..455f3a2
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/ExtractAttachments.java
@@ -0,0 +1,227 @@
+/*
+ * $Id: ExtractAttachments.java,v 1.5 2006/03/01 11:41:16 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Paulo Soares and Anonymous.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.swing.JInternalFrame;
+
+import com.lowagie.text.pdf.*;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.LabelAccessory;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * This tool lets you extract the attachements of a PDF.
+ */
+public class ExtractAttachments extends AbstractTool {
+
+ static {
+ addVersion("$Id: ExtractAttachments.java,v 1.5 2006/03/01 11:41:16 blowagie Exp $");
+ }
+
+ /**
+ * Constructs a ExtractAttachements object.
+ */
+ public ExtractAttachments() {
+ FileArgument f = new FileArgument(this, "srcfile",
+ "The file you want to operate on", false, new PdfFilter());
+ f.setLabel(new LabelAccessory());
+ arguments.add(f);
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("ExtractAttachments", true, false,
+ true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== ExtractAttachments OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null)
+ throw new InstantiationException(
+ "You need to choose a sourcefile");
+ File src = (File) getValue("srcfile");
+
+ // we create a reader for a certain document
+ PdfReader reader = new PdfReader(src.getAbsolutePath());
+ final File parentFile = src.getParentFile();
+ final String outPath;
+ if (parentFile != null) {
+ outPath = parentFile.getAbsolutePath();
+ } else {
+ outPath = "";
+ }
+ PdfDictionary catalog = reader.getCatalog();
+ PdfDictionary names = (PdfDictionary) PdfReader
+ .getPdfObject(catalog.get(PdfName.NAMES));
+ if (names != null) {
+ PdfDictionary embFiles = (PdfDictionary) PdfReader
+ .getPdfObject(names.get(new PdfName("EmbeddedFiles")));
+ if (embFiles != null) {
+ HashMap embMap = PdfNameTree.readTree(embFiles);
+ for (Iterator i = embMap.values().iterator(); i.hasNext();) {
+ PdfDictionary filespec = (PdfDictionary) PdfReader
+ .getPdfObject((PdfObject) i.next());
+ unpackFile(reader, filespec, outPath);
+ }
+ }
+ }
+ for (int k = 1; k <= reader.getNumberOfPages(); ++k) {
+ PdfArray annots = (PdfArray) PdfReader.getPdfObject(reader
+ .getPageN(k).get(PdfName.ANNOTS));
+ if (annots == null)
+ continue;
+ for (Iterator i = annots.listIterator(); i.hasNext();) {
+ PdfDictionary annot = (PdfDictionary) PdfReader
+ .getPdfObject((PdfObject) i.next());
+ PdfName subType = (PdfName) PdfReader.getPdfObject(annot
+ .get(PdfName.SUBTYPE));
+ if (!PdfName.FILEATTACHMENT.equals(subType))
+ continue;
+ PdfDictionary filespec = (PdfDictionary) PdfReader
+ .getPdfObject(annot.get(PdfName.FS));
+ unpackFile(reader, filespec, outPath);
+ }
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the
+ // commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Extract the attachements of a PDF.
+ *
+ * @param args
+ */
+ public static void main(String[] args) {
+ ExtractAttachments tool = new ExtractAttachments();
+ if (args.length < 1) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ throw new InstantiationException("There is more than one destfile.");
+ }
+
+ /**
+ * Unpacks a file attachment.
+ *
+ * @param reader
+ * The object that reads the PDF document
+ * @param filespec
+ * The dictonary containing the file specifications
+ * @param outPath
+ * The path where the attachment has to be written
+ * @throws IOException
+ */
+ public static void unpackFile(PdfReader reader, PdfDictionary filespec,
+ String outPath) throws IOException {
+ if (filespec == null)
+ return;
+ PdfName type = (PdfName) PdfReader.getPdfObject(filespec
+ .get(PdfName.TYPE));
+ if (!PdfName.F.equals(type) && !PdfName.FILESPEC.equals(type))
+ return;
+ PdfDictionary ef = (PdfDictionary) PdfReader.getPdfObject(filespec
+ .get(PdfName.EF));
+ if (ef == null)
+ return;
+ PdfString fn = (PdfString) PdfReader.getPdfObject(filespec
+ .get(PdfName.F));
+ System.out.println("Unpacking file '" + fn + "' to " + outPath);
+ if (fn == null)
+ return;
+ File fLast = new File(fn.toUnicodeString());
+ File fullPath = new File(outPath, fLast.getName());
+ if (fullPath.exists())
+ return;
+ PRStream prs = (PRStream) PdfReader.getPdfObject(ef.get(PdfName.F));
+ if (prs == null)
+ return;
+ byte b[] = PdfReader.getStreamBytes(prs);
+ FileOutputStream fout = new FileOutputStream(fullPath);
+ fout.write(b);
+ fout.close();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/tools/plugins/Handouts.java b/src/main/java/com/lowagie/tools/plugins/Handouts.java
new file mode 100644
index 0000000..fed8788
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/Handouts.java
@@ -0,0 +1,232 @@
+/*
+ * $Id: Handouts.java,v 1.7 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.PageSize;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfImportedPage;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.OptionArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Generates a PDF file that is useable as Handout.
+ */
+public class Handouts extends AbstractTool {
+
+ static {
+ addVersion("$Id: Handouts.java,v 1.7 2005/11/29 21:05:02 blowagie Exp $");
+ }
+
+ /**
+ * Constructs a Handouts object.
+ */
+ public Handouts() {
+ arguments.add(new FileArgument(this, "srcfile", "The file you want to convert", false, new PdfFilter()));
+ arguments.add(new FileArgument(this, "destfile", "The file to which the Handout has to be written", true, new PdfFilter()));
+ OptionArgument oa = new OptionArgument(this, "pages", "The number of pages you want on one handout page");
+ oa.addOption("2 pages on 1", "2");
+ oa.addOption("3 pages on 1", "3");
+ oa.addOption("4 pages on 1", "4");
+ oa.addOption("5 pages on 1", "5");
+ oa.addOption("6 pages on 1", "6");
+ oa.addOption("7 pages on 1", "7");
+ oa.addOption("8 pages on 1", "8");
+ arguments.add(oa);
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Handouts", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Handouts OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) throw new InstantiationException("You need to choose a sourcefile");
+ File src = (File)getValue("srcfile");
+ if (getValue("destfile") == null) throw new InstantiationException("You need to choose a destination file");
+ File dest = (File)getValue("destfile");
+ int pages;
+ try {
+ pages = Integer.parseInt((String) getValue("pages"));
+ }
+ catch(Exception e) {
+ pages = 4;
+ }
+
+ float x1 = 30f;
+ float x2 = 280f;
+ float x3 = 320f;
+ float x4 = 565f;
+
+ float[] y1 = new float[pages];
+ float[] y2 = new float[pages];
+
+ float height = (778f - (20f * (pages - 1))) / pages;
+ y1[0] = 812f;
+ y2[0] = 812f - height;
+
+ for (int i = 1; i < pages; i++) {
+ y1[i] = y2[i - 1] - 20f;
+ y2[i] = y1[i] - height;
+ }
+
+ // we create a reader for a certain document
+ PdfReader reader = new PdfReader(src.getAbsolutePath());
+ // we retrieve the total number of pages
+ int n = reader.getNumberOfPages();
+ System.out.println("There are " + n + " pages in the original file.");
+
+ // step 1: creation of a document-object
+ Document document = new Document(PageSize.A4);
+ // step 2: we create a writer that listens to the document
+ PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
+ // step 3: we open the document
+ document.open();
+ PdfContentByte cb = writer.getDirectContent();
+ PdfImportedPage page;
+ int rotation;
+ int i = 0;
+ int p = 0;
+ // step 4: we add content
+ while (i < n) {
+ i++;
+ Rectangle rect = reader.getPageSizeWithRotation(i);
+ float factorx = (x2 - x1) / rect.width();
+ float factory = (y1[p] - y2[p]) / rect.height();
+ float factor = (factorx < factory ? factorx : factory);
+ float dx = (factorx == factor ? 0f : ((x2 - x1) - rect.width() * factor) / 2f);
+ float dy = (factory == factor ? 0f : ((y1[p] - y2[p]) - rect.height() * factor) / 2f);
+ page = writer.getImportedPage(reader, i);
+ rotation = reader.getPageRotation(i);
+ if (rotation == 90 || rotation == 270) {
+ cb.addTemplate(page, 0, -factor, factor, 0, x1 + dx, y2[p] + dy + rect.height() * factor);
+ }
+ else {
+ cb.addTemplate(page, factor, 0, 0, factor, x1 + dx, y2[p] + dy);
+ }
+ cb.setRGBColorStroke(0xC0, 0xC0, 0xC0);
+ cb.rectangle(x3 - 5f, y2[p] - 5f, x4 - x3 + 10f, y1[p] - y2[p] + 10f);
+ for (float l = y1[p] - 19; l > y2[p]; l -= 16) {
+ cb.moveTo(x3, l);
+ cb.lineTo(x4, l);
+ }
+ cb.rectangle(x1 + dx, y2[p] + dy, rect.width() * factor, rect.height() * factor);
+ cb.stroke();
+ System.out.println("Processed page " + i);
+ p++;
+ if (p == pages) {
+ p = 0;
+ document.newPage();
+ }
+ }
+ // step 5: we close the document
+ document.close();
+ }
+ catch(Exception e) {
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+
+ /**
+ * Converts a PDF file to a PDF file useable as Handout.
+ * @param args
+ */
+ public static void main(String[] args) {
+ Handouts tool = new Handouts();
+ if (args.length < 2) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile");
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/HtmlBookmarks.java b/src/main/java/com/lowagie/tools/plugins/HtmlBookmarks.java
new file mode 100644
index 0000000..aa8966f
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/HtmlBookmarks.java
@@ -0,0 +1,288 @@
+/*
+ * $Id: HtmlBookmarks.java,v 1.12 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.Anchor;
+import com.lowagie.text.Chapter;
+import com.lowagie.text.Chunk;
+import com.lowagie.text.Document;
+import com.lowagie.text.Header;
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.Section;
+import com.lowagie.text.html.HtmlTags;
+import com.lowagie.text.html.HtmlWriter;
+import com.lowagie.text.markup.MarkupTags;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.SimpleBookmark;
+import com.lowagie.tools.Executable;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Allows you to generate an index file in HTML containing Bookmarks to an existing PDF file.
+ */
+public class HtmlBookmarks extends AbstractTool {
+
+ static {
+ addVersion("$Id: HtmlBookmarks.java,v 1.12 2005/11/29 21:05:02 blowagie Exp $");
+ }
+
+ /**
+ * Constructs an HtmlBookmarks object.
+ */
+ public HtmlBookmarks() {
+ arguments.add(new FileArgument(this, "srcfile", "The file you want to inspect", false, new PdfFilter()));
+ arguments.add(new ToolArgument(this, "ownerpassword", "The owner password if the file is encrypt", String.class.getName()));
+ arguments.add(new ToolArgument(this, "css", "The path to a CSS file", String.class.getName()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Html Bookmarks", true, true, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Html Bookmarks OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) throw new InstantiationException("You need to choose a sourcefile");
+ File src = (File)getValue("srcfile");
+ PdfReader reader;
+ if (getValue("ownerpassword") == null) {
+ reader = new PdfReader(src.getAbsolutePath());
+ }
+ else {
+ reader = new PdfReader(src.getAbsolutePath(), ((String)getValue("ownerpassword")).getBytes());
+ }
+ File directory = src.getParentFile();
+ String name = src.getName();
+ name = name.substring(0, name.lastIndexOf("."));
+ File html = new File(directory, name + "_index.html");
+ Document document = new Document();
+ HtmlWriter.getInstance(document, new FileOutputStream(html));
+ Object css = getValue("css");
+ if (css != null) {
+ document.add(new Header(HtmlTags.STYLESHEET, css.toString()));
+ }
+ Object title = reader.getInfo().get("Title");
+ if (title == null)
+ document.addTitle("Index for " + src.getName());
+ else
+ document.addKeywords("Index for '" + title + "'");
+ Object keywords = reader.getInfo().get("Keywords");
+ if (keywords != null)
+ document.addKeywords((String)keywords);
+ Object description = reader.getInfo().get("Subject");
+ if (keywords != null)
+ document.addSubject((String)description);
+ document.open();
+ Paragraph t;
+ if (title == null)
+ t = new Paragraph("Index for " + src.getName());
+ else
+ t = new Paragraph("Index for '" + title + "'");
+ t.setMarkupAttribute(MarkupTags.HTML_ATTR_CSS_CLASS, "title");
+ document.add(t);
+ if (description != null) {
+ Paragraph d = new Paragraph((String) description);
+ d.setMarkupAttribute(MarkupTags.HTML_ATTR_CSS_CLASS, "description");
+ document.add(d);
+ }
+ List list = SimpleBookmark.getBookmark(reader);
+ if (list == null) {
+ document.add(new Paragraph("This document has no bookmarks."));
+ }
+ else {
+ HashMap c;
+ for (Iterator i = list.iterator(); i.hasNext(); ) {
+ c = (HashMap) i.next();
+ Chapter chapter = (Chapter)createBookmark(src.getName(), null, c);
+ ArrayList kids = (ArrayList) c.get("Kids");
+ if (kids != null) {
+ for (Iterator k = kids.iterator(); k.hasNext(); ) {
+ addBookmark(src.getName(), chapter, (HashMap)k.next());
+ }
+ }
+ document.add(chapter);
+ }
+ }
+ document.close();
+ Executable.launchBrowser(html.getAbsolutePath());
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Recursive method to write Bookmark titles to the System.out.
+ * @param pdf the path to the PDF file
+ * @param section the section to which the bookmarks should be added
+ * @param bookmark a HashMap containing a Bookmark (and possible kids)
+ */
+ private static void addBookmark(String pdf, Section section, HashMap bookmark) {
+ Section s = createBookmark(pdf, section, bookmark);
+ ArrayList kids = (ArrayList) bookmark.get("Kids");
+ if (kids == null) return;
+ for (Iterator i = kids.iterator(); i.hasNext(); ) {
+ addBookmark(pdf, s, (HashMap)i.next());
+ }
+ }
+
+ /**
+ * Adds a line with the title and an anchor.
+ * @param pdf the link to the PDF file
+ * @param section the section that gets the line
+ * @param bookmark the bookmark that has the data for the line
+ * @return a subsection of section
+ */
+ private static Section createBookmark(String pdf, Section section, HashMap bookmark) {
+ Section s;
+ Paragraph title = new Paragraph((String)bookmark.get("Title"));
+ System.out.println((String)bookmark.get("Title"));
+ String action = (String)bookmark.get("Action");
+ if ("GoTo".equals(action)) {
+ if (bookmark.get("Page") != null) {
+ String page = (String)bookmark.get("Page");
+ StringTokenizer tokens = new StringTokenizer(page);
+ String token = tokens.nextToken();
+ Anchor anchor = new Anchor(" page" + token);
+ anchor.setReference(pdf + "#page=" + token);
+ title.add(anchor);
+ }
+ }
+ else if ("URI".equals(action)) {
+ String url = (String)bookmark.get("URI");
+ Anchor anchor = new Anchor(" Goto URL");
+ anchor.setReference(url);
+ title.add(anchor);
+ }
+ else if ("GoToR".equals(action)) {
+ String remote = (String)bookmark.get("File");
+ Anchor anchor = new Anchor(" goto " + remote);
+ if (bookmark.get("Named") != null) {
+ String named = (String)bookmark.get("Named");
+ remote = remote + "#nameddest=" + named;
+ }
+ else if (bookmark.get("Page") != null) {
+ String page = (String)bookmark.get("Page");
+ StringTokenizer tokens = new StringTokenizer(page);
+ String token = tokens.nextToken();
+ anchor.add(new Chunk(" page " + token));
+ remote = remote + "#page=" + token;
+ }
+ anchor.setReference(remote);
+ title.add(anchor);
+ }
+ if (section == null) {
+ s = new Chapter(title, 0);
+ }
+ else {
+ s = section.addSection(title);
+ }
+ s.setNumberDepth(0);
+ return s;
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Allows you to generate an index file in HTML containing Bookmarks to an existing PDF file.
+ * @param args
+ */
+ public static void main(String[] args) {
+ HtmlBookmarks tool = new HtmlBookmarks();
+ if (args.length < 1) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ throw new InstantiationException("There is no file to show.");
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/ImageXRefViewer.java b/src/main/java/com/lowagie/tools/plugins/ImageXRefViewer.java
new file mode 100644
index 0000000..34bbc50
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/ImageXRefViewer.java
@@ -0,0 +1,256 @@
+/*
+ * $Id: ImageXRefViewer.java,v 1.3 2006/04/05 07:55:00 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Carsten Hammer and Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999-2006 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000-2006 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Toolkit;
+import java.io.File;
+
+import javax.swing.BorderFactory;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerModel;
+import javax.swing.SwingConstants;
+import javax.swing.event.ChangeEvent;
+
+import com.lowagie.text.pdf.PRStream;
+import com.lowagie.text.pdf.PdfName;
+import com.lowagie.text.pdf.PdfObject;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfStream;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+import javax.swing.JScrollPane;
+import com.lowagie.tools.SwingWorker;
+import java.io.*;
+import java.awt.Cursor;
+
+/**
+ * Allows you to inspect the Image XObjects inside a PDF file.
+ */
+public class ImageXRefViewer
+ extends AbstractTool {
+ static {
+ addVersion("$Id: ImageXRefViewer.java,v 1.3 2006/04/05 07:55:00 blowagie Exp $");
+ }
+
+ class ViewXRefImages_jSpinner1_propertyChangeAdapter
+ implements javax.swing.event.ChangeListener {
+ private ImageXRefViewer adaptee;
+ ViewXRefImages_jSpinner1_propertyChangeAdapter(ImageXRefViewer adaptee) {
+ this.adaptee = adaptee;
+ }
+ /**
+ * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
+ */
+ public void stateChanged(ChangeEvent e) {
+ adaptee.jSpinner_propertyChange(e);
+ }
+ }
+
+ JPanel jPanel1 = new JPanel();
+ BorderLayout borderLayout1 = new BorderLayout();
+ JLabel jLabel1 = new JLabel();
+ int picturenumber = 0;
+ JPanel jPanel2 = new JPanel();
+ BorderLayout borderLayout2 = new BorderLayout();
+ CardLayout cardLayout1 = new CardLayout();
+ JPanel jPanel3 = new JPanel();
+ JSpinner jSpinner1 = new JSpinner();
+ BorderLayout borderLayout3 = new BorderLayout();
+ SpinnerModel spinnerModel1 = jSpinner1.getModel();
+ JScrollPane jScrollPane1 = new JScrollPane();
+
+
+ /**
+ * Creates a ViewImageXObjects object.
+ */
+ public ImageXRefViewer() {
+ arguments.add(new FileArgument(this, "srcfile", "The file you want to inspect", false, new PdfFilter()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ throw new InstantiationException("There is no file to show.");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("View Image XObjects", true, false, true);
+ internalFrame.setSize(500, 300);
+ internalFrame.setJMenuBar(getMenubar());
+ internalFrame.getContentPane().setLayout(borderLayout1);
+ jPanel1.setLayout(borderLayout2);
+ jLabel1.setHorizontalAlignment(SwingConstants.CENTER);
+ jLabel1.setText("images");
+ jPanel2.setLayout(cardLayout1);
+ jPanel3.setLayout(borderLayout3);
+ jSpinner1.addChangeListener(new
+ ViewXRefImages_jSpinner1_propertyChangeAdapter(this));
+ jPanel2.setBorder(BorderFactory.createEtchedBorder());
+ internalFrame.getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER);
+ jPanel3.add(jSpinner1, java.awt.BorderLayout.CENTER);
+ jPanel3.add(jLabel1, java.awt.BorderLayout.NORTH);
+ jPanel1.add(jPanel3, java.awt.BorderLayout.NORTH);
+ jPanel1.add(jScrollPane1, java.awt.BorderLayout.CENTER);
+ jScrollPane1.setViewportView(jPanel2);
+ System.out.println("=== Image XObject Viewer OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ // do nothing
+ }
+
+ /**
+ * Reflects the change event in the JSpinner object.
+ * @param evt
+ */
+ public void jSpinner_propertyChange(ChangeEvent evt) {
+ int blatt = Integer.parseInt(jSpinner1.getValue().toString());
+ if (blatt < 0) blatt = 0;
+ if (blatt >= picturenumber) blatt = picturenumber - 1;
+ this.cardLayout1.show(jPanel2, String.valueOf(blatt));
+ jPanel2.repaint();
+ }
+ /**
+ * Shows the images that are added to the PDF as Image XObjects.
+ * @param args
+ */
+ public static void main(String[] args) {
+ InspectPDF tool = new InspectPDF();
+ if (args.length < 1) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ picturenumber = 0;
+ try {
+ if (getValue("srcfile") == null) throw new InstantiationException("You need to choose a sourcefile");
+
+ SwingWorker work= new SwingWorker(){
+
+ public Object construct() {
+ try {
+ PdfReader reader = new PdfReader( ( (File)
+ getValue("srcfile")).getAbsolutePath());
+ for (int i = 0; i < reader.getXrefSize(); i++) {
+ PdfObject pdfobj = reader.getPdfObject(i);
+ if (pdfobj != null) {
+ if (pdfobj.isStream()) {
+ PdfStream pdfdict = (PdfStream) pdfobj;
+ PdfObject pdfsubtype = pdfdict.get(PdfName.
+ SUBTYPE);
+ if (pdfsubtype == null) {
+ continue;
+ }
+ if (!pdfsubtype.toString().equals(PdfName.
+ IMAGE.toString())) {
+ continue;
+ }
+ System.out.println("picturenumber: " +
+ picturenumber);
+ System.out.println("height:" +
+ pdfdict.get(PdfName.HEIGHT));
+ System.out.println("width:" +
+ pdfdict.get(PdfName.WIDTH));
+ System.out.println("bitspercomponent:" +
+ pdfdict.get(PdfName.BITSPERCOMPONENT));
+ byte[] barr = PdfReader.getStreamBytesRaw( (
+ PRStream) pdfdict);
+ java.awt.Image im = Toolkit.
+ getDefaultToolkit().createImage(barr);
+ javax.swing.ImageIcon ii = new javax.swing.
+ ImageIcon(im);
+
+ JLabel jLabel1 = new JLabel();
+ jLabel1.setIcon(ii);
+ jPanel2.add(jLabel1, "" + picturenumber++);
+ }
+ }
+ }
+ }
+ catch (InstantiationException ex) {
+ }
+ catch (IOException ex) {
+ }
+ internalFrame.setCursor(Cursor.getDefaultCursor());
+ return null;
+ }
+ };
+ internalFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ work.start();
+ }
+ catch(Exception e) {
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/InspectPDF.java b/src/main/java/com/lowagie/tools/plugins/InspectPDF.java
new file mode 100644
index 0000000..b017221
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/InspectPDF.java
@@ -0,0 +1,173 @@
+/*
+ * $Id: InspectPDF.java,v 1.6 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.pdf.PdfEncryptor;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Allows you to inspect an existing PDF file.
+ */
+public class InspectPDF extends AbstractTool {
+ static {
+ addVersion("$Id: InspectPDF.java,v 1.6 2005/11/29 21:05:02 blowagie Exp $");
+ }
+
+ /**
+ * Constructs an InpectPDF object.
+ */
+ public InspectPDF() {
+ arguments.add(new FileArgument(this, "srcfile", "The file you want to inspect", false, new PdfFilter()));
+ arguments.add(new ToolArgument(this, "ownerpassword", "The owner password if the file is encrypt", String.class.getName()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Pdf Information", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Pdf Information OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) throw new InstantiationException("You need to choose a sourcefile");
+ PdfReader reader;
+ if (getValue("ownerpassword") == null) {
+ reader = new PdfReader(((File)getValue("srcfile")).getAbsolutePath());
+ }
+ else {
+ reader = new PdfReader(((File)getValue("srcfile")).getAbsolutePath(), ((String)getValue("ownerpassword")).getBytes());
+ }
+ // Some general document information and page size
+ System.out.println("=== Document Information ===");
+ System.out.println("PDF Version: " + reader.getPdfVersion());
+ System.out.println("Number of pages: " + reader.getNumberOfPages());
+ System.out.println("Number of PDF objects: " + reader.getXrefSize());
+ System.out.println("File length: " + reader.getFileLength());
+ System.out.println("Encrypted? " + reader.isEncrypted());
+ if (reader.isEncrypted()) {
+ System.out.println("Permissions: " + PdfEncryptor.getPermissionsVerbose(reader.getPermissions()));
+ System.out.println("128 bit? " + reader.is128Key());
+ }
+ System.out.println("Rebuilt? " + (!reader.isRebuilt()));
+ // Some metadata
+ System.out.println("=== Metadata ===");
+ HashMap info = reader.getInfo();
+ String key;
+ String value;
+ for (Iterator i = info.keySet().iterator(); i.hasNext(); ) {
+ key = (String) i.next();
+ value = (String) info.get(key);
+ System.out.println(key + ": " + value);
+ }
+ if (reader.getMetadata() == null) {
+ System.out.println("There is no XML Metadata in the file");
+ }
+ else {
+ System.out.println("XML Metadata: " + new String(reader.getMetadata()));
+ }
+ }
+ catch(Exception e) {
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Inspects an existing PDF file.
+ * @param args
+ */
+ public static void main(String[] args) {
+ InspectPDF tool = new InspectPDF();
+ if (args.length < 1) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ throw new InstantiationException("There is no file to show.");
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/KnitTiff.java b/src/main/java/com/lowagie/tools/plugins/KnitTiff.java
new file mode 100644
index 0000000..428bbb7
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/KnitTiff.java
@@ -0,0 +1,178 @@
+/*
+ * $Id: KnitTiff.java,v 1.7 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.Image;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.text.pdf.RandomAccessFileOrArray;
+import com.lowagie.text.pdf.codec.TiffImage;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.ImageFilter;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Knits two TIFF files, one with the even pages and another with the odd pages, together.
+ */
+public class KnitTiff extends AbstractTool {
+
+ static {
+ addVersion("$Id: KnitTiff.java,v 1.7 2005/11/29 21:05:02 blowagie Exp $");
+ }
+ /**
+ * Constructs a KnitTiff object.
+ */
+ public KnitTiff() {
+ menuoptions = MENU_EXECUTE | MENU_EXECUTE_SHOW;
+ arguments.add(new FileArgument(this, "odd", "The tiff file with the odd pages", false, new ImageFilter(false, false, false, false, false, true)));
+ arguments.add(new FileArgument(this, "even", "The tiff file with the even pages", false, new ImageFilter(false, false, false, false, false, true)));
+ arguments.add(new FileArgument(this, "destfile", "The file to which the converted TIFF has to be written", true, new PdfFilter()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("KnitTiff", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== KnitTiff OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("odd") == null) throw new InstantiationException("You need to choose a sourcefile for the odd pages");
+ File odd_file = (File)getValue("odd");
+ if (getValue("even") == null) throw new InstantiationException("You need to choose a sourcefile for the even pages");
+ File even_file = (File)getValue("even");
+ if (getValue("destfile") == null) throw new InstantiationException("You need to choose a destination file");
+ File pdf_file = (File)getValue("destfile");
+ RandomAccessFileOrArray odd = new RandomAccessFileOrArray(odd_file.getAbsolutePath());
+ RandomAccessFileOrArray even = new RandomAccessFileOrArray(even_file.getAbsolutePath());
+ Image img = TiffImage.getTiffImage(odd, 1);
+ Document document = new Document(new Rectangle(img.scaledWidth(),
+ img.scaledHeight()));
+ PdfWriter writer = PdfWriter.getInstance(document,
+ new FileOutputStream(pdf_file));
+ document.open();
+ PdfContentByte cb = writer.getDirectContent();
+ int count = Math.max(TiffImage.getNumberOfPages(odd), TiffImage
+ .getNumberOfPages(even));
+ for (int c = 0; c < count; ++c) {
+ try {
+ Image imgOdd = TiffImage.getTiffImage(odd, c + 1);
+ Image imgEven = TiffImage.getTiffImage(even, count - c);
+ document.setPageSize(new Rectangle(imgOdd.scaledWidth(),
+ imgOdd.scaledHeight()));
+ document.newPage();
+ imgOdd.setAbsolutePosition(0, 0);
+ cb.addImage(imgOdd);
+ document.setPageSize(new Rectangle(imgEven.scaledWidth(),
+ imgEven.scaledHeight()));
+ document.newPage();
+ imgEven.setAbsolutePosition(0, 0);
+ cb.addImage(imgEven);
+
+ } catch (Exception e) {
+ System.out.println("Exception page " + (c + 1) + " "
+ + e.getMessage());
+ }
+ }
+ odd.close();
+ even.close();
+ document.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+
+ /**
+ * Converts a tiff file to PDF.
+ * @param args
+ */
+ public static void main(String[] args) {
+ KnitTiff tool = new KnitTiff();
+ if (args.length < 3) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile");
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/LPRClient.java b/src/main/java/com/lowagie/tools/plugins/LPRClient.java
new file mode 100644
index 0000000..3214013
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/LPRClient.java
@@ -0,0 +1,245 @@
+/*
+ * $Id: LPRClient.java,v 1.4 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Anonymous.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+import com.lowagie.tools.LPR;
+
+/**
+ * Allows you to print an existing PDF file via lpr.
+ */
+public class LPRClient extends AbstractTool {
+ static {
+ addVersion("$Id: LPRClient.java,v 1.4 2005/11/29 21:05:02 blowagie Exp $");
+ }
+
+String fallback="%!PS\n"+
+ "/vpos 720 def\n"+
+ "/newline\n"+
+ " {\n"+
+ "/vpos vpos 15 sub def\n"+
+ "72 vpos moveto\n"+
+ "} def \n"+
+ "/printword\n"+
+ "{\n"+
+ "show\n"+
+ "newline\n"+
+ "vpos 100 le { \n"+
+ "showpage\n"+
+ "100 100 moveto\n"+
+ "/vpos 720 def\n"+
+ "/Helvetica findfont 15 scalefont setfont\n"+
+ "} if\n"+
+ "} def \n"+
+ "/nstr 9 string def\n"+
+ "/prt-n\n"+
+ "{\n"+
+ "nstr cvs printword\n"+
+ "} def\n"+
+ "100 100 moveto\n"+
+ "/Helvetica findfont 15 scalefont setfont\n"+
+ "(---) printword \n"+
+ "(Postscript Engine Testpage) printword\n"+
+ "() printword\n"+
+ "() printword\n"+
+ "(Defaultpagesize) printword\n"+
+ "currentpagedevice /PageSize get\n"+
+ "(Width: ) show \n"+
+ "0 get prt-n\n"+
+ "currentpagedevice /PageSize get\n"+
+ "(Height: ) show \n"+
+ "1 get prt-n\n"+
+ "() printword\n"+
+ "(Printerresolution) printword\n"+
+ "currentpagedevice /HWResolution get\n"+
+ "(X: ) show \n"+
+ "0 get prt-n\n"+
+ "currentpagedevice /HWResolution get\n"+
+ "(Y: ) show \n"+
+ "1 get prt-n\n"+
+ "() printword\n"+
+ "(Information about Postscriptengine) printword\n"+
+ "(Postscriptengine Type: ) show\n"+
+ "product printword\n"+
+ "(Version: ) show\n"+
+ "version printword\n"+
+ "() printword \n"+
+ "mark\n"+
+ "(\n) \n"+
+ "revision 10 mod \n"+
+ "revision 100 mod 10 idiv (.)\n"+
+ "revision 100 idiv \n"+
+ "(Revision: )\n"+
+ "(\n) \n"+
+ "counttomark\n"+
+ "{ 17 string cvs show\n"+
+ "} repeat pop \n"+
+ "() printword \n"+
+ "(Postscript Languagelevel: ) show\n"+
+ "/languagelevel where\n"+
+ "{pop languagelevel}\n"+
+ "{1}\n"+
+ "ifelse\n"+
+ "3 string cvs printword \n"+
+ "usertime \n"+
+ "prt-n \n"+
+ "vmstatus\n"+
+ "(Printerram Max.: ) show\n"+
+ "prt-n\n"+
+ "(Printerram Cur.: ) show\n"+
+ "prt-n\n"+
+ "() printword\n"+
+ "showpage";
+ /**
+ * Constructs an LPRClient object.
+ */
+ public LPRClient() {
+ arguments.add(new FileArgument(this, "srcfile",
+ "The file you want to print", false, new PdfFilter()));
+ arguments.add(new ToolArgument(this, "hostname",
+ "The hostname of the lpr server", String.class.getName()));
+ arguments.add(new ToolArgument(this, "queuename",
+ "The queuename of the lpr server", String.class.getName()));
+ arguments.add(new ToolArgument(this, "copies",
+ "The number of copies to print", String.class.getName()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("LPR", true, false, true);
+ internalFrame.setSize(500, 300);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== LPR OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ String filename=null;
+ File pdffile=null;
+ if (getValue("srcfile") == null){
+ filename=null;
+ }else{
+ filename = getValue("srcfile").toString();
+ pdffile = new File(filename);
+ }
+// throw new InstantiationException(
+// "You need to choose a sourcefile");
+ if (getValue("hostname") == null)
+ throw new InstantiationException(
+ "You need to choose a hostname");
+ if (getValue("queuename") == null)
+ throw new InstantiationException(
+ "You need to choose a queuename");
+ if (getValue("copies") == null)
+ throw new InstantiationException(
+ "You need to choose the number of copies");
+ LPR lpr = new LPR(getValue("hostname").toString(), System
+ .getProperty("user.name"));
+ lpr.setCopies(Integer.parseInt(getValue("copies").toString()));
+ if(filename==null){
+ lpr.print(getValue("queuename").toString(), fallback, "Version");
+ }else{
+ lpr.print(getValue("queuename").toString(), pdffile, pdffile
+ .getName());
+ }
+
+ } catch (Exception e) {
+ JOptionPane.showMessageDialog(internalFrame, e.getMessage(), e
+ .getClass().getName(), JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the
+ // commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Prints a PDF file via lpr.
+ *
+ * @param args
+ */
+ public static void main(String[] args) {
+ LPRClient tool = new LPRClient();
+ if (args.length < tool.getArguments().size()) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ throw new InstantiationException("There is no file to show.");
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/NUp.java b/src/main/java/com/lowagie/tools/plugins/NUp.java
new file mode 100644
index 0000000..7376213
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/NUp.java
@@ -0,0 +1,165 @@
+/*
+ * $Id: NUp.java,v 1.6 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * This code is free software. It may only be copied or modified
+ * if you include the following copyright notice:
+ *
+ * This class by Mark Thompson. Copyright (c) 2002 Mark Thompson.
+ *
+ * This code 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.
+ *
+ * itext-questions@list.sourceforge.net
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfImportedPage;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.OptionArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * This tool lets you generate a PDF that shows N pages on 1.
+ */
+public class NUp extends AbstractTool {
+
+ static {
+ addVersion("$Id: NUp.java,v 1.6 2005/11/29 21:05:02 blowagie Exp $");
+ }
+
+ /**
+ * Constructs an NUp object.
+ */
+ public NUp() {
+ menuoptions = MENU_EXECUTE | MENU_EXECUTE_SHOW;
+ arguments.add(new FileArgument(this, "srcfile", "The file you want to N-up", false, new PdfFilter()));
+ arguments.add(new FileArgument(this, "destfile", "The resulting PDF", true, new PdfFilter()));
+ OptionArgument oa = new OptionArgument(this, "pow2", "The number of pages you want to copy to 1 page");
+ oa.addOption("2", "1");
+ oa.addOption("4", "2");
+ oa.addOption("8", "3");
+ oa.addOption("16", "4");
+ oa.addOption("32", "5");
+ oa.addOption("64", "6");
+ arguments.add(oa);
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("N-up", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== N-up OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) throw new InstantiationException("You need to choose a sourcefile");
+ File src = (File)getValue("srcfile");
+ if (getValue("destfile") == null) throw new InstantiationException("You need to choose a destination file");
+ File dest = (File)getValue("destfile");
+ int pow2;
+ try {
+ pow2 = Integer.parseInt((String) getValue("pow2"));
+ }
+ catch(Exception e) {
+ pow2 = 1;
+ }
+ // we create a reader for a certain document
+ PdfReader reader = new PdfReader(src.getAbsolutePath());
+ // we retrieve the total number of pages and the page size
+ int total = reader.getNumberOfPages();
+ System.out.println("There are " + total + " pages in the original file.");
+ Rectangle pageSize = reader.getPageSize(1);
+ Rectangle newSize = (pow2 % 2) == 0 ? new Rectangle(pageSize.width(), pageSize.height()) : new Rectangle(pageSize.height(), pageSize.width());
+ Rectangle unitSize = new Rectangle(pageSize.width(), pageSize.height());
+ Rectangle currentSize;
+ for (int i = 0; i < pow2; i++) {
+ unitSize = new Rectangle(unitSize.height() / 2, unitSize.width());
+ }
+ int n = (int)Math.pow(2, pow2);
+ int r = (int)Math.pow(2, (int)pow2 / 2);
+ int c = n / r;
+ // step 1: creation of a document-object
+ Document document = new Document(newSize, 0, 0, 0, 0);
+ // step 2: we create a writer that listens to the document
+ PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
+ // step 3: we open the document
+ document.open();
+ // step 4: adding the content
+ PdfContentByte cb = writer.getDirectContent();
+ PdfImportedPage page;
+ float offsetX, offsetY, factor;
+ int p;
+ for (int i = 0; i < total; i++) {
+ if (i % n == 0) {
+ document.newPage();
+ }
+ p = i + 1;
+ offsetX = unitSize.width() * ((i % n) % c);
+ offsetY = newSize.height() - (unitSize.height() * (((i % n) / c) + 1));
+ currentSize = reader.getPageSize(p);
+ factor = Math.min(unitSize.width() / currentSize.width(), unitSize.height() / currentSize.height());
+ offsetX += (unitSize.width() - (currentSize.width() * factor)) / 2f;
+ offsetY += (unitSize.height() - (currentSize.height() * factor)) / 2f;
+ page = writer.getImportedPage(reader, p);
+ cb.addTemplate(page, factor, 0, 0, factor, offsetX, offsetY);
+ }
+ // step 5: we close the document
+ document.close();
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+
+ /**
+ * Generates an NUp version of an existing PDF file.
+ * @param args
+ */
+ public static void main(String[] args) {
+ NUp tool = new NUp();
+ if (args.length < 2) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile");
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/PhotoAlbum.java b/src/main/java/com/lowagie/tools/plugins/PhotoAlbum.java
new file mode 100644
index 0000000..26a95f3
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/PhotoAlbum.java
@@ -0,0 +1,200 @@
+/*
+ * $Id: PhotoAlbum.java,v 1.8 2006/03/21 08:49:35 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.Image;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.PdfPageLabels;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.tools.arguments.DirFilter;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Converts a Tiff file to a PDF file.
+ * Inspired by a comp.text.pdf question by Sebastian Schubert
+ * and an answer by Hans-Werner Hilse.
+ */
+public class PhotoAlbum extends AbstractTool {
+
+ static {
+ addVersion("$Id: PhotoAlbum.java,v 1.8 2006/03/21 08:49:35 blowagie Exp $");
+ }
+ /**
+ * Constructs a PhotoAlbum object.
+ */
+ public PhotoAlbum() {
+ menuoptions = MENU_EXECUTE | MENU_EXECUTE_SHOW;
+ arguments.add(new FileArgument(this, "srcdir", "The directory containing the image files", false, new DirFilter()));
+ arguments.add(new FileArgument(this, "destfile", "The file to which the converted TIFF has to be written", true, new PdfFilter()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("PhotoAlbum", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== PhotoAlbum OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcdir") == null) throw new InstantiationException("You need to choose a source directory");
+ File directory = (File)getValue("srcdir");
+ if (directory.isFile()) directory = directory.getParentFile();
+ if (getValue("destfile") == null) throw new InstantiationException("You need to choose a destination file");
+ File pdf_file = (File)getValue("destfile");
+ Document document = new Document();
+ PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pdf_file));
+ writer.setViewerPreferences(PdfWriter.PageModeUseThumbs);
+ PdfPageLabels pageLabels = new PdfPageLabels();
+ int dpiX, dpiY;
+ float imgWidthPica, imgHeightPica;
+ TreeSet images = new TreeSet();
+ File[] files = directory.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].isFile()) images.add(files[i]);
+ }
+ File image;
+ String label;
+ for (Iterator i = images.iterator(); i.hasNext(); ) {
+ image = (File) i.next();
+ System.out.println("Testing image: " + image.getName());
+ try {
+ Image img = Image.getInstance(image.getAbsolutePath());
+ dpiX=img.getDpiX();
+ if (dpiX == 0) dpiX=72;
+ dpiY=img.getDpiY();
+ if (dpiY == 0) dpiY=72;
+ imgWidthPica=(72*img.plainWidth()) / dpiX;
+ imgHeightPica=(72*img.plainHeight()) / dpiY;
+ img.scaleAbsolute(imgWidthPica,imgHeightPica);
+ document.setPageSize(new Rectangle(imgWidthPica, imgHeightPica));
+ if (document.isOpen()) {
+ document.newPage();
+ }
+ else {
+ document.open();
+ }
+ img.setAbsolutePosition(0, 0);
+ document.add(img);
+ label = image.getName();
+ if (label.lastIndexOf(".") > 0);
+ label = label.substring(0, label.lastIndexOf("."));
+ pageLabels.addPageLabel(writer.getPageNumber(), PdfPageLabels.EMPTY, label);
+ System.out.println("Added image: " + image.getName());
+ }
+ catch(Exception e) {
+ System.err.println(e.getMessage());
+ }
+ }
+ if (document.isOpen()) {
+ writer.setPageLabels(pageLabels);
+ document.close();
+ }
+ else {
+ System.err.println("No images were found in directory " + directory.getAbsolutePath());
+ }
+ } catch (Exception e) {
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+
+ /**
+ * Converts a tiff file to PDF.
+ * @param args
+ */
+ public static void main(String[] args) {
+ PhotoAlbum tool = new PhotoAlbum();
+ if (args.length < 2) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile");
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/RemoveLaunchApplication.java b/src/main/java/com/lowagie/tools/plugins/RemoveLaunchApplication.java
new file mode 100644
index 0000000..fc082e7
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/RemoveLaunchApplication.java
@@ -0,0 +1,189 @@
+/*
+ * $Id: RemoveLaunchApplication.java,v 1.4 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+
+import com.lowagie.text.pdf.PRIndirectReference;
+import com.lowagie.text.pdf.PdfDictionary;
+import com.lowagie.text.pdf.PdfName;
+import com.lowagie.text.pdf.PdfObject;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfStamper;
+import com.lowagie.text.pdf.PdfString;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * This tool copies an existing PDF and removes potentially dangerous code that launches an application.
+ */
+public class RemoveLaunchApplication
+ extends AbstractTool {
+
+ static {
+ addVersion(
+ "$Id: RemoveLaunchApplication.java,v 1.4 2005/11/29 21:05:02 blowagie Exp $");
+ }
+
+ /**
+ * Constructs a ReversePages object.
+ */
+ public RemoveLaunchApplication() {
+ menuoptions = MENU_EXECUTE | MENU_EXECUTE_SHOW;
+ arguments.add(new FileArgument(this, "srcfile",
+ "The file from which you want to remove Launch Application actions", false,
+ new PdfFilter()));
+ arguments.add(new FileArgument(this, "destfile",
+ "The file to which the cleaned up version of the original PDF has to be written", true,
+ new PdfFilter()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Remove Launch Applications", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Remove Launch Applications OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) {
+ throw new InstantiationException("You need to choose a sourcefile");
+ }
+ File src = (File) getValue("srcfile");
+ if (getValue("destfile") == null) {
+ throw new InstantiationException(
+ "You need to choose a destination file");
+ }
+ File dest = (File) getValue("destfile");
+
+ // we create a reader for a certain document
+ PdfReader reader = new PdfReader(src.getAbsolutePath());
+ PdfObject o;
+ PdfDictionary d;
+ PdfDictionary l;
+ PdfName n;
+ for (int i = 1; i < reader.getXrefSize(); i++) {
+ o = reader.getPdfObject(i);
+ if (o instanceof PdfDictionary) {
+ d = (PdfDictionary)o;
+ o = d.get(PdfName.A);
+ if (o == null) continue;
+ if (o instanceof PdfDictionary) {
+ l = (PdfDictionary)o;
+ }
+ else {
+ PRIndirectReference r =(PRIndirectReference)o;
+ l = (PdfDictionary)reader.getPdfObject(r.getNumber());
+ }
+ n = (PdfName)l.get(PdfName.S);
+ if (PdfName.LAUNCH.equals(n)) {
+ if (l.get(PdfName.F) != null) {
+ System.out.println("Removed: " + l.get(PdfName.F));
+ l.remove(PdfName.F);
+ }
+ if (l.get(PdfName.WIN) != null) {
+ System.out.println("Removed: " + l.get(PdfName.WIN));
+ l.remove(PdfName.WIN);
+ }
+ l.put(PdfName.S, PdfName.JAVASCRIPT);
+ l.put(PdfName.JS, new PdfString("app.alert('Launch Application Action removed by iText');\r"));
+ }
+ }
+ }
+ PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
+ stamper.close();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Copy an existing PDF and replace the Launch Application Action with JavaScript alerts.
+ * @param args
+ */
+ public static void main(String[] args) {
+ RemoveLaunchApplication tool = new RemoveLaunchApplication();
+ if (args.length < 2) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File) getValue("destfile");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/tools/plugins/ReversePages.java b/src/main/java/com/lowagie/tools/plugins/ReversePages.java
new file mode 100644
index 0000000..52b3053
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/ReversePages.java
@@ -0,0 +1,180 @@
+/*
+ * $Id: ReversePages.java,v 1.3 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie and anonymous.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.pdf.PRAcroForm;
+import com.lowagie.text.pdf.PdfCopy;
+import com.lowagie.text.pdf.PdfImportedPage;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * This tool lets you take pages from an existing PDF and copy them in reverse order into a new PDF.
+ */
+public class ReversePages
+ extends AbstractTool {
+
+ static {
+ addVersion(
+ "$Id: ReversePages.java,v 1.3 2005/11/29 21:05:02 blowagie Exp $");
+ }
+
+ /**
+ * Constructs a ReversePages object.
+ */
+ public ReversePages() {
+ menuoptions = MENU_EXECUTE | MENU_EXECUTE_SHOW;
+ arguments.add(new FileArgument(this, "srcfile",
+ "The file you want to reorder", false,
+ new PdfFilter()));
+ arguments.add(new FileArgument(this, "destfile",
+ "The file to which the reordered version of the original PDF has to be written", true,
+ new PdfFilter()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("ReversePages", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== ReversePages OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) {
+ throw new InstantiationException("You need to choose a sourcefile");
+ }
+ File src = (File) getValue("srcfile");
+ if (getValue("destfile") == null) {
+ throw new InstantiationException(
+ "You need to choose a destination file");
+ }
+ File dest = (File) getValue("destfile");
+
+ // we create a reader for a certain document
+ PdfReader reader = new PdfReader(src.getAbsolutePath());
+ System.out.println("The original file had " + reader.getNumberOfPages() +
+ " pages.");
+ int pages = reader.getNumberOfPages();
+ java.util.ArrayList li=new java.util.ArrayList();
+ for(int i=pages;i>0;i--){
+ li.add(new Integer(i));
+ }
+ reader.selectPages(li);
+
+ System.err.println("The new file has " + pages + " pages.");
+ Document document = new Document(reader.getPageSizeWithRotation(1));
+ PdfCopy copy = new PdfCopy(document,
+ new FileOutputStream(dest.getAbsolutePath()));
+ document.open();
+ PdfImportedPage page;
+ for (int i = 0; i < pages; ) {
+ ++i;
+ System.out.println("Processed page " + i);
+ page = copy.getImportedPage(reader, i);
+ copy.addPage(page);
+ }
+
+ PRAcroForm form = reader.getAcroForm();
+ if (form != null) {
+ copy.copyAcroForm(reader);
+ }
+ document.close();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Take pages from an existing PDF and copy them in reverse order into a new PDF.
+ * @param args
+ */
+ public static void main(String[] args) {
+ ReversePages tool = new ReversePages();
+ if (args.length < 2) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File) getValue("destfile");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/tools/plugins/SelectedPages.java b/src/main/java/com/lowagie/tools/plugins/SelectedPages.java
new file mode 100644
index 0000000..73b3b4f
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/SelectedPages.java
@@ -0,0 +1,150 @@
+/*
+ * $Id: SelectedPages.java,v 1.7 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * This code is free software. It may only be copied or modified
+ * if you include the following copyright notice:
+ *
+ * This class by Mark Thompson. Copyright (c) 2002 Mark Thompson.
+ *
+ * This code 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.
+ *
+ * itext-questions@list.sourceforge.net
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.pdf.PRAcroForm;
+import com.lowagie.text.pdf.PdfCopy;
+import com.lowagie.text.pdf.PdfImportedPage;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PageSelectorToolArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * This tool lets you select pages from an existing PDF and copy them into a new PDF.
+ */
+public class SelectedPages
+ extends AbstractTool {
+
+ static {
+ addVersion(
+ "$Id: SelectedPages.java,v 1.7 2005/11/29 21:05:02 blowagie Exp $");
+ }
+
+ /**
+ * Constructs a SelectedPages object.
+ */
+ public SelectedPages() {
+ menuoptions = MENU_EXECUTE | MENU_EXECUTE_SHOW;
+ ToolArgument inputfile = new FileArgument(this, "srcfile",
+ "The file you want to split", false,
+ new PdfFilter());
+ arguments.add(inputfile);
+ arguments.add(new FileArgument(this, "destfile",
+ "The file to which the first part of the original PDF has to be written", true,
+ new PdfFilter()));
+ ToolArgument spfpdf = new PageSelectorToolArgument(this, "selection",
+ "A selection of pages (see Help for more info)", String.class.getName());
+ arguments.add(spfpdf);
+ inputfile.addPropertyChangeListener(spfpdf);
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("SelectedPages", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== SelectedPages OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) {
+ throw new InstantiationException("You need to choose a sourcefile");
+ }
+ File src = (File) getValue("srcfile");
+ if (getValue("destfile") == null) {
+ throw new InstantiationException(
+ "You need to choose a destination file for the first part of the PDF");
+ }
+ File dest = (File) getValue("destfile");
+ String selection = (String) getValue("selection");
+
+ // we create a reader for a certain document
+ PdfReader reader = new PdfReader(src.getAbsolutePath());
+ System.out.println("The original file had " + reader.getNumberOfPages() +
+ " pages.");
+ reader.selectPages(selection);
+ int pages = reader.getNumberOfPages();
+ System.err.println("The new file has " + pages + " pages.");
+ Document document = new Document(reader.getPageSizeWithRotation(1));
+ PdfCopy copy = new PdfCopy(document,
+ new FileOutputStream(dest.getAbsolutePath()));
+ document.open();
+ PdfImportedPage page;
+ for (int i = 0; i < pages; ) {
+ ++i;
+ System.out.println("Processed page " + i);
+ page = copy.getImportedPage(reader, i);
+ copy.addPage(page);
+ }
+ PRAcroForm form = reader.getAcroForm();
+ if (form != null) {
+ copy.copyAcroForm(reader);
+ }
+ document.close();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ System.out.println("klasse:" + arg.getClassname());
+ System.out.println("arg:" + arg.getValue());
+ }
+
+ /**
+ * Select pages from an existing PDF and copy them into a new PDF.
+ * @param args
+ */
+ public static void main(String[] args) {
+ SelectedPages tool = new SelectedPages();
+ if (args.length < 4) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File) getValue("destfile");
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/Split.java b/src/main/java/com/lowagie/tools/plugins/Split.java
new file mode 100644
index 0000000..2c14651
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/Split.java
@@ -0,0 +1,168 @@
+/*
+ * $Id: Split.java,v 1.9 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * This code is free software. It may only be copied or modified
+ * if you include the following copyright notice:
+ *
+ * This class by Mark Thompson. Copyright (c) 2002 Mark Thompson.
+ *
+ * This code 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.
+ *
+ * itext-questions@list.sourceforge.net
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfImportedPage;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.LabelAccessory;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * This tool lets you split a PDF in two separate PDF files.
+ */
+public class Split extends AbstractTool {
+
+ static {
+ addVersion("$Id: Split.java,v 1.9 2005/11/29 21:05:02 blowagie Exp $");
+ }
+ /**
+ * Constructs an Split object.
+ */
+ public Split() {
+ FileArgument f = new FileArgument(this, "srcfile", "The file you want to split", false, new PdfFilter());
+ f.setLabel(new LabelAccessory());
+ arguments.add(f);
+ arguments.add(new FileArgument(this, "destfile1", "The file to which the first part of the original PDF has to be written", true, new PdfFilter()));
+ arguments.add(new FileArgument(this, "destfile2", "The file to which the second part of the original PDF has to be written", true, new PdfFilter()));
+ arguments.add(new ToolArgument(this, "pagenumber", "The pagenumber where you want to split", String.class.getName()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Split", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Split OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) throw new InstantiationException("You need to choose a sourcefile");
+ File src = (File)getValue("srcfile");
+ if (getValue("destfile1") == null) throw new InstantiationException("You need to choose a destination file for the first part of the PDF");
+ File file1 = (File)getValue("destfile1");
+ if (getValue("destfile2") == null) throw new InstantiationException("You need to choose a destination file for the second part of the PDF");
+ File file2 = (File)getValue("destfile2");
+ int pagenumber = Integer.parseInt((String)getValue("pagenumber"));
+
+ // we create a reader for a certain document
+ PdfReader reader = new PdfReader(src.getAbsolutePath());
+ // we retrieve the total number of pages
+ int n = reader.getNumberOfPages();
+ System.out.println("There are " + n + " pages in the original file.");
+
+ if (pagenumber < 2 || pagenumber > n) {
+ throw new DocumentException("You can't split this document at page " + pagenumber + "; there is no such page.");
+ }
+
+ // step 1: creation of a document-object
+ Document document1 = new Document(reader.getPageSizeWithRotation(1));
+ Document document2 = new Document(reader.getPageSizeWithRotation(pagenumber));
+ // step 2: we create a writer that listens to the document
+ PdfWriter writer1 = PdfWriter.getInstance(document1, new FileOutputStream(file1));
+ PdfWriter writer2 = PdfWriter.getInstance(document2, new FileOutputStream(file2));
+ // step 3: we open the document
+ document1.open();
+ PdfContentByte cb1 = writer1.getDirectContent();
+ document2.open();
+ PdfContentByte cb2 = writer2.getDirectContent();
+ PdfImportedPage page;
+ int rotation;
+ int i = 0;
+ // step 4: we add content
+ while (i < pagenumber - 1) {
+ i++;
+ document1.setPageSize(reader.getPageSizeWithRotation(i));
+ document1.newPage();
+ page = writer1.getImportedPage(reader, i);
+ rotation = reader.getPageRotation(i);
+ if (rotation == 90 || rotation == 270) {
+ cb1.addTemplate(page, 0, -1f, 1f, 0, 0, reader.getPageSizeWithRotation(i).height());
+ }
+ else {
+ cb1.addTemplate(page, 1f, 0, 0, 1f, 0, 0);
+ }
+ }
+ while (i < n) {
+ i++;
+ document2.setPageSize(reader.getPageSizeWithRotation(i));
+ document2.newPage();
+ page = writer2.getImportedPage(reader, i);
+ rotation = reader.getPageRotation(i);
+ if (rotation == 90 || rotation == 270) {
+ cb2.addTemplate(page, 0, -1f, 1f, 0, 0, reader.getPageSizeWithRotation(i).height());
+ }
+ else {
+ cb2.addTemplate(page, 1f, 0, 0, 1f, 0, 0);
+ }
+ }
+ // step 5: we close the document
+ document1.close();
+ document2.close();
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+
+ /**
+ * Split a PDF in two separate PDF files.
+ * @param args
+ */
+ public static void main(String[] args) {
+ Split tool = new Split();
+ if (args.length < 4) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile1");
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/Tiff2Pdf.java b/src/main/java/com/lowagie/tools/plugins/Tiff2Pdf.java
new file mode 100644
index 0000000..8455cdf
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/Tiff2Pdf.java
@@ -0,0 +1,192 @@
+/*
+ * $Id: Tiff2Pdf.java,v 1.9 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.Image;
+import com.lowagie.text.PageSize;
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.text.pdf.RandomAccessFileOrArray;
+import com.lowagie.text.pdf.codec.TiffImage;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.ImageFilter;
+import com.lowagie.tools.arguments.OptionArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Converts a Tiff file to a PDF file.
+ */
+public class Tiff2Pdf extends AbstractTool {
+
+ static {
+ addVersion("$Id: Tiff2Pdf.java,v 1.9 2005/11/29 21:05:02 blowagie Exp $");
+ }
+ /**
+ * Constructs a Tiff2Pdf object.
+ */
+ public Tiff2Pdf() {
+ menuoptions = MENU_EXECUTE | MENU_EXECUTE_SHOW;
+ arguments.add(new FileArgument(this, "srcfile", "The file you want to convert", false, new ImageFilter(false, false, false, false, false, true)));
+ arguments.add(new FileArgument(this, "destfile", "The file to which the converted TIFF has to be written", true, new PdfFilter()));
+ OptionArgument oa = new OptionArgument(this, "pagesize", "Pagesize");
+ oa.addOption("A4", "A4");
+ oa.addOption("Letter", "LETTER");
+ oa.addOption("Original format", "ORIGINAL");
+ arguments.add(oa);
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Tiff2Pdf", true, false, true);
+ internalFrame.setSize(550, 250);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Tiff2Pdf OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) throw new InstantiationException("You need to choose a sourcefile");
+ File tiff_file = (File)getValue("srcfile");
+ if (getValue("destfile") == null) throw new InstantiationException("You need to choose a destination file");
+ File pdf_file = (File)getValue("destfile");
+ RandomAccessFileOrArray ra = new RandomAccessFileOrArray(tiff_file.getAbsolutePath());
+ int comps = TiffImage.getNumberOfPages(ra);
+ boolean adjustSize = false;
+ Document document = new Document(PageSize.A4);
+ if ("ORIGINAL".equals(getValue("pagesize"))) {
+ Image img = TiffImage.getTiffImage(ra, 1);
+ document.setPageSize(new Rectangle(img.scaledWidth(), img.scaledHeight()));
+ adjustSize = true;
+ }
+ else if ("LETTER".equals(getValue("pagesize"))) {
+ document.setPageSize(PageSize.LETTER);
+ }
+ PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pdf_file));
+ document.open();
+ PdfContentByte cb = writer.getDirectContent();
+ for (int c = 0; c < comps; ++c) {
+ Image img = TiffImage.getTiffImage(ra, c + 1);
+ if (img != null) {
+ if (adjustSize) {
+ document.setPageSize(new Rectangle(img.scaledWidth(),
+ img.scaledHeight()));
+ document.newPage();
+ img.setAbsolutePosition(0, 0);
+ }
+ else {
+ if (img.scaledWidth() > 500 || img.scaledHeight() > 700) {
+ img.scaleToFit(500, 700);
+ }
+ img.setAbsolutePosition(20, 20);
+ document.newPage();
+ document.add(new Paragraph(tiff_file + " - page " + (c + 1)));
+ }
+ cb.addImage(img);
+ System.out.println("Finished page " + (c + 1));
+ }
+ }
+ ra.close();
+ document.close();
+ } catch (Exception e) {
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+
+ /**
+ * Converts a tiff file to PDF.
+ * @param args
+ */
+ public static void main(String[] args) {
+ Tiff2Pdf tool = new Tiff2Pdf();
+ if (args.length < 2) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/tools/plugins/TreeViewPDF.java b/src/main/java/com/lowagie/tools/plugins/TreeViewPDF.java
new file mode 100644
index 0000000..0b55b8c
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/TreeViewPDF.java
@@ -0,0 +1,160 @@
+/*
+ * $Id: TreeViewPDF.java,v 1.2 2006/05/29 10:31:17 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Anonymous.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+
+import javax.swing.JOptionPane;
+
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.LabelAccessory;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+import com.lowagie.tools.plugins.treeview.TreeViewInternalFrame;
+import com.lowagie.tools.Toolbox;
+import java.beans.*;
+
+/**
+ * Allows you to inspect an existing PDF file.
+ */
+public class TreeViewPDF
+ extends AbstractTool {
+ static {
+ addVersion("$Id: TreeViewPDF.java,v 1.2 2006/05/29 10:31:17 blowagie Exp $");
+ }
+
+ TreeViewInternalFrame ul;
+ FileArgument inputfile;
+ /**
+ * Constructs an TreeViewPDF object.
+ */
+ public TreeViewPDF() {
+ inputfile = new FileArgument(this, "srcfile",
+ "The file you want to inspect", false,
+ new PdfFilter());
+ inputfile.setLabel(new LabelAccessory());
+ arguments.add(inputfile);
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ ul = new TreeViewInternalFrame("Pdf Analysis", true, false, true);
+ internalFrame = ul;
+ internalFrame.setSize(500, 300);
+ internalFrame.setJMenuBar(getMenubar());
+ inputfile.addPropertyChangeListener(ul);
+ System.out.println("=== Pdf Analysis OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) {
+ throw new InstantiationException("You need to choose a sourcefile");
+ }
+
+ }
+ catch (Exception e) {
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Inspects an existing PDF file.
+ * @param args
+ */
+ public static void main(String[] args) {
+ try {
+ Toolbox toolbox = new Toolbox();
+ AbstractTool tool = toolbox.createFrame("TreeViewPDF");
+ if (args.length > 1) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+ catch (PropertyVetoException ex) {
+ }
+ catch (ClassNotFoundException ex) {
+ }
+ catch (IllegalAccessException ex) {
+ }
+ catch (InstantiationException ex) {
+ }
+// throw new RuntimeException("GUI only Application!");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ throw new InstantiationException("There is no file to show.");
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/Txt2Pdf.java b/src/main/java/com/lowagie/tools/plugins/Txt2Pdf.java
new file mode 100644
index 0000000..8a377fd
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/Txt2Pdf.java
@@ -0,0 +1,169 @@
+/*
+ * $Id: Txt2Pdf.java,v 1.9 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.Font;
+import com.lowagie.text.FontFactory;
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.OptionArgument;
+import com.lowagie.tools.arguments.PageSizeArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Converts a monospaced txt file to a PDF file.
+ */
+public class Txt2Pdf extends AbstractTool {
+
+ static {
+ addVersion("$Id: Txt2Pdf.java,v 1.9 2005/11/29 21:05:02 blowagie Exp $");
+ }
+ /**
+ * Constructs a Txt2Pdf object.
+ */
+ public Txt2Pdf() {
+ menuoptions = MENU_EXECUTE | MENU_EXECUTE_SHOW | MENU_EXECUTE_PRINT_SILENT;
+ arguments.add(new FileArgument(this, "srcfile", "The file you want to convert", false));
+ arguments.add(new FileArgument(this, "destfile", "The file to which the converted text has to be written", true, new PdfFilter()));
+ PageSizeArgument oa1 = new PageSizeArgument(this, "pagesize", "Pagesize");
+ arguments.add(oa1);
+ OptionArgument oa2 = new OptionArgument(this, "orientation", "Orientation of the page");
+ oa2.addOption("Portrait", "PORTRAIT");
+ oa2.addOption("Landscape", "LANDSCAPE");
+ arguments.add(oa2);
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Txt2Pdf", true, true, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Txt2Pdf OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ String line = null;
+ Document document;
+ Font f;
+ Rectangle pagesize = (Rectangle)getValue("pagesize");
+ if ("LANDSCAPE".equals(getValue("orientation"))) {
+ f = FontFactory.getFont(FontFactory.COURIER, 10);
+ document = new Document(pagesize.rotate(), 36, 9, 36, 36);
+ }
+ else {
+ f = FontFactory.getFont(FontFactory.COURIER, 11);
+ document = new Document(pagesize, 72, 36, 36, 36);
+ }
+ BufferedReader in = new BufferedReader(new FileReader((File)getValue("srcfile")));
+ PdfWriter.getInstance(document, new FileOutputStream((File)getValue("destfile")));
+ document.open();
+ while ((line = in.readLine()) != null) {
+ document.add(new Paragraph(12, line, f));
+ }
+ document.close();
+ } catch (Exception e) {
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+
+ /**
+ * Converts a monospaced txt file to a PDF file.
+ * @param args
+ */
+ public static void main(String[] args) {
+ Txt2Pdf tool = new Txt2Pdf();
+ if (args.length < 3) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile");
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/Watermarker.java b/src/main/java/com/lowagie/tools/plugins/Watermarker.java
new file mode 100644
index 0000000..e1e64f8
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/Watermarker.java
@@ -0,0 +1,163 @@
+/*
+ * $Id: Watermarker.java,v 1.6 2006/03/20 14:30:33 blowagie Exp $
+ * $Name: $
+ *
+ * This code is free software. It may only be copied or modified
+ * if you include the following copyright notice:
+ *
+ * This class by Carsten hammer and Bruno Lowagie.
+ * Copyright (c) 2005 Carsten Hammer and Bruno Lowagie.
+ *
+ * This code 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.
+ *
+ * itext-questions@list.sourceforge.net
+ */
+
+package com.lowagie.tools.plugins;
+
+import java.io.*;
+
+import javax.swing.*;
+
+import com.lowagie.text.*;
+import com.lowagie.text.pdf.*;
+import com.lowagie.tools.arguments.*;
+
+/**
+ * This tool lets you add a text watermark to all pages of a document.
+ */
+public class Watermarker extends AbstractTool {
+
+ static {
+ addVersion("$Id: Watermarker.java,v 1.6 2006/03/20 14:30:33 blowagie Exp $");
+ }
+
+ /**
+ * This tool lets you add a text watermark to all pages of a document.
+ */
+ public Watermarker() {
+ super();
+ arguments.add(new FileArgument(this, "srcfile",
+ "The file you want to watermark", false, new PdfFilter()));
+ arguments.add(new ToolArgument(this, "watermark", "The text that can be used as watermark", String.class.getName()));
+ arguments.add(new ToolArgument(this, "fontsize", "The fontsize of the watermark text", String.class.getName()));
+ arguments.add(new ToolArgument(this, "opacity", "The opacity of the watermark text", String.class.getName()));
+ arguments.add(new FileArgument(this, "destfile",
+ "The file to which the watermarked PDF has to be written",
+ true, new PdfFilter()));
+
+ }
+
+ /**
+ * Creates the internal frame.
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("Watermark", true, false, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== Watermark OPENED ===");
+ }
+
+ /**
+ * Executes the tool (in most cases this generates a PDF file).
+ */
+ public void execute() {
+ try {
+ if (getValue("srcfile") == null) {
+ throw new InstantiationException(
+ "You need to choose a sourcefile");
+ }
+ if (getValue("destfile") == null) {
+ throw new InstantiationException(
+ "You need to choose a destination file");
+ }
+ if (getValue("watermark") == null) {
+ throw new InstantiationException(
+ "You need to add a text for the watermark");
+ }
+ int fontsize = Integer.parseInt((String)getValue("fontsize"));
+ float opacity = Float.parseFloat((String)getValue("opacity"));
+ BaseFont bf = BaseFont.createFont("Helvetica", BaseFont.WINANSI,
+ false);
+ PdfReader reader = new PdfReader(((File) getValue("srcfile"))
+ .getAbsolutePath());
+ int pagecount = reader.getNumberOfPages();
+ PdfGState gs1 = new PdfGState();
+ gs1.setFillOpacity(opacity);
+ String text = (String)getValue("watermark");
+ PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(
+ (File) getValue("destfile")));
+ float txtwidth = bf.getWidthPoint(text, fontsize);
+ for (int i = 1; i <= pagecount; i++) {
+ PdfContentByte seitex = stamp.getOverContent(i);
+ Rectangle recc = reader.getCropBox(i);
+ float winkel = (float) Math.atan(recc.height() / recc.width());
+ float m1 = (float) Math.cos(winkel);
+ float m2 = (float) -Math.sin(winkel);
+ float m3 = (float) Math.sin(winkel);
+ float m4 = (float) Math.cos(winkel);
+ float xoff = (float) (-Math.cos(winkel) * txtwidth / 2 - Math
+ .sin(winkel)
+ * fontsize / 2);
+ float yoff = (float) (Math.sin(winkel) * txtwidth / 2 - Math
+ .cos(winkel)
+ * fontsize / 2);
+ seitex.saveState();
+ seitex.setGState(gs1);
+ seitex.beginText();
+ seitex.setFontAndSize(bf, fontsize);
+ seitex.setTextMatrix(m1, m2, m3, m4, xoff + recc.width() / 2,
+ yoff + recc.height() / 2);
+ seitex.showText(text);
+ seitex.endText();
+ seitex.restoreState();
+ }
+ stamp.close();
+ } catch (Exception e) {
+ JOptionPane.showMessageDialog(internalFrame, e.getMessage(), e
+ .getClass().getName(), JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * Gets the PDF file that should be generated (or null if the output isn't a
+ * PDF file).
+ *
+ * @return the PDF file that should be generated
+ * @throws InstantiationException
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File) getValue("destfile");
+ }
+
+ /**
+ * Indicates that the value of an argument has changed.
+ *
+ * @param arg
+ * the argument that has changed
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the
+ // commandline
+ return;
+ }
+
+ }
+
+ /**
+ * This methods helps you running this tool as a standalone application.
+ * @param args the srcfile, watermark text and destfile
+ */
+ public static void main(String[] args) {
+ Watermarker watermarker = new Watermarker();
+ if (args.length != 5) {
+ System.err.println(watermarker.getUsage());
+ }
+ watermarker.setArguments(args);
+ watermarker.execute();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/tools/plugins/XML2Bookmarks.java b/src/main/java/com/lowagie/tools/plugins/XML2Bookmarks.java
new file mode 100644
index 0000000..9b23657
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/XML2Bookmarks.java
@@ -0,0 +1,156 @@
+/*
+ * $Id: XML2Bookmarks.java,v 1.2 2006/02/03 08:30:56 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Hans-Werner Hilse.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.List;
+
+import javax.swing.JInternalFrame;
+import javax.swing.JOptionPane;
+
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfStamper;
+import com.lowagie.text.pdf.PdfWriter;
+import com.lowagie.text.pdf.SimpleBookmark;
+import com.lowagie.tools.arguments.FileArgument;
+import com.lowagie.tools.arguments.PdfFilter;
+import com.lowagie.tools.arguments.ToolArgument;
+
+/**
+ * Allows you to add bookmarks to an existing PDF file
+ */
+public class XML2Bookmarks extends AbstractTool {
+
+ static {
+ addVersion("$Id: XML2Bookmarks.java,v 1.2 2006/02/03 08:30:56 blowagie Exp $");
+ }
+
+ /**
+ * Constructs an XML2Bookmarks object.
+ */
+ public XML2Bookmarks() {
+ arguments.add(new FileArgument(this, "xmlfile", "the bookmarks in XML", false));
+ arguments.add(new FileArgument(this, "pdffile", "the PDF to which you want to add bookmarks", false, new PdfFilter()));
+ arguments.add(new FileArgument(this, "destfile", "the resulting PDF", true, new PdfFilter()));
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#createFrame()
+ */
+ protected void createFrame() {
+ internalFrame = new JInternalFrame("XML + PDF = PDF", true, true, true);
+ internalFrame.setSize(300, 80);
+ internalFrame.setJMenuBar(getMenubar());
+ System.out.println("=== XML2Bookmarks OPENED ===");
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#execute()
+ */
+ public void execute() {
+ try {
+ if (getValue("xmlfile") == null) throw new InstantiationException("You need to choose an xml file");
+ if (getValue("pdffile") == null) throw new InstantiationException("You need to choose a source PDF file");
+ if (getValue("destfile") == null) throw new InstantiationException("You need to choose a destination PDF file");
+ FileInputStream bmReader = new FileInputStream( (File) getValue("xmlfile") );
+ List bookmarks = SimpleBookmark.importFromXML( bmReader );
+ bmReader.close();
+ PdfReader reader = new PdfReader(((File)getValue("pdffile")).getAbsolutePath());
+ reader.consolidateNamedDestinations();
+ int n = reader.getNumberOfPages();
+ PdfStamper stamper = new PdfStamper(reader, new FileOutputStream((File)getValue("destfile")));
+ stamper.setOutlines(bookmarks);
+ stamper.setViewerPreferences(reader.getViewerPreferences() | PdfWriter.PageModeUseOutlines);
+ stamper.close();
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ JOptionPane.showMessageDialog(internalFrame,
+ e.getMessage(),
+ e.getClass().getName(),
+ JOptionPane.ERROR_MESSAGE);
+ System.err.println(e.getMessage());
+ }
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#valueHasChanged(com.lowagie.tools.arguments.ToolArgument)
+ */
+ public void valueHasChanged(ToolArgument arg) {
+ if (internalFrame == null) {
+ // if the internal frame is null, the tool was called from the commandline
+ return;
+ }
+ // represent the changes of the argument in the internal frame
+ }
+
+ /**
+ * Allows you to generate an index file in HTML containing Bookmarks to an existing PDF file.
+ * @param args
+ */
+ public static void main(String[] args) {
+ XML2Bookmarks tool = new XML2Bookmarks();
+ if (args.length < 3) {
+ System.err.println(tool.getUsage());
+ }
+ tool.setArguments(args);
+ tool.execute();
+ }
+
+ /**
+ * @see com.lowagie.tools.plugins.AbstractTool#getDestPathPDF()
+ */
+ protected File getDestPathPDF() throws InstantiationException {
+ return (File)getValue("destfile");
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/treeview/AnalyzePDF.java b/src/main/java/com/lowagie/tools/plugins/treeview/AnalyzePDF.java
new file mode 100644
index 0000000..5907f9a
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/treeview/AnalyzePDF.java
@@ -0,0 +1,507 @@
+/*
+ * $Id: AnalyzePDF.java,v 1.4 2006/05/30 09:11:45 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Anonymous.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins.treeview;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Vector;
+
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+import com.lowagie.text.pdf.*;
+import java.util.Set;
+import java.util.Iterator;
+import javax.swing.table.TableModel;
+import javax.swing.table.AbstractTableModel;
+
+public class AnalyzePDF
+ extends Thread implements TreeModel, ICommonAnalyzer {
+ DefaultMutableTreeNode root;
+ DefaultMutableTreeNode filenode;
+ int pagecount;
+ ProgressDialog progressdialog;
+ int numberofpages;
+ ArrayList pageInh = new ArrayList();
+ private transient Vector treeModelListeners;
+ PdfReader reader;
+
+ public AnalyzePDF(String infile,
+ com.lowagie.tools.plugins.treeview.ProgressDialog blubb) {
+ this.progressdialog = blubb;
+ try {
+ reader = new PdfReader(infile);
+ root = new SimpletextTreeNode("Dokument");
+ filenode = new FileTreeNode(infile, reader);
+ root.add(filenode);
+ this.numberofpages = reader.getNumberOfPages();
+ }
+ catch (IOException ex) {
+ }
+ pagecount = 0;
+ }
+
+ public TableModel getXReftable() {
+ TableModel tm = new AbstractTableModel() {
+ public int getColumnCount() {
+ return 2;
+ }
+
+ public int getRowCount() {
+ return reader.getXrefSize() - 1;
+ }
+
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ switch (columnIndex) {
+ case 0:
+ return new Integer(rowIndex + 1);
+ case 1:
+ PdfObject pdfob=reader.getPdfObject(rowIndex + 1);
+ if(pdfob.isStream()){
+ return "Stream "+pdfob;
+ }else{
+ return pdfob;
+ }
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Returns the name of the column at Title: Description: Copyright: Copyright (c) 2005 Company: ");
+ sb.append(this.userObject);
+ sb.append(" ");
+ sb.append(" " + arl.get(i).toString());
+ sb.append(" Title: Description: Copyright: Copyright (c) 2005 Company: ");
+ sb.append(this.userObject);
+ sb.append(" ");
+ sb.append("Key " + it.next().toString());
+ sb.append(" ");
+ sb.append(this.userObject);
+ sb.append(" ");
+ sb.append("PDF Version 1." + this.reader.getPdfVersion());
+ sb.append(" ");
+ sb.append("Number of Pages: " + this.reader.getNumberOfPages());
+ sb.append(" Title: Description: Copyright: Copyright (c) 2005 Company: ");
+ sb.append(this.userObject);
+ sb.append(" ");
+ sb.append("Key " + it.next().toString());
+ sb.append(" ");
+ sb.append(this.userObject);
+ sb.append(" ");
+ sb.append(" " + arl.get(i).toString());
+ sb.append(" ");
+ sb.append("Page " + getSeitennummer());
+ sb.append(" ");
+ sb.append("Size: " + getWidth() + "*" + getHeight());
+ sb.append(" ");
+ sb.append("Key " + it.next().toString());
+ sb.append(" ");
+ sb.append(this.userObject);
+ sb.append("
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 7.7 (page 198-203).
+ *
+ * @see PdfName
+ * @see PdfDictionary
+ * @see BadPdfFormatException
+ */
+
+class PdfFont implements Comparable {
+
+
+ /** the font metrics. */
+ private BaseFont font;
+
+ /** the size. */
+ private float size;
+
+ /** an image. */
+ protected Image image;
+
+ protected float hScale = 1;
+
+ // constructors
+
+ PdfFont(BaseFont bf, float size) {
+ this.size = size;
+ font = bf;
+ }
+
+ // methods
+
+ /**
+ * Compares this PdfFont
with another
+ *
+ * @param object the other PdfFont
+ * @return a value
+ */
+
+ public int compareTo(Object object) {
+ if (image != null)
+ return 0;
+ if (object == null) {
+ return -1;
+ }
+ PdfFont pdfFont;
+ try {
+ pdfFont = (PdfFont) object;
+ if (font != pdfFont.font) {
+ return 1;
+ }
+ if (this.size() != pdfFont.size()) {
+ return 2;
+ }
+ return 0;
+ }
+ catch(ClassCastException cce) {
+ return -2;
+ }
+ }
+
+ /**
+ * Returns the size of this font.
+ *
+ * @return a size
+ */
+
+ float size() {
+ if (image == null)
+ return size;
+ else {
+ return image.scaledHeight();
+ }
+ }
+
+ /**
+ * Returns the approximative width of 1 character of this font.
+ *
+ * @return a width in Text Space
+ */
+
+ float width() {
+ return width(' ');
+ }
+
+ /**
+ * Returns the width of a certain character of this font.
+ *
+ * @param character a certain character
+ * @return a width in Text Space
+ */
+
+ float width(char character) {
+ if (image == null)
+ return font.getWidthPoint(character, size) * hScale;
+ else
+ return image.scaledWidth();
+ }
+
+ float width(String s) {
+ if (image == null)
+ return font.getWidthPoint(s, size) * hScale;
+ else
+ return image.scaledWidth();
+ }
+
+ BaseFont getFont() {
+ return font;
+ }
+
+ void setImage(Image image) {
+ this.image = image;
+ }
+
+ static PdfFont getDefaultFont() {
+ try {
+ BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, false);
+ return new PdfFont(bf, 12);
+ }
+ catch (Exception ee) {
+ throw new ExceptionConverter(ee);
+ }
+ }
+ void setHorizontalScaling(float hScale) {
+ this.hScale = hScale;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfFormField.java b/src/main/java/com/lowagie/text/pdf/PdfFormField.java
new file mode 100644
index 0000000..bd13ddc
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfFormField.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import com.lowagie.text.Rectangle;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/** Implements form fields.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfFormField extends PdfAnnotation {
+
+ public static final int FF_READ_ONLY = 1;
+ public static final int FF_REQUIRED = 2;
+ public static final int FF_NO_EXPORT = 4;
+ public static final int FF_NO_TOGGLE_TO_OFF = 16384;
+ public static final int FF_RADIO = 32768;
+ public static final int FF_PUSHBUTTON = 65536;
+ public static final int FF_MULTILINE = 4096;
+ public static final int FF_PASSWORD = 8192;
+ public static final int FF_COMBO = 131072;
+ public static final int FF_EDIT = 262144;
+ public static final int FF_FILESELECT = 1048576;
+ public static final int FF_MULTISELECT = 2097152;
+ public static final int FF_DONOTSPELLCHECK = 4194304;
+ public static final int FF_DONOTSCROLL = 8388608;
+ public static final int FF_COMB = 16777216;
+ public static final int FF_RADIOSINUNISON = 1 << 25;
+ public static final int Q_LEFT = 0;
+ public static final int Q_CENTER = 1;
+ public static final int Q_RIGHT = 2;
+ public static final int MK_NO_ICON = 0;
+ public static final int MK_NO_CAPTION = 1;
+ public static final int MK_CAPTION_BELOW = 2;
+ public static final int MK_CAPTION_ABOVE = 3;
+ public static final int MK_CAPTION_RIGHT = 4;
+ public static final int MK_CAPTION_LEFT = 5;
+ public static final int MK_CAPTION_OVERLAID = 6;
+ public static final PdfName IF_SCALE_ALWAYS = PdfName.A;
+ public static final PdfName IF_SCALE_BIGGER = PdfName.B;
+ public static final PdfName IF_SCALE_SMALLER = PdfName.S;
+ public static final PdfName IF_SCALE_NEVER = PdfName.N;
+ public static final PdfName IF_SCALE_ANAMORPHIC = PdfName.A;
+ public static final PdfName IF_SCALE_PROPORTIONAL = PdfName.P;
+ public static final boolean MULTILINE = true;
+ public static final boolean SINGLELINE = false;
+ public static final boolean PLAINTEXT = false;
+ public static final boolean PASSWORD = true;
+ static PdfName mergeTarget[] = {PdfName.FONT, PdfName.XOBJECT, PdfName.COLORSPACE, PdfName.PATTERN};
+
+ /** Holds value of property parent. */
+ protected PdfFormField parent;
+
+ protected ArrayList kids;
+
+/**
+ * Constructs a new PdfAnnotation
of subtype link (Action).
+ */
+
+ public PdfFormField(PdfWriter writer, float llx, float lly, float urx, float ury, PdfAction action) {
+ super(writer, llx, lly, urx, ury, action);
+ put(PdfName.TYPE, PdfName.ANNOT);
+ put(PdfName.SUBTYPE, PdfName.WIDGET);
+ annotation = true;
+ }
+
+ /** Creates new PdfFormField */
+ protected PdfFormField(PdfWriter writer) {
+ super(writer, null);
+ form = true;
+ annotation = false;
+ }
+
+ public void setWidget(Rectangle rect, PdfName highlight) {
+ put(PdfName.TYPE, PdfName.ANNOT);
+ put(PdfName.SUBTYPE, PdfName.WIDGET);
+ put(PdfName.RECT, new PdfRectangle(rect));
+ annotation = true;
+ if (highlight != null && !highlight.equals(HIGHLIGHT_INVERT))
+ put(PdfName.H, highlight);
+ }
+
+ public static PdfFormField createEmpty(PdfWriter writer) {
+ PdfFormField field = new PdfFormField(writer);
+ return field;
+ }
+
+ public void setButton(int flags) {
+ put(PdfName.FT, PdfName.BTN);
+ if (flags != 0)
+ put(PdfName.FF, new PdfNumber(flags));
+ }
+
+ protected static PdfFormField createButton(PdfWriter writer, int flags) {
+ PdfFormField field = new PdfFormField(writer);
+ field.setButton(flags);
+ return field;
+ }
+
+ public static PdfFormField createPushButton(PdfWriter writer) {
+ return createButton(writer, FF_PUSHBUTTON);
+ }
+
+ public static PdfFormField createCheckBox(PdfWriter writer) {
+ return createButton(writer, 0);
+ }
+
+ public static PdfFormField createRadioButton(PdfWriter writer, boolean noToggleToOff) {
+ return createButton(writer, FF_RADIO + (noToggleToOff ? FF_NO_TOGGLE_TO_OFF : 0));
+ }
+
+ public static PdfFormField createTextField(PdfWriter writer, boolean multiline, boolean password, int maxLen) {
+ PdfFormField field = new PdfFormField(writer);
+ field.put(PdfName.FT, PdfName.TX);
+ int flags = (multiline ? FF_MULTILINE : 0);
+ flags += (password ? FF_PASSWORD : 0);
+ field.put(PdfName.FF, new PdfNumber(flags));
+ if (maxLen > 0)
+ field.put(PdfName.MAXLEN, new PdfNumber(maxLen));
+ return field;
+ }
+
+ protected static PdfFormField createChoice(PdfWriter writer, int flags, PdfArray options, int topIndex) {
+ PdfFormField field = new PdfFormField(writer);
+ field.put(PdfName.FT, PdfName.CH);
+ field.put(PdfName.FF, new PdfNumber(flags));
+ field.put(PdfName.OPT, options);
+ if (topIndex > 0)
+ field.put(PdfName.TI, new PdfNumber(topIndex));
+ return field;
+ }
+
+ public static PdfFormField createList(PdfWriter writer, String options[], int topIndex) {
+ return createChoice(writer, 0, processOptions(options), topIndex);
+ }
+
+ public static PdfFormField createList(PdfWriter writer, String options[][], int topIndex) {
+ return createChoice(writer, 0, processOptions(options), topIndex);
+ }
+
+ public static PdfFormField createCombo(PdfWriter writer, boolean edit, String options[], int topIndex) {
+ return createChoice(writer, FF_COMBO + (edit ? FF_EDIT : 0), processOptions(options), topIndex);
+ }
+
+ public static PdfFormField createCombo(PdfWriter writer, boolean edit, String options[][], int topIndex) {
+ return createChoice(writer, FF_COMBO + (edit ? FF_EDIT : 0), processOptions(options), topIndex);
+ }
+
+ protected static PdfArray processOptions(String options[]) {
+ PdfArray array = new PdfArray();
+ for (int k = 0; k < options.length; ++k) {
+ array.add(new PdfString(options[k], PdfObject.TEXT_UNICODE));
+ }
+ return array;
+ }
+
+ protected static PdfArray processOptions(String options[][]) {
+ PdfArray array = new PdfArray();
+ for (int k = 0; k < options.length; ++k) {
+ String subOption[] = options[k];
+ PdfArray ar2 = new PdfArray(new PdfString(subOption[0], PdfObject.TEXT_UNICODE));
+ ar2.add(new PdfString(subOption[1], PdfObject.TEXT_UNICODE));
+ array.add(ar2);
+ }
+ return array;
+ }
+
+ public static PdfFormField createSignature(PdfWriter writer) {
+ PdfFormField field = new PdfFormField(writer);
+ field.put(PdfName.FT, PdfName.SIG);
+ return field;
+ }
+
+ /** Getter for property parent.
+ * @return Value of property parent.
+ */
+ public PdfFormField getParent() {
+ return parent;
+ }
+
+ public void addKid(PdfFormField field) {
+ field.parent = this;
+ if (kids == null)
+ kids = new ArrayList();
+ kids.add(field);
+ }
+
+ ArrayList getKids() {
+ return kids;
+ }
+
+ public int setFieldFlags(int flags) {
+ PdfNumber obj = (PdfNumber)get(PdfName.FF);
+ int old;
+ if (obj == null)
+ old = 0;
+ else
+ old = obj.intValue();
+ int v = old | flags;
+ put(PdfName.FF, new PdfNumber(v));
+ return old;
+ }
+
+ public void setValueAsString(String s) {
+ put(PdfName.V, new PdfString(s, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setValueAsName(String s) {
+ put(PdfName.V, new PdfName(s));
+ }
+
+ public void setValue(PdfSignature sig) {
+ put(PdfName.V, sig);
+ }
+
+ public void setDefaultValueAsString(String s) {
+ put(PdfName.DV, new PdfString(s, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setDefaultValueAsName(String s) {
+ put(PdfName.DV, new PdfName(s));
+ }
+
+ public void setFieldName(String s) {
+ if (s != null)
+ put(PdfName.T, new PdfString(s, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setUserName(String s) {
+ put(PdfName.TU, new PdfString(s, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setMappingName(String s) {
+ put(PdfName.TM, new PdfString(s, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setQuadding(int v) {
+ put(PdfName.Q, new PdfNumber(v));
+ }
+
+ static void mergeResources(PdfDictionary result, PdfDictionary source, PdfStamperImp writer) {
+ PdfDictionary dic = null;
+ PdfDictionary res = null;
+ PdfName target = null;
+ for (int k = 0; k < mergeTarget.length; ++k) {
+ target = mergeTarget[k];
+ PdfDictionary pdfDict = (PdfDictionary)PdfReader.getPdfObject(source.get(target));
+ if ((dic = pdfDict) != null) {
+ if ((res = (PdfDictionary)PdfReader.getPdfObject(result.get(target), result)) == null) {
+ res = new PdfDictionary();
+ }
+ res.mergeDifferent(dic);
+ result.put(target, res);
+ if (writer != null)
+ writer.markUsed(res);
+ }
+ }
+ }
+
+ static void mergeResources(PdfDictionary result, PdfDictionary source) {
+ mergeResources(result, source, null);
+ }
+
+ void setUsed() {
+ used = true;
+ if (parent != null)
+ put(PdfName.PARENT, parent.getIndirectReference());
+ if (kids != null) {
+ PdfArray array = new PdfArray();
+ for (int k = 0; k < kids.size(); ++k)
+ array.add(((PdfFormField)kids.get(k)).getIndirectReference());
+ put(PdfName.KIDS, array);
+ }
+ if (templates == null)
+ return;
+ PdfDictionary dic = new PdfDictionary();
+ for (Iterator it = templates.keySet().iterator(); it.hasNext();) {
+ PdfTemplate template = (PdfTemplate)it.next();
+ mergeResources(dic, (PdfDictionary)template.getResources());
+ }
+ put(PdfName.DR, dic);
+ }
+
+ public static PdfAnnotation shallowDuplicate(PdfAnnotation annot) {
+ PdfAnnotation dup;
+ if (annot.isForm()) {
+ dup = new PdfFormField(annot.writer);
+ PdfFormField dupField = (PdfFormField)dup;
+ PdfFormField srcField = (PdfFormField)annot;
+ dupField.parent = srcField.parent;
+ dupField.kids = srcField.kids;
+ }
+ else
+ dup = new PdfAnnotation(annot.writer, null);
+ dup.merge(annot);
+ dup.form = annot.form;
+ dup.annotation = annot.annotation;
+ dup.templates = annot.templates;
+ return dup;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfFormXObject.java b/src/main/java/com/lowagie/text/pdf/PdfFormXObject.java
new file mode 100644
index 0000000..15f0041
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfFormXObject.java
@@ -0,0 +1,103 @@
+/*
+ * $Id: PdfFormXObject.java,v 1.58 2005/07/16 16:49:22 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * PdfFormObject
is a type of XObject containing a template-object.
+ */
+
+public class PdfFormXObject extends PdfStream {
+
+ // public static final variables
+
+/** This is a PdfNumber representing 0. */
+ public static final PdfNumber ZERO = new PdfNumber(0);
+
+/** This is a PdfNumber representing 1. */
+ public static final PdfNumber ONE = new PdfNumber(1);
+
+/** This is the 1 - matrix. */
+ public static final PdfLiteral MATRIX = new PdfLiteral("[1 0 0 1 0 0]");
+
+ // membervariables
+
+
+ // constructor
+
+/**
+ * Constructs a PdfFormXObject
-object.
+ *
+ * @param template the template
+ */
+
+ PdfFormXObject(PdfTemplate template) // throws BadPdfFormatException
+ {
+ super();
+ put(PdfName.TYPE, PdfName.XOBJECT);
+ put(PdfName.SUBTYPE, PdfName.FORM);
+ put(PdfName.RESOURCES, template.getResources());
+ put(PdfName.BBOX, new PdfRectangle(template.getBoundingBox()));
+ put(PdfName.FORMTYPE, ONE);
+ if (template.getLayer() != null)
+ put(PdfName.OC, template.getLayer().getRef());
+ if (template.getGroup() != null)
+ put(PdfName.GROUP, template.getGroup());
+ PdfArray matrix = template.getMatrix();
+ if (matrix == null)
+ put(PdfName.MATRIX, MATRIX);
+ else
+ put(PdfName.MATRIX, matrix);
+ bytes = template.toPdf(null);
+ put(PdfName.LENGTH, new PdfNumber(bytes.length));
+ flateCompress();
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfFunction.java b/src/main/java/com/lowagie/text/pdf/PdfFunction.java
new file mode 100644
index 0000000..1460ba8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfFunction.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.ExceptionConverter;
+import java.io.IOException;
+/** Implements PDF functions.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfFunction {
+
+ protected PdfWriter writer;
+
+ protected PdfIndirectReference reference;
+
+ protected PdfDictionary dictionary;
+
+ /** Creates new PdfFunction */
+ protected PdfFunction(PdfWriter writer) {
+ this.writer = writer;
+ }
+
+ PdfIndirectReference getReference() {
+ try {
+ if (reference == null) {
+ reference = writer.addToBody(dictionary).getIndirectReference();
+ }
+ }
+ catch (IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ return reference;
+ }
+
+ public static PdfFunction type0(PdfWriter writer, float domain[], float range[], int size[],
+ int bitsPerSample, int order, float encode[], float decode[], byte stream[]) {
+ PdfFunction func = new PdfFunction(writer);
+ func.dictionary = new PdfStream(stream);
+ ((PdfStream)func.dictionary).flateCompress();
+ func.dictionary.put(PdfName.FUNCTIONTYPE, new PdfNumber(0));
+ func.dictionary.put(PdfName.DOMAIN, new PdfArray(domain));
+ func.dictionary.put(PdfName.RANGE, new PdfArray(range));
+ func.dictionary.put(PdfName.SIZE, new PdfArray(size));
+ func.dictionary.put(PdfName.BITSPERSAMPLE, new PdfNumber(bitsPerSample));
+ if (order != 1)
+ func.dictionary.put(PdfName.ORDER, new PdfNumber(order));
+ if (encode != null)
+ func.dictionary.put(PdfName.ENCODE, new PdfArray(encode));
+ if (decode != null)
+ func.dictionary.put(PdfName.DECODE, new PdfArray(decode));
+ return func;
+ }
+
+ public static PdfFunction type2(PdfWriter writer, float domain[], float range[], float c0[], float c1[], float n) {
+ PdfFunction func = new PdfFunction(writer);
+ func.dictionary = new PdfDictionary();
+ func.dictionary.put(PdfName.FUNCTIONTYPE, new PdfNumber(2));
+ func.dictionary.put(PdfName.DOMAIN, new PdfArray(domain));
+ if (range != null)
+ func.dictionary.put(PdfName.RANGE, new PdfArray(range));
+ if (c0 != null)
+ func.dictionary.put(PdfName.C0, new PdfArray(c0));
+ if (c1 != null)
+ func.dictionary.put(PdfName.C1, new PdfArray(c1));
+ func.dictionary.put(PdfName.N, new PdfNumber(n));
+ return func;
+ }
+
+ public static PdfFunction type3(PdfWriter writer, float domain[], float range[], PdfFunction functions[], float bounds[], float encode[]) {
+ PdfFunction func = new PdfFunction(writer);
+ func.dictionary = new PdfDictionary();
+ func.dictionary.put(PdfName.FUNCTIONTYPE, new PdfNumber(3));
+ func.dictionary.put(PdfName.DOMAIN, new PdfArray(domain));
+ if (range != null)
+ func.dictionary.put(PdfName.RANGE, new PdfArray(range));
+ PdfArray array = new PdfArray();
+ for (int k = 0; k < functions.length; ++k)
+ array.add(functions[k].getReference());
+ func.dictionary.put(PdfName.FUNCTIONS, array);
+ func.dictionary.put(PdfName.BOUNDS, new PdfArray(bounds));
+ func.dictionary.put(PdfName.ENCODE, new PdfArray(encode));
+ return func;
+ }
+
+ public static PdfFunction type4(PdfWriter writer, float domain[], float range[], String postscript) {
+ byte b[] = new byte[postscript.length()];
+ for (int k = 0; k < b.length; ++k)
+ b[k] = (byte)postscript.charAt(k);
+ PdfFunction func = new PdfFunction(writer);
+ func.dictionary = new PdfStream(b);
+ ((PdfStream)func.dictionary).flateCompress();
+ func.dictionary.put(PdfName.FUNCTIONTYPE, new PdfNumber(4));
+ func.dictionary.put(PdfName.DOMAIN, new PdfArray(domain));
+ func.dictionary.put(PdfName.RANGE, new PdfArray(range));
+ return func;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfGState.java b/src/main/java/com/lowagie/text/pdf/PdfGState.java
new file mode 100644
index 0000000..6ea1ebc
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfGState.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2003 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+/** The graphic state dictionary.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfGState extends PdfDictionary {
+ /** A possible blend mode */
+ public static final PdfName BM_NORMAL = new PdfName("Normal");
+ /** A possible blend mode */
+ public static final PdfName BM_COMPATIBLE = new PdfName("Compatible");
+ /** A possible blend mode */
+ public static final PdfName BM_MULTIPLY = new PdfName("Multiply");
+ /** A possible blend mode */
+ public static final PdfName BM_SCREEN = new PdfName("Screen");
+ /** A possible blend mode */
+ public static final PdfName BM_OVERLAY = new PdfName("Overlay");
+ /** A possible blend mode */
+ public static final PdfName BM_DARKEN = new PdfName("Darken");
+ /** A possible blend mode */
+ public static final PdfName BM_LIGHTEN = new PdfName("Lighten");
+ /** A possible blend mode */
+ public static final PdfName BM_COLORDODGE = new PdfName("ColorDodge");
+ /** A possible blend mode */
+ public static final PdfName BM_COLORBURN = new PdfName("ColorBurn");
+ /** A possible blend mode */
+ public static final PdfName BM_HARDLIGHT = new PdfName("HardLight");
+ /** A possible blend mode */
+ public static final PdfName BM_SOFTLIGHT = new PdfName("SoftLight");
+ /** A possible blend mode */
+ public static final PdfName BM_DIFFERENCE = new PdfName("Difference");
+ /** A possible blend mode */
+ public static final PdfName BM_EXCLUSION = new PdfName("Exclusion");
+
+ /**
+ * Sets the flag whether to apply overprint for stroking.
+ * @param ov
+ */
+ public void setOverPrintStroking(boolean ov) {
+ put(PdfName.OP, ov ? PdfBoolean.PDFTRUE : PdfBoolean.PDFFALSE);
+ }
+
+ /**
+ * Sets the flag whether to apply overprint for non stroking painting operations.
+ * @param ov
+ */
+ public void setOverPrintNonStroking(boolean ov) {
+ put(PdfName.op, ov ? PdfBoolean.PDFTRUE : PdfBoolean.PDFFALSE);
+ }
+
+ /**
+ * Sets the current stroking alpha constant, specifying the constant shape or
+ * constant opacity value to be used for stroking operations in the transparent
+ * imaging model.
+ * @param n
+ */
+ public void setStrokeOpacity(float n) {
+ put(PdfName.CA, new PdfNumber(n));
+ }
+
+ /**
+ * Sets the current stroking alpha constant, specifying the constant shape or
+ * constant opacity value to be used for nonstroking operations in the transparent
+ * imaging model.
+ * @param n
+ */
+ public void setFillOpacity(float n) {
+ put(PdfName.ca, new PdfNumber(n));
+ }
+
+ /**
+ * The alpha source flag specifying whether the current soft mask
+ * and alpha constant are to be interpreted as shape values (true)
+ * or opacity values (false).
+ * @param v
+ */
+ public void setAlphaIsShape(boolean v) {
+ put(PdfName.AIS, v ? PdfBoolean.PDFTRUE : PdfBoolean.PDFFALSE);
+ }
+
+ /**
+ * Determines the behaviour of overlapping glyphs within a text object
+ * in the transparent imaging model.
+ * @param v
+ */
+ public void setTextKnockout(boolean v) {
+ put(PdfName.TK, v ? PdfBoolean.PDFTRUE : PdfBoolean.PDFFALSE);
+ }
+
+ /**
+ * The current blend mode to be used in the transparent imaging model.
+ * @param bm
+ */
+ public void setBlendMode(PdfName bm) {
+ put(PdfName.BM, bm);
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfGraphics2D.java b/src/main/java/com/lowagie/text/pdf/PdfGraphics2D.java
new file mode 100644
index 0000000..962b502
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfGraphics2D.java
@@ -0,0 +1,1450 @@
+/*
+ * Copyright 2002 by Jim Moore PdfICCBased
defines a ColorSpace
+ *
+ * @see PdfStream
+ */
+
+class PdfICCBased extends PdfStream {
+
+ protected int NumberOfComponents;
+
+ PdfICCBased(ICC_Profile profile) {
+ super();
+ try {
+ NumberOfComponents = profile.getNumComponents();
+ switch (NumberOfComponents) {
+ case 1:
+ put(PdfName.ALTERNATE, PdfName.DEVICEGRAY);
+ break;
+ case 3:
+ put(PdfName.ALTERNATE, PdfName.DEVICERGB);
+ break;
+ case 4:
+ put(PdfName.ALTERNATE, PdfName.DEVICECMYK);
+ break;
+ default:
+ throw new PdfException(NumberOfComponents + " component(s) is not supported in PDF1.4");
+ }
+ put(PdfName.N, new PdfNumber(NumberOfComponents));
+ bytes = profile.getData();
+ flateCompress();
+ } catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfImage.java b/src/main/java/com/lowagie/text/pdf/PdfImage.java
new file mode 100644
index 0000000..46d737f
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfImage.java
@@ -0,0 +1,279 @@
+/*
+ * $Id: PdfImage.java,v 1.66 2005/11/01 12:27:05 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ *
+ * REMARK:
+ * LZW/GIF is covered by a software patent which is owned by Unisys Corporation.
+ * Unisys refuses to license this patent for PDF-related use in software
+ * even when this software is released for free and may be freely distributed.
+ * HOWEVER:
+ * This library doesn't compress or decompress data using the LZW
+ * algorithm, nor does it create or visualize GIF-images in any way;
+ * it only copies parts of an existing GIF file into a PDF file.
+ *
+ * More information about the GIF format can be found in the following documents:
+ * * GRAPHICS INTERCHANGE FORMAT(sm) Version 89a
+ * (c)1987,1988,1989,1990 Copyright CompuServe Incorporated. Columbus, Ohio
+ * * LZW and GIF explained
+ * Steve Blackstock
+ * * http://mistress.informatik.unibw-muenchen.de/
+ * very special thanks to klee@informatik.unibw-muenchen.de for the algorithm
+ * to extract the LZW data from a GIF.
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.Image;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * PdfImage
is a PdfStream
containing an image-Dictionary
and -stream.
+ */
+
+class PdfImage extends PdfStream {
+
+ static final int TRANSFERSIZE = 4096;
+ // membervariables
+
+ /** This is the PdfName
of the image. */
+ protected PdfName name = null;
+
+ // constructor
+
+ /**
+ * Constructs a PdfImage
-object.
+ *
+ * @param image the Image
-object
+ * @param name the PdfName
for this image
+ * @throws BadPdfFormatException on error
+ */
+
+ public PdfImage(Image image, String name, PdfIndirectReference maskRef) throws BadPdfFormatException {
+ super();
+ this.name = new PdfName(name);
+ put(PdfName.TYPE, PdfName.XOBJECT);
+ put(PdfName.SUBTYPE, PdfName.IMAGE);
+ put(PdfName.WIDTH, new PdfNumber(image.width()));
+ put(PdfName.HEIGHT, new PdfNumber(image.height()));
+ if (image.getLayer() != null)
+ put(PdfName.OC, image.getLayer().getRef());
+ if (image.isMask() && (image.bpc() == 1 || image.bpc() > 0xff))
+ put(PdfName.IMAGEMASK, PdfBoolean.PDFTRUE);
+ if (maskRef != null) {
+ if (image.isSmask())
+ put(PdfName.SMASK, maskRef);
+ else
+ put(PdfName.MASK, maskRef);
+ }
+ if (image.isMask() && image.isInvertMask())
+ put(PdfName.DECODE, new PdfLiteral("[1 0]"));
+ if (image.isInterpolation())
+ put(PdfName.INTERPOLATE, PdfBoolean.PDFTRUE);
+ InputStream is = null;
+ try {
+
+ // Raw Image data
+ if (image.isImgRaw()) {
+ // will also have the CCITT parameters
+ int colorspace = image.colorspace();
+ int transparency[] = image.getTransparency();
+ if (transparency != null && !image.isMask() && maskRef == null) {
+ String s = "[";
+ for (int k = 0; k < transparency.length; ++k)
+ s += transparency[k] + " ";
+ s += "]";
+ put(PdfName.MASK, new PdfLiteral(s));
+ }
+ bytes = image.rawData();
+ put(PdfName.LENGTH, new PdfNumber(bytes.length));
+ int bpc = image.bpc();
+ if (bpc > 0xff) {
+ if (!image.isMask())
+ put(PdfName.COLORSPACE, PdfName.DEVICEGRAY);
+ put(PdfName.BITSPERCOMPONENT, new PdfNumber(1));
+ put(PdfName.FILTER, PdfName.CCITTFAXDECODE);
+ int k = bpc - Image.CCITTG3_1D;
+ PdfDictionary decodeparms = new PdfDictionary();
+ if (k != 0)
+ decodeparms.put(PdfName.K, new PdfNumber(k));
+ if ((colorspace & Image.CCITT_BLACKIS1) != 0)
+ decodeparms.put(PdfName.BLACKIS1, PdfBoolean.PDFTRUE);
+ if ((colorspace & Image.CCITT_ENCODEDBYTEALIGN) != 0)
+ decodeparms.put(PdfName.ENCODEDBYTEALIGN, PdfBoolean.PDFTRUE);
+ if ((colorspace & Image.CCITT_ENDOFLINE) != 0)
+ decodeparms.put(PdfName.ENDOFLINE, PdfBoolean.PDFTRUE);
+ if ((colorspace & Image.CCITT_ENDOFBLOCK) != 0)
+ decodeparms.put(PdfName.ENDOFBLOCK, PdfBoolean.PDFFALSE);
+ decodeparms.put(PdfName.COLUMNS, new PdfNumber(image.width()));
+ decodeparms.put(PdfName.ROWS, new PdfNumber(image.height()));
+ put(PdfName.DECODEPARMS, decodeparms);
+ }
+ else {
+ switch(colorspace) {
+ case 1:
+ put(PdfName.COLORSPACE, PdfName.DEVICEGRAY);
+ if (image.isInverted())
+ put(PdfName.DECODE, new PdfLiteral("[1 0]"));
+ break;
+ case 3:
+ put(PdfName.COLORSPACE, PdfName.DEVICERGB);
+ if (image.isInverted())
+ put(PdfName.DECODE, new PdfLiteral("[1 0 1 0 1 0]"));
+ break;
+ case 4:
+ default:
+ put(PdfName.COLORSPACE, PdfName.DEVICECMYK);
+ if (image.isInverted())
+ put(PdfName.DECODE, new PdfLiteral("[1 0 1 0 1 0 1 0]"));
+ }
+ PdfDictionary additional = image.getAdditional();
+ if (additional != null)
+ putAll(additional);
+ if (image.isMask() && (image.bpc() == 1 || image.bpc() > 8))
+ remove(PdfName.COLORSPACE);
+ put(PdfName.BITSPERCOMPONENT, new PdfNumber(image.bpc()));
+ if (image.isDeflated())
+ put(PdfName.FILTER, PdfName.FLATEDECODE);
+ else {
+ flateCompress();
+ }
+ }
+ return;
+ }
+
+ // GIF, JPEG or PNG
+ String errorID;
+ if (image.rawData() == null){
+ is = image.url().openStream();
+ errorID = image.url().toString();
+ }
+ else{
+ is = new java.io.ByteArrayInputStream(image.rawData());
+ errorID = "Byte array";
+ }
+ switch(image.type()) {
+ case Image.JPEG:
+ put(PdfName.FILTER, PdfName.DCTDECODE);
+ switch(image.colorspace()) {
+ case 1:
+ put(PdfName.COLORSPACE, PdfName.DEVICEGRAY);
+ break;
+ case 3:
+ put(PdfName.COLORSPACE, PdfName.DEVICERGB);
+ break;
+ default:
+ put(PdfName.COLORSPACE, PdfName.DEVICECMYK);
+ if (image.isInverted()) {
+ put(PdfName.DECODE, new PdfLiteral("[1 0 1 0 1 0 1 0]"));
+ }
+ }
+ put(PdfName.BITSPERCOMPONENT, new PdfNumber(8));
+ if (image.rawData() != null){
+ bytes = image.rawData();
+ put(PdfName.LENGTH, new PdfNumber(bytes.length));
+ return;
+ }
+ streamBytes = new ByteArrayOutputStream();
+ transferBytes(is, streamBytes, -1);
+ break;
+ default:
+ throw new BadPdfFormatException(errorID + " is an unknown Image format.");
+ }
+ put(PdfName.LENGTH, new PdfNumber(streamBytes.size()));
+ }
+ catch(IOException ioe) {
+ throw new BadPdfFormatException(ioe.getMessage());
+ }
+ finally {
+ if (is != null) {
+ try{
+ is.close();
+ }
+ catch (Exception ee) {
+ // empty on purpose
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the PdfName
of the image.
+ *
+ * @return the name
+ */
+
+ public PdfName name() {
+ return name;
+ }
+
+ static void transferBytes(InputStream in, OutputStream out, int len) throws IOException {
+ byte buffer[] = new byte[TRANSFERSIZE];
+ if (len < 0)
+ len = 0x7ffffff;
+ int size;
+ while (len != 0) {
+ size = in.read(buffer, 0, Math.min(len, TRANSFERSIZE));
+ if (size < 0)
+ return;
+ out.write(buffer, 0, size);
+ len -= size;
+ }
+ }
+
+ protected void importAll(PdfImage dup) {
+ name = dup.name;
+ compressed = dup.compressed;
+ streamBytes = dup.streamBytes;
+ bytes = dup.bytes;
+ hashMap = dup.hashMap;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfImportedPage.java b/src/main/java/com/lowagie/text/pdf/PdfImportedPage.java
new file mode 100644
index 0000000..7e6566e
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfImportedPage.java
@@ -0,0 +1,149 @@
+/*
+ * $Id: PdfImportedPage.java,v 1.48 2005/05/04 14:32:39 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Image;
+import java.io.IOException;
+
+/** Represents an imported page.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfImportedPage extends com.lowagie.text.pdf.PdfTemplate {
+
+ PdfReaderInstance readerInstance;
+ int pageNumber;
+
+ PdfImportedPage(PdfReaderInstance readerInstance, PdfWriter writer, int pageNumber) {
+ this.readerInstance = readerInstance;
+ this.pageNumber = pageNumber;
+ thisReference = writer.getPdfIndirectReference();
+ bBox = readerInstance.getReader().getPageSize(pageNumber);
+ type = TYPE_IMPORTED;
+ }
+
+ /** Reads the content from this PdfImportedPage
-object from a reader.
+ *
+ * @return self
+ *
+ */
+ public PdfImportedPage getFromReader() {
+ return this;
+ }
+
+ public int getPageNumber() {
+ return pageNumber;
+ }
+
+
+ /** Always throws an error. This operation is not allowed.
+ * @param image dummy
+ * @param a dummy
+ * @param b dummy
+ * @param c dummy
+ * @param d dummy
+ * @param e dummy
+ * @param f dummy
+ * @throws DocumentException dummy */
+ public void addImage(Image image, float a, float b, float c, float d, float e, float f) throws DocumentException {
+ throwError();
+ }
+
+ /** Always throws an error. This operation is not allowed.
+ * @param template dummy
+ * @param a dummy
+ * @param b dummy
+ * @param c dummy
+ * @param d dummy
+ * @param e dummy
+ * @param f dummy */
+ public void addTemplate(PdfTemplate template, float a, float b, float c, float d, float e, float f) {
+ throwError();
+ }
+
+ /** Always throws an error. This operation is not allowed.
+ * @return dummy */
+ public PdfContentByte getDuplicate() {
+ throwError();
+ return null;
+ }
+
+ PdfStream getFormXObject() throws IOException {
+ return readerInstance.getFormXObject(pageNumber);
+ }
+
+ public void setColorFill(PdfSpotColor sp, float tint) {
+ throwError();
+ }
+
+ public void setColorStroke(PdfSpotColor sp, float tint) {
+ throwError();
+ }
+
+ PdfObject getResources() {
+ return readerInstance.getResources(pageNumber);
+ }
+
+ /** Always throws an error. This operation is not allowed.
+ * @param bf dummy
+ * @param size dummy */
+ public void setFontAndSize(BaseFont bf, float size) {
+ throwError();
+ }
+
+ void throwError() {
+ throw new RuntimeException("Content can not be added to a PdfImportedPage.");
+ }
+
+ PdfReaderInstance getPdfReaderInstance() {
+ return readerInstance;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfIndirectObject.java b/src/main/java/com/lowagie/text/pdf/PdfIndirectObject.java
new file mode 100644
index 0000000..81ef485
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfIndirectObject.java
@@ -0,0 +1,170 @@
+/*
+ * $Id: PdfIndirectObject.java,v 1.60 2005/05/04 14:31:40 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.lowagie.text.DocWriter;
+
+/**
+ * PdfIndirectObject
is the Pdf indirect object.
+ * PdfObject
may be labeled as an indirect object.
+ * An indirect object consists of an object identifier, a direct object, and the endobj
+ * keyword. The object identifier consists of an integer object number, an integer
+ * generation number, and the obj keyword.
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 4.10 (page 53).
+ *
+ * @see PdfObject
+ * @see PdfIndirectReference
+ */
+
+public class PdfIndirectObject {
+
+ // membervariables
+
+/** The object number */
+ protected int number;
+
+/** the generation number */
+ protected int generation = 0;
+
+ static final byte STARTOBJ[] = DocWriter.getISOBytes(" obj");
+ static final byte ENDOBJ[] = DocWriter.getISOBytes("\nendobj\n");
+ static final int SIZEOBJ = STARTOBJ.length + ENDOBJ.length;
+ PdfObject object;
+ PdfWriter writer;
+
+ // constructors
+
+/**
+ * Constructs a PdfIndirectObject
.
+ *
+ * @param number the object number
+ * @param object the direct object
+ */
+
+ PdfIndirectObject(int number, PdfObject object, PdfWriter writer) {
+ this(number, 0, object, writer);
+ }
+
+ PdfIndirectObject(PdfIndirectReference ref, PdfObject object, PdfWriter writer) {
+ this(ref.getNumber(),ref.getGeneration(),object,writer);
+ }
+/**
+ * Constructs a PdfIndirectObject
.
+ *
+ * @param number the object number
+ * @param generation the generation number
+ * @param object the direct object
+ */
+
+ PdfIndirectObject(int number, int generation, PdfObject object, PdfWriter writer) {
+ this.writer = writer;
+ this.number = number;
+ this.generation = generation;
+ this.object = object;
+ PdfEncryption crypto = null;
+ if (writer != null)
+ crypto = writer.getEncryption();
+ if (crypto != null) {
+ crypto.setHashKey(number, generation);
+ }
+ }
+
+ // methods
+
+/**
+ * Return the length of this PdfIndirectObject
.
+ *
+ * @return the length of the PDF-representation of this indirect object.
+ */
+
+// public int length() {
+// if (isStream)
+// return bytes.size() + SIZEOBJ + stream.getStreamLength(writer);
+// else
+// return bytes.size();
+// }
+
+
+/**
+ * Returns a PdfIndirectReference
to this PdfIndirectObject
.
+ *
+ * @return a PdfIndirectReference
+ */
+
+ public PdfIndirectReference getIndirectReference() {
+ return new PdfIndirectReference(object.type(), number, generation);
+ }
+
+/**
+ * Writes eficiently to a stream
+ *
+ * @param os the stream to write to
+ * @throws IOException on write error
+ */
+ void writeTo(OutputStream os) throws IOException
+ {
+ os.write(DocWriter.getISOBytes(String.valueOf(number)));
+ os.write(' ');
+ os.write(DocWriter.getISOBytes(String.valueOf(generation)));
+ os.write(STARTOBJ);
+ int type = object.type();
+ if (type != PdfObject.ARRAY && type != PdfObject.DICTIONARY && type != PdfObject.NAME && type != PdfObject.STRING)
+ os.write(' ');
+ object.toPdf(writer, os);
+ os.write(ENDOBJ);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfIndirectReference.java b/src/main/java/com/lowagie/text/pdf/PdfIndirectReference.java
new file mode 100644
index 0000000..3b0e89c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfIndirectReference.java
@@ -0,0 +1,133 @@
+/*
+ * $Id: PdfIndirectReference.java,v 1.55 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * PdfIndirectReference
contains a reference to a PdfIndirectObject
.
+ *
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 4.11 (page 54).
+ *
+ * @see PdfObject
+ * @see PdfIndirectObject
+ */
+
+public class PdfIndirectReference extends PdfObject {
+
+ // membervariables
+
+/** the object number */
+ protected int number;
+
+/** the generation number */
+ protected int generation = 0;
+
+ // constructors
+
+ protected PdfIndirectReference() {
+ super(0);
+ }
+
+/**
+ * Constructs a PdfIndirectReference
.
+ *
+ * @param type the type of the PdfObject
that is referenced to
+ * @param number the object number.
+ * @param generation the generation number.
+ */
+
+ PdfIndirectReference(int type, int number, int generation) {
+ super(0, new StringBuffer().append(number).append(" ").append(generation).append(" R").toString());
+ this.number = number;
+ this.generation = generation;
+ }
+
+/**
+ * Constructs a PdfIndirectReference
.
+ *
+ * @param type the type of the PdfObject
that is referenced to
+ * @param number the object number.
+ */
+
+ PdfIndirectReference(int type, int number) {
+ this(type, number, 0);
+ }
+
+ // methods
+
+/**
+ * Returns the number of the object.
+ *
+ * @return a number.
+ */
+
+ public int getNumber() {
+ return number;
+ }
+
+/**
+ * Returns the generation of the object.
+ *
+ * @return a number.
+ */
+
+ public int getGeneration() {
+ return generation;
+ }
+
+ public String toString() {
+ return new StringBuffer().append(number).append(" ").append(generation).append(" R").toString();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfLayer.java b/src/main/java/com/lowagie/text/pdf/PdfLayer.java
new file mode 100644
index 0000000..bf53904
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfLayer.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.ArrayList;
+/**
+ * An optional content group is a dictionary representing a collection of graphics
+ * that can be made visible or invisible dynamically by users of viewer applications.
+ * In iText they are referenced as layers.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfLayer extends PdfDictionary implements PdfOCG {
+ protected PdfIndirectReference ref;
+ protected ArrayList children;
+ protected PdfLayer parent;
+ protected String title;
+
+ /**
+ * Holds value of property on.
+ */
+ private boolean on = true;
+
+ /**
+ * Holds value of property onPanel.
+ */
+ private boolean onPanel = true;
+
+ PdfLayer(String title) {
+ this.title = title;
+ }
+
+ /**
+ * Creates a title layer. A title layer is not really a layer but a collection of layers
+ * under the same title heading.
+ * @param title the title text
+ * @param writer the PdfWriter
+ * @return the title layer
+ */
+ public static PdfLayer createTitle(String title, PdfWriter writer) {
+ if (title == null)
+ throw new NullPointerException("Title cannot be null.");
+ PdfLayer layer = new PdfLayer(title);
+ writer.registerLayer(layer);
+ return layer;
+ }
+ /**
+ * Creates a new layer.
+ * @param name the name of the layer
+ * @param writer the writer
+ */
+ public PdfLayer(String name, PdfWriter writer) {
+ super(PdfName.OCG);
+ setName(name);
+ ref = writer.getPdfIndirectReference();
+ writer.registerLayer(this);
+ }
+
+ String getTitle() {
+ return title;
+ }
+
+ /**
+ * Adds a child layer. Nested layers can only have one parent.
+ * @param child the child layer
+ */
+ public void addChild(PdfLayer child) {
+ if (child.parent != null)
+ throw new IllegalArgumentException("The layer '" + ((PdfString)child.get(PdfName.NAME)).toUnicodeString() + "' already has a parent.");
+ child.parent = this;
+ if (children == null)
+ children = new ArrayList();
+ children.add(child);
+ }
+
+
+ /**
+ * Gets the parent layer.
+ * @return the parent layer or null
if the layer has no parent
+ */
+ public PdfLayer getParent() {
+ return parent;
+ }
+
+ /**
+ * Gets the children layers.
+ * @return the children layers or null
if the layer has no children
+ */
+ public ArrayList getChildren() {
+ return children;
+ }
+
+ /**
+ * Gets the PdfIndirectReference
that represents this layer.
+ * @return the PdfIndirectReference
that represents this layer
+ */
+ public PdfIndirectReference getRef() {
+ return ref;
+ }
+
+ /**
+ * Sets the name of this layer.
+ * @param name the name of this layer
+ */
+ public void setName(String name) {
+ put(PdfName.NAME, new PdfString(name, PdfObject.TEXT_UNICODE));
+ }
+
+ /**
+ * Gets the dictionary representing the layer. It just returns this
.
+ * @return the dictionary representing the layer
+ */
+ public PdfObject getPdfObject() {
+ return this;
+ }
+
+ /**
+ * Gets the initial visibility of the layer.
+ * @return the initial visibility of the layer
+ */
+ public boolean isOn() {
+ return this.on;
+ }
+
+ /**
+ * Sets the initial visibility of the layer.
+ * @param on the initial visibility of the layer
+ */
+ public void setOn(boolean on) {
+ this.on = on;
+ }
+
+ private PdfDictionary getUsage() {
+ PdfDictionary usage = (PdfDictionary)get(PdfName.USAGE);
+ if (usage == null) {
+ usage = new PdfDictionary();
+ put(PdfName.USAGE, usage);
+ }
+ return usage;
+ }
+
+ /**
+ * Used by the creating application to store application-specific
+ * data associated with this optional content group.
+ * @param creator a text string specifying the application that created the group
+ * @param subtype a string defining the type of content controlled by the group. Suggested
+ * values include but are not limited to Artwork, for graphic-design or publishing
+ * applications, and Technical, for technical designs such as building plans or
+ * schematics
+ */
+ public void setCreatorInfo(String creator, String subtype) {
+ PdfDictionary usage = getUsage();
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.CREATOR, new PdfString(creator, PdfObject.TEXT_UNICODE));
+ dic.put(PdfName.SUBTYPE, new PdfName(subtype));
+ usage.put(PdfName.CREATORINFO, dic);
+ }
+
+ /**
+ * Specifies the language of the content controlled by this
+ * optional content group
+ * @param lang a language string which specifies a language and possibly a locale
+ * (for example, es-MX represents Mexican Spanish)
+ * @param preferred used by viewer applications when there is a partial match but no exact
+ * match between the system language and the language strings in all usage dictionaries
+ */
+ public void setLanguage(String lang, boolean preferred) {
+ PdfDictionary usage = getUsage();
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.LANG, new PdfString(lang, PdfObject.TEXT_UNICODE));
+ if (preferred)
+ dic.put(PdfName.PREFERRED, PdfName.ON);
+ usage.put(PdfName.LANGUAGE, dic);
+ }
+
+ /**
+ * Specifies the recommended state for content in this
+ * group when the document (or part of it) is saved by a viewer application to a format
+ * that does not support optional content (for example, an earlier version of
+ * PDF or a raster image format).
+ * @param export the export state
+ */
+ public void setExport(boolean export) {
+ PdfDictionary usage = getUsage();
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.EXPORTSTATE, export ? PdfName.ON : PdfName.OFF);
+ usage.put(PdfName.EXPORT, dic);
+ }
+
+ /**
+ * Specifies a range of magnifications at which the content
+ * in this optional content group is best viewed.
+ * @param min the minimum recommended magnification factors at which the group
+ * should be ON. A negative value will set the default to 0
+ * @param max the maximum recommended magnification factor at which the group
+ * should be ON. A negative value will set the largest possible magnification supported by the
+ * viewer application
+ */
+ public void setZoom(float min, float max) {
+ if (min <= 0 && max < 0)
+ return;
+ PdfDictionary usage = getUsage();
+ PdfDictionary dic = new PdfDictionary();
+ if (min > 0)
+ dic.put(PdfName.MIN, new PdfNumber(min));
+ if (max >= 0)
+ dic.put(PdfName.MAX, new PdfNumber(max));
+ usage.put(PdfName.ZOOM, dic);
+ }
+
+ /**
+ * Specifies that the content in this group is intended for
+ * use in printing
+ * @param subtype a name specifying the kind of content controlled by the group;
+ * for example, Trapping, PrintersMarks and Watermark
+ * @param printstate indicates that the group should be
+ * set to that state when the document is printed from a viewer application
+ */
+ public void setPrint(String subtype, boolean printstate) {
+ PdfDictionary usage = getUsage();
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.SUBTYPE, new PdfName(subtype));
+ dic.put(PdfName.PRINTSTATE, printstate ? PdfName.ON : PdfName.OFF);
+ usage.put(PdfName.PRINT, dic);
+ }
+
+ /**
+ * Indicates that the group should be set to that state when the
+ * document is opened in a viewer application.
+ * @param view the view state
+ */
+ public void setView(boolean view) {
+ PdfDictionary usage = getUsage();
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.VIEWSTATE, view ? PdfName.ON : PdfName.OFF);
+ usage.put(PdfName.VIEW, dic);
+ }
+
+ /**
+ * Gets the layer visibility in Acrobat's layer panel
+ * @return the layer visibility in Acrobat's layer panel
+ */
+ public boolean isOnPanel() {
+ return this.onPanel;
+ }
+
+ /**
+ * Sets the visibility of the layer in Acrobat's layer panel. If false
+ * the layer cannot be directly manipulated by the user. Note that any children layers will
+ * also be absent from the panel.
+ * @param onPanel the visibility of the layer in Acrobat's layer panel
+ */
+ public void setOnPanel(boolean onPanel) {
+ this.onPanel = onPanel;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfLayerMembership.java b/src/main/java/com/lowagie/text/pdf/PdfLayerMembership.java
new file mode 100644
index 0000000..3a5be50
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfLayerMembership.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.HashSet;
+import java.util.Collection;
+
+/**
+ * Content typically belongs to a single optional content group,
+ * and is visible when the group is ON and invisible when it is OFF. To express more
+ * complex visibility policies, content should not declare itself to belong to an optional
+ * content group directly, but rather to an optional content membership dictionary
+ * represented by this class.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfLayerMembership extends PdfDictionary implements PdfOCG {
+
+ /**
+ * Visible only if all of the entries are ON.
+ */
+ public static PdfName ALLON = new PdfName("AllOn");
+ /**
+ * Visible if any of the entries are ON.
+ */
+ public static PdfName ANYON = new PdfName("AnyOn");
+ /**
+ * Visible if any of the entries are OFF.
+ */
+ public static PdfName ANYOFF = new PdfName("AnyOff");
+ /**
+ * Visible only if all of the entries are OFF.
+ */
+ public static PdfName ALLOFF = new PdfName("AllOff");
+
+ PdfIndirectReference ref;
+ PdfArray members = new PdfArray();
+ HashSet layers = new HashSet();
+
+ /**
+ * Creates a new, empty, membership layer.
+ * @param writer the writer
+ */
+ public PdfLayerMembership(PdfWriter writer) {
+ super(PdfName.OCMD);
+ put(PdfName.OCGS, members);
+ ref = writer.getPdfIndirectReference();
+ }
+
+ /**
+ * Gets the PdfIndirectReference
that represents this membership layer.
+ * @return the PdfIndirectReference
that represents this layer
+ */
+ public PdfIndirectReference getRef() {
+ return ref;
+ }
+
+ /**
+ * Adds a new member to the layer.
+ * @param layer the new member to the layer
+ */
+ public void addMember(PdfLayer layer) {
+ if (!layers.contains(layer)) {
+ members.add(layer.getRef());
+ layers.add(layer);
+ }
+ }
+
+ /**
+ * Gets the member layers.
+ * @return the member layers
+ */
+ public Collection getLayers() {
+ return layers;
+ }
+
+ /**
+ * Sets the visibility policy for content belonging to this
+ * membership dictionary. Possible values are ALLON, ANYON, ANYOFF and ALLOFF.
+ * The default value is ANYON.
+ * @param type the visibility policy
+ */
+ public void setVisibilityPolicy(PdfName type) {
+ put(PdfName.P, type);
+ }
+
+ /**
+ * Gets the dictionary representing the membership layer. It just returns this
.
+ * @return the dictionary representing the layer
+ */
+ public PdfObject getPdfObject() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfLine.java b/src/main/java/com/lowagie/text/pdf/PdfLine.java
new file mode 100644
index 0000000..8ccd84a
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfLine.java
@@ -0,0 +1,477 @@
+/*
+ * $Id: PdfLine.java,v 1.66 2006/01/18 20:35:48 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import com.lowagie.text.Element;
+import com.lowagie.text.ListItem;
+import com.lowagie.text.Chunk;
+
+/**
+ * PdfLine
defines an array with PdfChunk
-objects
+ * that fit into 1 line.
+ */
+
+public class PdfLine {
+
+ // membervariables
+
+ /** The arraylist containing the chunks. */
+ protected ArrayList line;
+
+ /** The left indentation of the line. */
+ protected float left;
+
+ /** The width of the line. */
+ protected float width;
+
+ /** The alignment of the line. */
+ protected int alignment;
+
+ /** The heigth of the line. */
+ protected float height;
+
+ /** The listsymbol (if necessary). */
+ protected Chunk listSymbol = null;
+
+ /** The listsymbol (if necessary). */
+ protected float symbolIndent;
+
+ /** true
if the chunk splitting was caused by a newline. */
+ protected boolean newlineSplit = false;
+
+ /** The original width. */
+ protected float originalWidth;
+
+ protected boolean isRTL = false;
+
+ // constructors
+
+ /**
+ * Constructs a new PdfLine
-object.
+ *
+ * @param left the limit of the line at the left
+ * @param right the limit of the line at the right
+ * @param alignment the alignment of the line
+ * @param height the height of the line
+ */
+
+ PdfLine(float left, float right, int alignment, float height) {
+ this.left = left;
+ this.width = right - left;
+ this.originalWidth = this.width;
+ this.alignment = alignment;
+ this.height = height;
+ this.line = new ArrayList();
+ }
+
+ PdfLine(float left, float remainingWidth, int alignment, boolean newlineSplit, ArrayList line, boolean isRTL) {
+ this.left = left;
+ this.width = remainingWidth;
+ this.alignment = alignment;
+ this.line = line;
+ this.newlineSplit = newlineSplit;
+ this.isRTL = isRTL;
+ }
+
+ // methods
+
+ /**
+ * Adds a PdfChunk
to the PdfLine
.
+ *
+ * @param chunk the PdfChunk
to add
+ * @return null
if the chunk could be added completely; if not
+ * a PdfChunk
containing the part of the chunk that could
+ * not be added is returned
+ */
+
+ PdfChunk add(PdfChunk chunk) {
+ // nothing happens if the chunk is null.
+ if (chunk == null || chunk.toString().equals("")) {
+ return null;
+ }
+
+ // we split the chunk to be added
+ PdfChunk overflow = chunk.split(width);
+ newlineSplit = (chunk.isNewlineSplit() || overflow == null);
+ // if (chunk.isNewlineSplit() && alignment == Element.ALIGN_JUSTIFIED)
+ // alignment = Element.ALIGN_LEFT;
+
+
+ // if the length of the chunk > 0 we add it to the line
+ if (chunk.length() > 0) {
+ if (overflow != null)
+ chunk.trimLastSpace();
+ width -= chunk.width();
+ line.add(chunk);
+ }
+
+ // if the length == 0 and there were no other chunks added to the line yet,
+ // we risk to end up in an endless loop trying endlessly to add the same chunk
+ else if (line.size() < 1) {
+ chunk = overflow;
+ overflow = chunk.truncate(width);
+ width -= chunk.width();
+ if (chunk.length() > 0) {
+ line.add(chunk);
+ return overflow;
+ }
+ // if the chunck couldn't even be truncated, we add everything, so be it
+ else {
+ if (overflow != null)
+ line.add(overflow);
+ return null;
+ }
+ }
+ else {
+ width += ((PdfChunk)(line.get(line.size() - 1))).trimLastSpace();
+ }
+ return overflow;
+ }
+
+ // methods to retrieve information
+
+ /**
+ * Returns the number of chunks in the line.
+ *
+ * @return a value
+ */
+
+ public int size() {
+ return line.size();
+ }
+
+ /**
+ * Returns an iterator of PdfChunk
s.
+ *
+ * @return an Iterator
+ */
+
+ public Iterator iterator() {
+ return line.iterator();
+ }
+
+ /**
+ * Returns the height of the line.
+ *
+ * @return a value
+ */
+
+ float height() {
+ return height;
+ }
+
+ /**
+ * Returns the left indentation of the line taking the alignment of the line into account.
+ *
+ * @return a value
+ */
+
+ float indentLeft() {
+ if (isRTL) {
+ switch (alignment) {
+ case Element.ALIGN_LEFT:
+ return left + width;
+ case Element.ALIGN_CENTER:
+ return left + (width / 2f);
+ default:
+ return left;
+ }
+ }
+ else {
+ switch (alignment) {
+ case Element.ALIGN_RIGHT:
+ return left + width;
+ case Element.ALIGN_CENTER:
+ return left + (width / 2f);
+ default:
+ return left;
+ }
+ }
+ }
+
+ /**
+ * Checks if this line has to be justified.
+ *
+ * @return true
if the alignment equals ALIGN_JUSTIFIED and there is some width left.
+ */
+
+ public boolean hasToBeJustified() {
+ return ((alignment == Element.ALIGN_JUSTIFIED || alignment == Element.ALIGN_JUSTIFIED_ALL) && width != 0);
+ }
+
+ /**
+ * Resets the alignment of this line.
+ * Paragraph
+ * that has to be justified, has to be reset to ALIGN_LEFT.
+ */
+
+ public void resetAlignment() {
+ if (alignment == Element.ALIGN_JUSTIFIED) {
+ alignment = Element.ALIGN_LEFT;
+ }
+ }
+
+ /**
+ * Returns the width that is left, after a maximum of characters is added to the line.
+ *
+ * @return a value
+ */
+
+ float widthLeft() {
+ return width;
+ }
+
+ /**
+ * Returns the number of space-characters in this line.
+ *
+ * @return a value
+ */
+
+ int numberOfSpaces() {
+ String string = toString();
+ int length = string.length();
+ int numberOfSpaces = 0;
+ for (int i = 0; i < length; i++) {
+ if (string.charAt(i) == ' ') {
+ numberOfSpaces++;
+ }
+ }
+ return numberOfSpaces;
+ }
+
+ /**
+ * Sets the listsymbol of this line.
+ * ListItem
.
+ *
+ * @param listItem the list symbol
+ */
+
+ public void setListItem(ListItem listItem) {
+ this.listSymbol = listItem.listSymbol();
+ this.symbolIndent = listItem.indentationLeft();
+ }
+
+ /**
+ * Returns the listsymbol of this line.
+ *
+ * @return a PdfChunk
if the line has a listsymbol; null
otherwise
+ */
+
+ public Chunk listSymbol() {
+ return listSymbol;
+ }
+
+ /**
+ * Return the indentation needed to show the listsymbol.
+ *
+ * @return a value
+ */
+
+ public float listIndent() {
+ return symbolIndent;
+ }
+
+ /**
+ * Get the string representation of what is in this line.
+ *
+ * @return a String
+ */
+
+ public String toString() {
+ StringBuffer tmp = new StringBuffer();
+ for (Iterator i = line.iterator(); i.hasNext(); ) {
+ tmp.append(((PdfChunk) i.next()).toString());
+ }
+ return tmp.toString();
+ }
+
+ /**
+ * Checks if a newline caused the line split.
+ * @return true
if a newline caused the line split
+ */
+ public boolean isNewlineSplit() {
+ return newlineSplit && (alignment != Element.ALIGN_JUSTIFIED_ALL);
+ }
+
+ /**
+ * Gets the index of the last PdfChunk
with metric attributes
+ * @return the last PdfChunk
with metric attributes
+ */
+ public int getLastStrokeChunk() {
+ int lastIdx = line.size() - 1;
+ for (; lastIdx >= 0; --lastIdx) {
+ PdfChunk chunk = (PdfChunk)line.get(lastIdx);
+ if (chunk.isStroked())
+ break;
+ }
+ return lastIdx;
+ }
+
+ /**
+ * Gets a PdfChunk
by index.
+ * @param idx the index
+ * @return the PdfChunk
or null if beyond the array
+ */
+ public PdfChunk getChunk(int idx) {
+ if (idx < 0 || idx >= line.size())
+ return null;
+ return (PdfChunk)line.get(idx);
+ }
+
+ /**
+ * Gets the original width of the line.
+ * @return the original width of the line
+ */
+ public float getOriginalWidth() {
+ return originalWidth;
+ }
+
+ /**
+ * Gets the maximum size of all the fonts used in this line
+ * including images (if there are images in the line and if
+ * the leading has to be changed).
+ * @return maximum size of all the fonts used in this line
+ */
+ float getMaxSize() {
+ float maxSize = 0;
+ for (int k = 0; k < line.size(); ++k) {
+ PdfChunk chunk = (PdfChunk)line.get(k);
+ if (!chunk.isImage() || !chunk.changeLeading()) {
+ maxSize = Math.max(chunk.font().size(), maxSize);
+ }
+ else {
+ maxSize = Math.max(chunk.getImage().scaledHeight() + chunk.getImageOffsetY() , maxSize);
+ }
+ }
+ return maxSize;
+ }
+
+ /**
+ * Gets the maximum size of all the fonts used in this line
+ * including images.
+ * @return maximum size of all the fonts used in this line
+ */
+ float getMaxSizeSimple() {
+ float maxSize = 0;
+ for (int k = 0; k < line.size(); ++k) {
+ PdfChunk chunk = (PdfChunk)line.get(k);
+ if (!chunk.isImage()) {
+ maxSize = Math.max(chunk.font().size(), maxSize);
+ }
+ else {
+ maxSize = Math.max(chunk.getImage().scaledHeight() + chunk.getImageOffsetY() , maxSize);
+ }
+ }
+ return maxSize;
+ }
+
+ boolean isRTL() {
+ return isRTL;
+ }
+
+ /**
+ * Gets a width corrected with a charSpacing and wordSpacing.
+ * @param charSpacing
+ * @param wordSpacing
+ * @return a corrected width
+ */
+ public float getWidthCorrected(float charSpacing, float wordSpacing) {
+ float total = 0;
+ for (int k = 0; k < line.size(); ++k) {
+ PdfChunk ck = (PdfChunk)line.get(k);
+ total += ck.getWidthCorrected(charSpacing, wordSpacing);
+ }
+ return total;
+ }
+
+/**
+ * Gets the maximum size of the ascender for all the fonts used
+ * in this line.
+ * @return maximum size of all the ascenders used in this line
+ */
+ public float getAscender() {
+ float ascender = 0;
+ for (int k = 0; k < line.size(); ++k) {
+ PdfChunk ck = (PdfChunk)line.get(k);
+ if (ck.isImage())
+ ascender = Math.max(ascender, ck.getImage().scaledHeight() + ck.getImageOffsetY());
+ else {
+ PdfFont font = ck.font();
+ ascender = Math.max(ascender, font.getFont().getFontDescriptor(BaseFont.ASCENT, font.size()));
+ }
+ }
+ return ascender;
+ }
+
+/**
+ * Gets the biggest descender for all the fonts used
+ * in this line. Note that this is a negative number.
+ * @return maximum size of all the ascenders used in this line
+ */
+ public float getDescender() {
+ float descender = 0;
+ for (int k = 0; k < line.size(); ++k) {
+ PdfChunk ck = (PdfChunk)line.get(k);
+ if (ck.isImage())
+ descender = Math.min(descender, ck.getImageOffsetY());
+ else {
+ PdfFont font = ck.font();
+ descender = Math.min(descender, font.getFont().getFontDescriptor(BaseFont.DESCENT, font.size()));
+ }
+ }
+ return descender;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfLister.java b/src/main/java/com/lowagie/text/pdf/PdfLister.java
new file mode 100644
index 0000000..c3ecf63
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfLister.java
@@ -0,0 +1,187 @@
+/*
+ * $Id: PdfLister.java,v 1.34 2005/11/01 12:27:05 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * This class by Mark Thompson. Copyright (C) 2002 Mark Thompson
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+ package com.lowagie.text.pdf;
+
+import java.io.*;
+import java.util.Iterator;
+/**
+ * List a PDF file in human-readable form (for debugging reasons mostly)
+ * @author Mark Thompson
+ */
+
+public class PdfLister {
+
+ /** the printStream you want to write the output to. */
+ PrintStream out;
+
+ /**
+ * Create a new lister object.
+ * @param out
+ */
+ public PdfLister(PrintStream out) {
+ this.out = out;
+ }
+
+ /**
+ * Visualizes a PDF object.
+ * @param object a com.lowagie.text.pdf object
+ */
+ public void listAnyObject(PdfObject object)
+ {
+ switch (object.type()) {
+ case PdfObject.ARRAY:
+ listArray((PdfArray)object);
+ break;
+ case PdfObject.DICTIONARY:
+ listDict((PdfDictionary) object);
+ break;
+ case PdfObject.STRING:
+ out.println("(" + object.toString() + ")");
+ break;
+ default:
+ out.println(object.toString());
+ break;
+ }
+ }
+ /**
+ * Visualizes a PdfDictionary object.
+ * @param dictionary a com.lowagie.text.pdf.PdfDictionary object
+ */
+ public void listDict(PdfDictionary dictionary)
+ {
+ out.println("<<");
+ PdfName key;
+ PdfObject value;
+ for (Iterator i = dictionary.getKeys().iterator(); i.hasNext(); ) {
+ key = (PdfName) i.next();
+ value = dictionary.get(key);
+ out.print(key.toString());
+ out.print(' ');
+ listAnyObject(value);
+ }
+ out.println(">>");
+ }
+
+ /**
+ * Visualizes a PdfArray object.
+ * @param array a com.lowagie.text.pdf.PdfArray object
+ */
+ public void listArray(PdfArray array)
+ {
+ out.println('[');
+ for (Iterator i = array.getArrayList().iterator(); i.hasNext(); ) {
+ PdfObject item = (PdfObject)i.next();
+ listAnyObject(item);
+ }
+ out.println(']');
+ }
+ /**
+ * Visualizes a Stream.
+ * @param stream
+ * @param reader
+ */
+ public void listStream(PRStream stream, PdfReaderInstance reader)
+ {
+ try {
+ listDict(stream);
+ out.println("startstream");
+ byte[] b = PdfReader.getStreamBytes(stream);
+// byte buf[] = new byte[Math.min(stream.getLength(), 4096)];
+// int r = 0;
+// stream.openStream(reader);
+// for (;;) {
+// r = stream.readStream(buf, 0, buf.length);
+// if (r == 0) break;
+// out.write(buf, 0, r);
+// }
+// stream.closeStream();
+ int len = b.length - 1;
+ for (int k = 0; k < len; ++k) {
+ if (b[k] == '\r' && b[k + 1] != '\n')
+ b[k] = (byte)'\n';
+ }
+ out.println(new String(b));
+ out.println("endstream");
+ } catch (IOException e) {
+ System.err.println("I/O exception: " + e);
+// } catch (java.util.zip.DataFormatException e) {
+// System.err.println("Data Format Exception: " + e);
+ }
+ }
+ /**
+ * Visualizes an imported page
+ * @param iPage
+ */
+ public void listPage(PdfImportedPage iPage)
+ {
+ int pageNum = iPage.getPageNumber();
+ PdfReaderInstance readerInst = iPage.getPdfReaderInstance();
+ PdfReader reader = readerInst.getReader();
+
+ PdfDictionary page = reader.getPageN(pageNum);
+ listDict(page);
+ PdfObject obj = PdfReader.getPdfObject(page.get(PdfName.CONTENTS));
+ switch (obj.type) {
+ case PdfObject.STREAM:
+ listStream((PRStream)obj, readerInst);
+ break;
+ case PdfObject.ARRAY:
+ for (Iterator i = ((PdfArray)obj).getArrayList().iterator(); i.hasNext();) {
+ PdfObject o = PdfReader.getPdfObject((PdfObject)i.next());
+ listStream((PRStream)o, readerInst);
+ out.println("-----------");
+ }
+ break;
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfLiteral.java b/src/main/java/com/lowagie/text/pdf/PdfLiteral.java
new file mode 100644
index 0000000..bf71bc6
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfLiteral.java
@@ -0,0 +1,111 @@
+/*
+ * $Id: PdfLiteral.java,v 1.53 2005/05/04 14:31:48 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * a Literal
+ */
+
+public class PdfLiteral extends PdfObject {
+
+ /**
+ * Holds value of property position.
+ */
+ private int position;
+
+ public PdfLiteral(String text) {
+ super(0, text);
+ }
+
+ public PdfLiteral(byte b[]) {
+ super(0, b);
+ }
+
+ public PdfLiteral(int size) {
+ super(0, (byte[])null);
+ bytes = new byte[size];
+ java.util.Arrays.fill(bytes, (byte)32);
+ }
+
+ public PdfLiteral(int type, String text) {
+ super(type, text);
+ }
+
+ public PdfLiteral(int type, byte b[]) {
+ super(type, b);
+ }
+
+ public void toPdf(PdfWriter writer, java.io.OutputStream os) throws java.io.IOException {
+ if (os instanceof OutputStreamCounter)
+ position = ((OutputStreamCounter)os).getCounter();
+ super.toPdf(writer, os);
+ }
+
+ /**
+ * Getter for property position.
+ * @return Value of property position.
+ */
+ public int getPosition() {
+ return this.position;
+ }
+
+ /**
+ * Getter for property posLength.
+ * @return Value of property posLength.
+ */
+ public int getPosLength() {
+ if (bytes != null)
+ return bytes.length;
+ else
+ return 0;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfMediaClipData.java b/src/main/java/com/lowagie/text/pdf/PdfMediaClipData.java
new file mode 100644
index 0000000..de17355
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfMediaClipData.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2003 Galo Gimenez
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+
+public class PdfMediaClipData extends PdfDictionary {
+
+ PdfMediaClipData(String file, PdfFileSpecification fs, String mimeType) throws IOException {
+ put(PdfName.TYPE,new PdfName("MediaClip"));
+ put(PdfName.S, new PdfName("MCD"));
+ put(PdfName.N, new PdfString("Media clip for "+file));
+ put(new PdfName("CT"), new PdfString(mimeType));
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(new PdfName("TF"), new PdfString("TEMPACCESS"));
+ put(new PdfName("P"), dic);
+ put(PdfName.D, fs.getReference());
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfName.java b/src/main/java/com/lowagie/text/pdf/PdfName.java
new file mode 100644
index 0000000..ccfbd01
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfName.java
@@ -0,0 +1,1141 @@
+/*
+ * $Id: PdfName.java,v 1.83 2006/06/04 22:23:38 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999-2006 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * PdfName
is an object that can be used as a name in a PDF-file.
+ *
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 4.5 (page 39-40).
+ * PdfName
. The name length will be checked.
+ * @param name the new name
+ */
+ public PdfName(String name) {
+ this(name, true);
+ }
+
+ /**
+ * Constructs a new PdfName
.
+ * @param name the new name
+ * @param lengthCheck if true
check the lenght validity, if false
the name can
+ * have any length
+ */
+
+ public PdfName(String name, boolean lengthCheck) {
+ super(PdfObject.NAME);
+ // The minimum number of characters in a name is 0, the maximum is 127 (the '/' not included)
+ int length = name.length();
+ if (lengthCheck && length > 127) {
+ throw new IllegalArgumentException("The name '" + name + "' is too long (" + length + " characters).");
+ }
+ // The name has to be checked for illegal characters
+ // every special character has to be substituted
+ ByteBuffer pdfName = new ByteBuffer(length + 20);
+ pdfName.append('/');
+ char character;
+ char chars[] = name.toCharArray();
+ // loop over all the characters
+ for (int index = 0; index < length; index++) {
+ character = (char)(chars[index] & 0xff);
+ // special characters are escaped (reference manual p.39)
+ switch (character) {
+ case ' ':
+ case '%':
+ case '(':
+ case ')':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case '/':
+ case '#':
+ pdfName.append('#');
+ pdfName.append(Integer.toString((int) character, 16));
+ break;
+ default:
+ if (character > 126 || character < 32) {
+ pdfName.append('#');
+ if (character < 16)
+ pdfName.append('0');
+ pdfName.append(Integer.toString((int) character, 16));
+ }
+ else
+ pdfName.append(character);
+ break;
+ }
+ }
+ bytes = pdfName.toByteArray();
+ }
+
+ /**
+ * Constructs a PdfName.
+ * @param bytes the byte representation of the name
+ */
+ public PdfName(byte bytes[]) {
+ super(PdfObject.NAME, bytes);
+ }
+ // methods
+
+ /**
+ * Compares this object with the specified object for order. Returns a
+ * negative integer, zero, or a positive integer as this object is less
+ * than, equal to, or greater than the specified object.true
if this object is the same as the obj
+ * argument; false
otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj instanceof PdfName)
+ return compareTo(obj) == 0;
+ return false;
+ }
+
+ /**
+ * Returns a hash code value for the object. This method is
+ * supported for the benefit of hashtables such as those provided by
+ * java.util.Hashtable
.
+ *
+ * @return a hash code value for this object.
+ */
+ public int hashCode() {
+ int h = hash;
+ if (h == 0) {
+ int ptr = 0;
+ int len = bytes.length;
+
+ for (int i = 0; i < len; i++)
+ h = 31*h + (bytes[ptr++] & 0xff);
+ hash = h;
+ }
+ return h;
+ }
+
+ /** Decodes an escaped name in the form "/AB#20CD" into "AB CD".
+ * @param name the name to decode
+ * @return the decoded name
+ */
+ public static String decodeName(String name) {
+ StringBuffer buf = new StringBuffer();
+ try {
+ int len = name.length();
+ for (int k = 1; k < len; ++k) {
+ char c = name.charAt(k);
+ if (c == '#') {
+ c = (char)((PRTokeniser.getHex(name.charAt(k + 1)) << 4) + PRTokeniser.getHex(name.charAt(k + 2)));
+ k += 2;
+ }
+ buf.append(c);
+ }
+ }
+ catch (IndexOutOfBoundsException e) {
+ // empty on purpose
+ }
+ return buf.toString();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfNameTree.java b/src/main/java/com/lowagie/text/pdf/PdfNameTree.java
new file mode 100644
index 0000000..999fca8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfNameTree.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.util.HashMap;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.io.IOException;
+import com.lowagie.text.StringCompare;
+
+/**
+ * Creates a name tree.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfNameTree {
+
+ private static final int leafSize = 64;
+ private static final StringCompare stringCompare = new StringCompare();
+
+ /**
+ * Creates a name tree.
+ * @param items the item of the name tree. The key is a String
+ * and the value is a PdfObject
. Note that although the
+ * keys are strings only the lower byte is used and no check is made for chars
+ * with the same lower byte and different upper byte. This will generate a wrong
+ * tree name.
+ * @param writer the writer
+ * @throws IOException on error
+ * @return the dictionary with the name tree. This dictionary is the one
+ * generally pointed to by the key /Dests, for example
+ */
+ public static PdfDictionary writeTree(HashMap items, PdfWriter writer) throws IOException {
+ if (items.size() == 0)
+ return null;
+ String names[] = new String[items.size()];
+ names = (String[])items.keySet().toArray(names);
+ Arrays.sort(names, stringCompare);
+ if (names.length <= leafSize) {
+ PdfDictionary dic = new PdfDictionary();
+ PdfArray ar = new PdfArray();
+ for (int k = 0; k < names.length; ++k) {
+ ar.add(new PdfString(names[k], null));
+ ar.add((PdfObject)items.get(names[k]));
+ }
+ dic.put(PdfName.NAMES, ar);
+ return dic;
+ }
+ int skip = leafSize;
+ PdfIndirectReference kids[] = new PdfIndirectReference[(names.length + leafSize - 1) / leafSize];
+ for (int k = 0; k < kids.length; ++k) {
+ int offset = k * leafSize;
+ int end = Math.min(offset + leafSize, names.length);
+ PdfDictionary dic = new PdfDictionary();
+ PdfArray arr = new PdfArray();
+ arr.add(new PdfString(names[offset], null));
+ arr.add(new PdfString(names[end - 1], null));
+ dic.put(PdfName.LIMITS, arr);
+ arr = new PdfArray();
+ for (; offset < end; ++offset) {
+ arr.add(new PdfString(names[offset], null));
+ arr.add((PdfObject)items.get(names[offset]));
+ }
+ dic.put(PdfName.NAMES, arr);
+ kids[k] = writer.addToBody(dic).getIndirectReference();
+ }
+ int top = kids.length;
+ while (true) {
+ if (top <= leafSize) {
+ PdfArray arr = new PdfArray();
+ for (int k = 0; k < top; ++k)
+ arr.add(kids[k]);
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.KIDS, arr);
+ return dic;
+ }
+ skip *= leafSize;
+ int tt = (names.length + skip - 1 )/ skip;
+ for (int k = 0; k < tt; ++k) {
+ int offset = k * leafSize;
+ int end = Math.min(offset + leafSize, top);
+ PdfDictionary dic = new PdfDictionary();
+ PdfArray arr = new PdfArray();
+ arr.add(new PdfString(names[k * skip], null));
+ arr.add(new PdfString(names[Math.min((k + 1) * skip, names.length) - 1], null));
+ dic.put(PdfName.LIMITS, arr);
+ arr = new PdfArray();
+ for (; offset < end; ++offset) {
+ arr.add(kids[offset]);
+ }
+ dic.put(PdfName.KIDS, arr);
+ kids[k] = writer.addToBody(dic).getIndirectReference();
+ }
+ top = tt;
+ }
+ }
+
+ private static void iterateItems(PdfDictionary dic, HashMap items) {
+ PdfArray nn = (PdfArray)PdfReader.getPdfObjectRelease(dic.get(PdfName.NAMES));
+ if (nn != null) {
+ ArrayList arr = nn.getArrayList();
+ for (int k = 0; k < arr.size(); ++k) {
+ PdfString s = (PdfString)PdfReader.getPdfObjectRelease((PdfObject)arr.get(k++));
+ items.put(PdfEncodings.convertToString(s.getBytes(), null), arr.get(k));
+ }
+ }
+ else if ((nn = (PdfArray)PdfReader.getPdfObjectRelease(dic.get(PdfName.KIDS))) != null) {
+ ArrayList arr = nn.getArrayList();
+ for (int k = 0; k < arr.size(); ++k) {
+ PdfDictionary kid = (PdfDictionary)PdfReader.getPdfObjectRelease((PdfObject)arr.get(k));
+ iterateItems(kid, items);
+ }
+ }
+ }
+
+ public static HashMap readTree(PdfDictionary dic) {
+ HashMap items = new HashMap();
+ if (dic != null)
+ iterateItems(dic, items);
+ return items;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfNull.java b/src/main/java/com/lowagie/text/pdf/PdfNull.java
new file mode 100644
index 0000000..ab40b32
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfNull.java
@@ -0,0 +1,87 @@
+/*
+ * $Id: PdfNull.java,v 1.55 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * PdfNull
is the Null object represented by the keyword null.
+ * PdfNull
-object. */
+ public static final PdfNull PDFNULL = new PdfNull();
+
+/** This is the content of a PdfNull
-object. */
+ private static final String CONTENT = "null";
+
+ // constructors
+
+/**
+ * Constructs a PdfNull
-object.
+ * PdfNumber
provides two types of numbers, integer and real.
+ *
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 4.3 (page 37).
+ *
+ * @see PdfObject
+ * @see BadPdfFormatException
+ */
+
+public class PdfNumber extends PdfObject {
+
+/** actual value of this PdfNumber
, represented as a double
*/
+ private double value;
+
+ // constructors
+
+/**
+ * Constructs a PdfNumber
-object.
+ *
+ * @param content value of the new PdfNumber
-object
+ */
+
+ public PdfNumber(String content) {
+ super(NUMBER);
+ try {
+ value = Double.valueOf(content.trim()).doubleValue();
+ setContent(content);
+ }
+ catch (NumberFormatException nfe){
+ throw new RuntimeException(content + " is not a valid number - " + nfe.toString());
+ }
+ }
+
+/**
+ * Constructs a new INTEGER PdfNumber
-object.
+ *
+ * @param value value of the new PdfNumber
-object
+ */
+
+ public PdfNumber(int value) {
+ super(NUMBER);
+ this.value = value;
+ setContent(String.valueOf(value));
+ }
+
+/**
+ * Constructs a new REAL PdfNumber
-object.
+ *
+ * @param value value of the new PdfNumber
-object
+ */
+
+ public PdfNumber(double value) {
+ super(NUMBER);
+ this.value = value;
+ setContent(ByteBuffer.formatDouble(value));
+ }
+
+/**
+ * Constructs a new REAL PdfNumber
-object.
+ *
+ * @param value value of the new PdfNumber
-object
+ */
+
+ public PdfNumber(float value) {
+ this((double)value);
+ }
+
+ // methods returning the value of this object
+
+/**
+ * Returns the primitive int
value of this object.
+ *
+ * @return a value
+ */
+
+ public int intValue() {
+ return (int) value;
+ }
+
+/**
+ * Returns the primitive double
value of this object.
+ *
+ * @return a value
+ */
+
+ public double doubleValue() {
+ return value;
+ }
+
+ public float floatValue() {
+ return (float)value;
+ }
+
+ // other methods
+
+/**
+ * Increments the value of the PdfNumber
-object with 1.
+ */
+
+ public void increment() {
+ value += 1.0;
+ setContent(ByteBuffer.formatDouble(value));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfNumberTree.java b/src/main/java/com/lowagie/text/pdf/PdfNumberTree.java
new file mode 100644
index 0000000..8e39c1d
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfNumberTree.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.util.HashMap;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.io.IOException;
+
+/**
+ * Creates a number tree.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfNumberTree {
+
+ private static final int leafSize = 64;
+
+ /**
+ * Creates a number tree.
+ * @param items the item of the number tree. The key is an Integer
+ * and the value is a PdfObject
.
+ * @param writer the writer
+ * @throws IOException on error
+ * @return the dictionary with the number tree.
+ */
+ public static PdfDictionary writeTree(HashMap items, PdfWriter writer) throws IOException {
+ if (items.size() == 0)
+ return null;
+ Integer numbers[] = new Integer[items.size()];
+ numbers = (Integer[])items.keySet().toArray(numbers);
+ Arrays.sort(numbers);
+ if (numbers.length <= leafSize) {
+ PdfDictionary dic = new PdfDictionary();
+ PdfArray ar = new PdfArray();
+ for (int k = 0; k < numbers.length; ++k) {
+ ar.add(new PdfNumber(numbers[k].intValue()));
+ ar.add((PdfObject)items.get(numbers[k]));
+ }
+ dic.put(PdfName.NUMS, ar);
+ return dic;
+ }
+ int skip = leafSize;
+ PdfIndirectReference kids[] = new PdfIndirectReference[(numbers.length + leafSize - 1) / leafSize];
+ for (int k = 0; k < kids.length; ++k) {
+ int offset = k * leafSize;
+ int end = Math.min(offset + leafSize, numbers.length);
+ PdfDictionary dic = new PdfDictionary();
+ PdfArray arr = new PdfArray();
+ arr.add(new PdfNumber(numbers[offset].intValue()));
+ arr.add(new PdfNumber(numbers[end - 1].intValue()));
+ dic.put(PdfName.LIMITS, arr);
+ arr = new PdfArray();
+ for (; offset < end; ++offset) {
+ arr.add(new PdfNumber(numbers[offset].intValue()));
+ arr.add((PdfObject)items.get(numbers[offset]));
+ }
+ dic.put(PdfName.NUMS, arr);
+ kids[k] = writer.addToBody(dic).getIndirectReference();
+ }
+ int top = kids.length;
+ while (true) {
+ if (top <= leafSize) {
+ PdfArray arr = new PdfArray();
+ for (int k = 0; k < top; ++k)
+ arr.add(kids[k]);
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.KIDS, arr);
+ return dic;
+ }
+ skip *= leafSize;
+ int tt = (numbers.length + skip - 1 )/ skip;
+ for (int k = 0; k < tt; ++k) {
+ int offset = k * leafSize;
+ int end = Math.min(offset + leafSize, top);
+ PdfDictionary dic = new PdfDictionary();
+ PdfArray arr = new PdfArray();
+ arr.add(new PdfNumber(numbers[k * skip].intValue()));
+ arr.add(new PdfNumber(numbers[Math.min((k + 1) * skip, numbers.length) - 1].intValue()));
+ dic.put(PdfName.LIMITS, arr);
+ arr = new PdfArray();
+ for (; offset < end; ++offset) {
+ arr.add(kids[offset]);
+ }
+ dic.put(PdfName.KIDS, arr);
+ kids[k] = writer.addToBody(dic).getIndirectReference();
+ }
+ top = tt;
+ }
+ }
+
+ private static void iterateItems(PdfDictionary dic, HashMap items) {
+ PdfArray nn = (PdfArray)PdfReader.getPdfObjectRelease(dic.get(PdfName.NUMS));
+ if (nn != null) {
+ ArrayList arr = nn.getArrayList();
+ for (int k = 0; k < arr.size(); ++k) {
+ PdfNumber s = (PdfNumber)PdfReader.getPdfObjectRelease((PdfObject)arr.get(k++));
+ items.put(new Integer(s.intValue()), arr.get(k));
+ }
+ }
+ else if ((nn = (PdfArray)PdfReader.getPdfObjectRelease(dic.get(PdfName.KIDS))) != null) {
+ ArrayList arr = nn.getArrayList();
+ for (int k = 0; k < arr.size(); ++k) {
+ PdfDictionary kid = (PdfDictionary)PdfReader.getPdfObjectRelease((PdfObject)arr.get(k));
+ iterateItems(kid, items);
+ }
+ }
+ }
+
+ public static HashMap readTree(PdfDictionary dic) {
+ HashMap items = new HashMap();
+ if (dic != null)
+ iterateItems(dic, items);
+ return items;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfOCG.java b/src/main/java/com/lowagie/text/pdf/PdfOCG.java
new file mode 100644
index 0000000..18809f8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfOCG.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+/**
+ * The interface common to all layer types.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public interface PdfOCG {
+
+ /**
+ * Gets the PdfIndirectReference
that represents this layer.
+ * @return the PdfIndirectReference
that represents this layer
+ */
+ public PdfIndirectReference getRef();
+
+ /**
+ * Gets the object representing the layer.
+ * @return the object representing the layer
+ */
+ public PdfObject getPdfObject();
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfOCProperties.java b/src/main/java/com/lowagie/text/pdf/PdfOCProperties.java
new file mode 100644
index 0000000..a4d0caa
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfOCProperties.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+/**
+ * This class represents the /OCProperties entry in the document catalog
+ * and holds the optional content properties dictionary, which contains
+ * a list of all the optional content groups in the document, as well as information
+ * about the default and alternate configurations for optional content.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfOCProperties extends PdfDictionary {
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfObject.java b/src/main/java/com/lowagie/text/pdf/PdfObject.java
new file mode 100644
index 0000000..5b92bb4
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfObject.java
@@ -0,0 +1,369 @@
+/*
+ * $Id: PdfObject.java,v 1.60 2005/11/29 21:05:02 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * PdfObject
is the abstract superclass of all PDF objects.
+ *
+ * All these basic PDF objects are described in the 'Portable Document Format
+ * Reference Manual version 1.3' Chapter 4 (pages 37-54).
+ *
+ * @see PdfNull
+ * @see PdfBoolean
+ * @see PdfNumber
+ * @see PdfString
+ * @see PdfName
+ * @see PdfArray
+ * @see PdfDictionary
+ * @see PdfStream
+ * @see PdfIndirectReference
+ */
+
+public abstract class PdfObject {
+
+ // static membervariables (all the possible types of a PdfObject)
+
+/** a possible type of PdfObject
*/
+ public static final int BOOLEAN = 1;
+
+/** a possible type of PdfObject
*/
+ public static final int NUMBER = 2;
+
+/** a possible type of PdfObject
*/
+ public static final int STRING = 3;
+
+/** a possible type of PdfObject
*/
+ public static final int NAME = 4;
+
+/** a possible type of PdfObject
*/
+ public static final int ARRAY = 5;
+
+/** a possible type of PdfObject
*/
+ public static final int DICTIONARY = 6;
+
+/** a possible type of PdfObject
*/
+ public static final int STREAM = 7;
+
+/** a possible type of PdfObject
*/
+ public static final int NULL = 8;
+
+ /** a possible type of PdfObject
*/
+ public static final int INDIRECT = 10;
+
+/** This is an empty string used for the PdfNull
-object and for an empty PdfString
-object. */
+ public static final String NOTHING = "";
+
+/** This is the default encoding to be used for converting Strings into bytes and vice versa.
+ * The default encoding is PdfDocEncoding.
+ */
+ public static final String TEXT_PDFDOCENCODING = "PDF";
+
+/** This is the encoding to be used to output text in Unicode. */
+ public static final String TEXT_UNICODE = "UnicodeBig";
+
+ // membervariables
+
+/** the content of this PdfObject
*/
+ protected byte[] bytes;
+
+/** the type of this PdfObject
*/
+ protected int type;
+
+ /**
+ * Holds value of property indRef.
+ */
+ protected PRIndirectReference indRef;
+
+ // constructors
+
+/**
+ * Constructs a PdfObject
of a certain type without any content.
+ *
+ * @param type type of the new PdfObject
+ */
+
+ protected PdfObject(int type) {
+ this.type = type;
+ }
+
+/**
+ * Constructs a PdfObject
of a certain type with a certain content.
+ *
+ * @param type type of the new PdfObject
+ * @param content content of the new PdfObject
as a String
.
+ */
+
+ protected PdfObject(int type, String content) {
+ this.type = type;
+ bytes = PdfEncodings.convertToBytes(content, null);
+ }
+
+/**
+ * Constructs a PdfObject
of a certain type with a certain content.
+ *
+ * @param type type of the new PdfObject
+ * @param bytes content of the new PdfObject
as an array of byte
.
+ */
+
+ protected PdfObject(int type, byte[] bytes) {
+ this.bytes = bytes;
+ this.type = type;
+ }
+
+ // methods dealing with the content of this object
+
+/**
+ * Writes the PDF representation of this PdfObject
as an array of byte
s to the writer.
+ * @param writer for backwards compatibility
+ * @param os the outputstream to write the bytes to.
+ * @throws IOException
+ */
+
+ public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
+ if (bytes != null)
+ os.write(bytes);
+ }
+
+ /**
+ * Gets the presentation of this object in a byte array
+ * @return a byte array
+ */
+ public byte[] getBytes() {
+ return bytes;
+ }
+
+ /**
+ * Can this object be in an object stream?
+ * @return true if this object can be in an object stream.
+ */
+ public boolean canBeInObjStm() {
+ return (type >= 1 && type <= 6) || type == 8;
+ }
+
+/**
+ * Returns the length of the PDF representation of the PdfObject
.
+ * PdfString
and PdfStream
,
+ * this method differs from the method length
because length
+ * returns the length of the actual content of the PdfObject
.String
-representation of this PdfObject
.
+ *
+ * @return a String
+ */
+
+ public String toString() {
+ if (bytes == null)
+ return super.toString();
+ else
+ return PdfEncodings.convertToString(bytes, null);
+ }
+
+/**
+ * Returns the length of the actual content of the PdfObject
.
+ * PdfString
and PdfStream
,
+ * this method differs from the method pdfLength
because pdfLength
+ * returns the length of the PDF representation of the object, not of the actual content
+ * as does the method length
.PdfObject
.
+ *
+ * @param content the new content of this PdfObject
+ */
+
+ protected void setContent(String content) {
+ bytes = PdfEncodings.convertToBytes(content, null);
+ }
+
+ // methods dealing with the type of this object
+
+/**
+ * Returns the type of this PdfObject
.
+ *
+ * @return a type
+ */
+
+ public int type() {
+ return type;
+ }
+
+/**
+ * Checks if this PdfObject
is of the type PdfNull
.
+ *
+ * @return true
or false
+ */
+
+ public boolean isNull() {
+ return (this.type == NULL);
+ }
+
+/**
+ * Checks if this PdfObject
is of the type PdfBoolean
.
+ *
+ * @return true
or false
+ */
+
+ public boolean isBoolean() {
+ return (this.type == BOOLEAN);
+ }
+
+/**
+ * Checks if this PdfObject
is of the type PdfNumber
.
+ *
+ * @return true
or false
+ */
+
+ public boolean isNumber() {
+ return (this.type == NUMBER);
+ }
+
+/**
+ * Checks if this PdfObject
is of the type PdfString
.
+ *
+ * @return true
or false
+ */
+
+ public boolean isString() {
+ return (this.type == STRING);
+ }
+
+/**
+ * Checks if this PdfObject
is of the type PdfName
.
+ *
+ * @return true
or false
+ */
+
+ public boolean isName() {
+ return (this.type == NAME);
+ }
+
+/**
+ * Checks if this PdfObject
is of the type PdfArray
.
+ *
+ * @return true
or false
+ */
+
+ public boolean isArray() {
+ return (this.type == ARRAY);
+ }
+
+/**
+ * Checks if this PdfObject
is of the type PdfDictionary
.
+ *
+ * @return true
or false
+ */
+
+ public boolean isDictionary() {
+ return (this.type == DICTIONARY);
+ }
+
+/**
+ * Checks if this PdfObject
is of the type PdfStream
.
+ *
+ * @return true
or false
+ */
+
+ public boolean isStream() {
+ return (this.type == STREAM);
+ }
+
+ /**
+ * Checks if this is an indirect object.
+ * @return true if this is an indirect object
+ */
+ public boolean isIndirect() {
+ return (this.type == INDIRECT);
+ }
+
+ /**
+ * Getter for property indRef.
+ * @return Value of property indRef.
+ */
+ public PRIndirectReference getIndRef() {
+ return this.indRef;
+ }
+
+ /**
+ * Setter for property indRef.
+ * @param indRef New value of property indRef.
+ */
+ public void setIndRef(PRIndirectReference indRef) {
+ this.indRef = indRef;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfOutline.java b/src/main/java/com/lowagie/text/pdf/PdfOutline.java
new file mode 100644
index 0000000..687006c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfOutline.java
@@ -0,0 +1,542 @@
+/*
+ * $Id: PdfOutline.java,v 1.52 2005/11/29 17:31:34 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.Iterator;
+
+import com.lowagie.text.Chunk;
+import com.lowagie.text.Paragraph;
+import java.util.ArrayList;
+import java.awt.Color;
+import com.lowagie.text.Font;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * PdfOutline
is an object that represents a PDF outline entry.
+ *
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 6.7 (page 104-106)
+ *
+ * @see PdfDictionary
+ */
+
+public class PdfOutline extends PdfDictionary {
+
+ // membervariables
+
+ /** the PdfIndirectReference
of this object */
+ private PdfIndirectReference reference;
+
+ /** value of the Count-key */
+ private int count = 0;
+
+ /** value of the Parent-key */
+ private PdfOutline parent;
+
+ /** value of the Destination-key */
+ private PdfDestination destination;
+
+ /** The PdfAction
for this outline.
+ */
+ private PdfAction action;
+
+ protected ArrayList kids = new ArrayList();
+
+ protected PdfWriter writer;
+
+ /** Holds value of property tag. */
+ private String tag;
+
+ /** Holds value of property open. */
+ private boolean open;
+
+ /** Holds value of property color. */
+ private Color color;
+
+ /** Holds value of property style. */
+ private int style = 0;
+
+ // constructors
+
+ /**
+ * Constructs a PdfOutline
.
+ * outlines object
.
+ *
+ * @param writer The PdfWriter you are adding the outline to
+ */
+
+ PdfOutline(PdfWriter writer) {
+ super(OUTLINES);
+ open = true;
+ parent = null;
+ this.writer = writer;
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
. The open mode is
+ * true
.
+ *
+ * @param parent the parent of this outline item
+ * @param action the PdfAction
for this outline item
+ * @param title the title of this outline item
+ */
+
+ public PdfOutline(PdfOutline parent, PdfAction action, String title) {
+ this(parent, action, title, true);
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
.
+ *
+ * @param parent the parent of this outline item
+ * @param action the PdfAction
for this outline item
+ * @param title the title of this outline item
+ * @param open true
if the children are visible
+ */
+ public PdfOutline(PdfOutline parent, PdfAction action, String title, boolean open) {
+ super();
+ this.action = action;
+ initOutline(parent, title, open);
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
. The open mode is
+ * true
.
+ *
+ * @param parent the parent of this outline item
+ * @param destination the destination for this outline item
+ * @param title the title of this outline item
+ */
+
+ public PdfOutline(PdfOutline parent, PdfDestination destination, String title) {
+ this(parent, destination, title, true);
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
.
+ *
+ * @param parent the parent of this outline item
+ * @param destination the destination for this outline item
+ * @param title the title of this outline item
+ * @param open true
if the children are visible
+ */
+ public PdfOutline(PdfOutline parent, PdfDestination destination, String title, boolean open) {
+ super();
+ this.destination = destination;
+ initOutline(parent, title, open);
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
. The open mode is
+ * true
.
+ *
+ * @param parent the parent of this outline item
+ * @param action the PdfAction
for this outline item
+ * @param title the title of this outline item
+ */
+ public PdfOutline(PdfOutline parent, PdfAction action, PdfString title) {
+ this(parent, action, title, true);
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
.
+ *
+ * @param parent the parent of this outline item
+ * @param action the PdfAction
for this outline item
+ * @param title the title of this outline item
+ * @param open true
if the children are visible
+ */
+ public PdfOutline(PdfOutline parent, PdfAction action, PdfString title, boolean open) {
+ this(parent, action, title.toString(), open);
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
. The open mode is
+ * true
.
+ *
+ * @param parent the parent of this outline item
+ * @param destination the destination for this outline item
+ * @param title the title of this outline item
+ */
+
+ public PdfOutline(PdfOutline parent, PdfDestination destination, PdfString title) {
+ this(parent, destination, title, true);
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
.
+ *
+ * @param parent the parent of this outline item
+ * @param destination the destination for this outline item
+ * @param title the title of this outline item
+ * @param open true
if the children are visible
+ */
+ public PdfOutline(PdfOutline parent, PdfDestination destination, PdfString title, boolean open) {
+ this(parent, destination, title.toString(), true);
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
. The open mode is
+ * true
.
+ *
+ * @param parent the parent of this outline item
+ * @param action the PdfAction
for this outline item
+ * @param title the title of this outline item
+ */
+
+ public PdfOutline(PdfOutline parent, PdfAction action, Paragraph title) {
+ this(parent, action, title, true);
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
.
+ *
+ * @param parent the parent of this outline item
+ * @param action the PdfAction
for this outline item
+ * @param title the title of this outline item
+ * @param open true
if the children are visible
+ */
+ public PdfOutline(PdfOutline parent, PdfAction action, Paragraph title, boolean open) {
+ super();
+ StringBuffer buf = new StringBuffer();
+ for (Iterator i = title.getChunks().iterator(); i.hasNext(); ) {
+ Chunk chunk = (Chunk) i.next();
+ buf.append(chunk.content());
+ }
+ this.action = action;
+ initOutline(parent, buf.toString(), open);
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
. The open mode is
+ * true
.
+ *
+ * @param parent the parent of this outline item
+ * @param destination the destination for this outline item
+ * @param title the title of this outline item
+ */
+
+ public PdfOutline(PdfOutline parent, PdfDestination destination, Paragraph title) {
+ this(parent, destination, title, true);
+ }
+
+ /**
+ * Constructs a PdfOutline
.
+ * outline entry
.
+ *
+ * @param parent the parent of this outline item
+ * @param destination the destination for this outline item
+ * @param title the title of this outline item
+ * @param open true
if the children are visible
+ */
+ public PdfOutline(PdfOutline parent, PdfDestination destination, Paragraph title, boolean open) {
+ super();
+ StringBuffer buf = new StringBuffer();
+ for (Iterator i = title.getChunks().iterator(); i.hasNext(); ) {
+ Chunk chunk = (Chunk) i.next();
+ buf.append(chunk.content());
+ }
+ this.destination = destination;
+ initOutline(parent, buf.toString(), open);
+ }
+
+
+ // methods
+
+ /** Helper for the constructors.
+ * @param parent the parent outline
+ * @param title the title for this outline
+ * @param open true
if the children are visible
+ */
+ void initOutline(PdfOutline parent, String title, boolean open) {
+ this.open = open;
+ this.parent = parent;
+ writer = parent.writer;
+ put(PdfName.TITLE, new PdfString(title, PdfObject.TEXT_UNICODE));
+ parent.addKid(this);
+ if (destination != null && !destination.hasPage()) // bugfix Finn Bock
+ setDestinationPage(writer.getCurrentPage());
+ }
+
+ /**
+ * Sets the indirect reference of this PdfOutline
.
+ *
+ * @param reference the PdfIndirectReference
to this outline.
+ */
+
+ public void setIndirectReference(PdfIndirectReference reference) {
+ this.reference = reference;
+ }
+
+ /**
+ * Gets the indirect reference of this PdfOutline
.
+ *
+ * @return the PdfIndirectReference
to this outline.
+ */
+
+ public PdfIndirectReference indirectReference() {
+ return reference;
+ }
+
+ /**
+ * Gets the parent of this PdfOutline
.
+ *
+ * @return the PdfOutline
that is the parent of this outline.
+ */
+
+ public PdfOutline parent() {
+ return parent;
+ }
+
+ /**
+ * Set the page of the PdfDestination
-object.
+ *
+ * @param pageReference indirect reference to the page
+ * @return true
if this page was set as the PdfDestination
-page.
+ */
+
+ public boolean setDestinationPage(PdfIndirectReference pageReference) {
+ if (destination == null) {
+ return false;
+ }
+ return destination.addPage(pageReference);
+ }
+
+ /**
+ * Gets the destination for this outline.
+ * @return the destination
+ */
+ public PdfDestination getPdfDestination() {
+ return destination;
+ }
+
+ int getCount() {
+ return count;
+ }
+
+ void setCount(int count) {
+ this.count = count;
+ }
+
+ /**
+ * returns the level of this outline.
+ *
+ * @return a level
+ */
+
+ public int level() {
+ if (parent == null) {
+ return 0;
+ }
+ return (parent.level() + 1);
+ }
+
+ /**
+ * Returns the PDF representation of this PdfOutline
.
+ *
+ * @param writer the encryption information
+ * @param os
+ * @throws IOException
+ */
+
+ public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
+ if (color != null && !color.equals(Color.black)) {
+ put(PdfName.C, new PdfArray(new float[]{color.getRed()/255f,color.getGreen()/255f,color.getBlue()/255f}));
+ }
+ int flag = 0;
+ if ((style & Font.BOLD) != 0)
+ flag |= 2;
+ if ((style & Font.ITALIC) != 0)
+ flag |= 1;
+ if (flag != 0)
+ put(PdfName.F, new PdfNumber(flag));
+ if (parent != null) {
+ put(PdfName.PARENT, parent.indirectReference());
+ }
+ if (destination != null && destination.hasPage()) {
+ put(PdfName.DEST, destination);
+ }
+ if (action != null)
+ put(PdfName.A, action);
+ if (count != 0) {
+ put(PdfName.COUNT, new PdfNumber(count));
+ }
+ super.toPdf(writer, os);
+ }
+
+ /**
+ * Adds a kid to the outline
+ * @param outline
+ */
+ public void addKid(PdfOutline outline) {
+ kids.add(outline);
+ }
+
+ /**
+ * Returns the kids of this outline
+ * @return an ArrayList with PdfOutlines
+ */
+ public ArrayList getKids() {
+ return kids;
+ }
+
+ /**
+ * Sets the kids of this outline
+ * @param kids
+ */
+ public void setKids(ArrayList kids) {
+ this.kids = kids;
+ }
+
+ /** Getter for property tag.
+ * @return Value of property tag.
+ */
+ public String getTag() {
+ return tag;
+ }
+
+ /** Setter for property tag.
+ * @param tag New value of property tag.
+ */
+ public void setTag(String tag) {
+ this.tag = tag;
+ }
+
+ /**
+ * Gets the title of this outline
+ * @return the title as a String
+ */
+ public String getTitle() {
+ PdfString title = (PdfString)get(PdfName.TITLE);
+ return title.toString();
+ }
+
+ /**
+ * Sets the title of this outline
+ * @param title
+ */
+ public void setTitle(String title) {
+ put(PdfName.TITLE, new PdfString(title, PdfObject.TEXT_UNICODE));
+ }
+
+ /** Getter for property open.
+ * @return Value of property open.
+ */
+ public boolean isOpen() {
+ return open;
+ }
+
+ /** Setter for property open.
+ * @param open New value of property open.
+ */
+ public void setOpen(boolean open) {
+ this.open = open;
+ }
+
+ /** Getter for property color.
+ * @return Value of property color.
+ *
+ */
+ public Color getColor() {
+ return this.color;
+ }
+
+ /** Setter for property color.
+ * @param color New value of property color.
+ *
+ */
+ public void setColor(Color color) {
+ this.color = color;
+ }
+
+ /** Getter for property style.
+ * @return Value of property style.
+ *
+ */
+ public int getStyle() {
+ return this.style;
+ }
+
+ /** Setter for property style.
+ * @param style New value of property style.
+ *
+ */
+ public void setStyle(int style) {
+ this.style = style;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPCell.java b/src/main/java/com/lowagie/text/pdf/PdfPCell.java
new file mode 100644
index 0000000..79d1f83
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPCell.java
@@ -0,0 +1,735 @@
+/*
+ * $Id: PdfPCell.java,v 1.59 2006/02/16 16:17:49 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.Element;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Image;
+import com.lowagie.text.Chunk;
+import com.lowagie.text.pdf.events.PdfPCellEventForwarder;
+
+/** A cell in a PdfPTable.
+ */
+
+public class PdfPCell extends Rectangle{
+
+ private ColumnText column = new ColumnText(null);
+
+ /** Holds value of property verticalAlignment. */
+ private int verticalAlignment = Element.ALIGN_TOP;
+
+ /** Holds value of property paddingLeft. */
+ private float paddingLeft = 2;
+
+ /** Holds value of property paddingLeft. */
+ private float paddingRight = 2;
+
+ /** Holds value of property paddingTop. */
+ private float paddingTop = 2;
+
+ /** Holds value of property paddingBottom. */
+ private float paddingBottom = 2;
+
+ /** Holds value of property fixedHeight. */
+ private float fixedHeight = 0;
+
+ /** Holds value of property noWrap. */
+ private boolean noWrap = false;
+
+ /** Holds value of property table. */
+ private PdfPTable table;
+
+ /** Holds value of property minimumHeight. */
+ private float minimumHeight;
+
+ /** Holds value of property colspan. */
+ private int colspan = 1;
+
+ /** Holds value of property image. */
+ private Image image;
+
+ /** Holds value of property cellEvent. */
+ private PdfPCellEvent cellEvent;
+
+ /** Holds value of property useDescender. */
+ private boolean useDescender;
+
+ /** Increases padding to include border if true */
+ private boolean useBorderPadding = false;
+
+
+ /** The text in the cell. */
+ protected Phrase phrase;
+
+ /** Constructs an empty PdfPCell
.
+ * The default padding is 2.
+ */
+ public PdfPCell() {
+ super(0, 0, 0, 0);
+ borderWidth = 0.5f;
+ border = BOX;
+ column.setLeading(0, 1);
+ }
+
+ /** Constructs a PdfPCell
with a Phrase
.
+ * The default padding is 2.
+ * @param phrase the text
+ */
+ public PdfPCell(Phrase phrase) {
+ super(0, 0, 0, 0);
+ borderWidth = 0.5f;
+ border = BOX;
+ column.addText(this.phrase = phrase);
+ column.setLeading(0, 1);
+ }
+
+ /** Constructs a PdfPCell
with an Image
.
+ * The default padding is 0.
+ * @param image the Image
+ */
+ public PdfPCell(Image image) {
+ super(0, 0, 0, 0);
+ borderWidth = 0.5f;
+ border = BOX;
+ column.addText(this.phrase = new Phrase(new Chunk(image, 0, 0)));
+ column.setLeading(0, 1);
+ setPadding(0);
+ }
+
+ /** Constructs a PdfPCell
with an Image
.
+ * The default padding is 0.25 for a border width of 0.5.
+ * @param image the Image
+ * @param fit true
to fit the image to the cell
+ */
+ public PdfPCell(Image image, boolean fit) {
+ super(0, 0, 0, 0);
+ if (fit) {
+ borderWidth = 0.5f;
+ border = BOX;
+ this.image = image;
+ column.setLeading(0, 1);
+ setPadding(borderWidth / 2);
+ }
+ else {
+ borderWidth = 0.5f;
+ border = BOX;
+ column.addText(this.phrase = new Phrase(new Chunk(image, 0, 0)));
+ column.setLeading(0, 1);
+ setPadding(0);
+ }
+ }
+
+ /** Constructs a PdfPCell
with a PdfPtable
.
+ * This constructor allows nested tables.
+ * The default padding is 0.
+ * @param table The PdfPTable
+ */
+ public PdfPCell(PdfPTable table) {
+ super(0, 0, 0, 0);
+ borderWidth = 0.5f;
+ border = BOX;
+ column.setLeading(0, 1);
+ setPadding(0);
+ this.table = table;
+ table.setWidthPercentage(100);
+ table.setExtendLastRow(true);
+ column.addElement(table);
+ }
+
+ /** Constructs a deep copy of a PdfPCell
.
+ * @param cell the PdfPCell
to duplicate
+ */
+ public PdfPCell(PdfPCell cell) {
+ super(cell.llx, cell.lly, cell.urx, cell.ury);
+ cloneNonPositionParameters(cell);
+ verticalAlignment = cell.verticalAlignment;
+ paddingLeft = cell.paddingLeft;
+ paddingRight = cell.paddingRight;
+ paddingTop = cell.paddingTop;
+ paddingBottom = cell.paddingBottom;
+ phrase = cell.phrase;
+ fixedHeight = cell.fixedHeight;
+ minimumHeight = cell.minimumHeight;
+ noWrap = cell.noWrap;
+ colspan = cell.colspan;
+ if (cell.table != null)
+ table = new PdfPTable(cell.table);
+ image = Image.getInstance(cell.image);
+ cellEvent = cell.cellEvent;
+ useDescender = cell.useDescender;
+ column = ColumnText.duplicate(cell.column);
+ useBorderPadding = cell.useBorderPadding;
+ rotation = cell.rotation;
+ }
+
+ /**
+ * Adds an iText element to the cell.
+ * @param element
+ */
+ public void addElement(Element element) {
+ if (table != null) {
+ table = null;
+ column.setText(null);
+ }
+ column.addElement(element);
+ }
+
+ /** Gets the Phrase
from this cell.
+ * @return the Phrase
+ */
+ public Phrase getPhrase() {
+ return phrase;
+ }
+
+ /** Sets the Phrase
for this cell.
+ * @param phrase the Phrase
+ */
+ public void setPhrase(Phrase phrase) {
+ table = null;
+ image = null;
+ column.setText(this.phrase = phrase);
+ }
+
+ /** Gets the horizontal alignment for the cell.
+ * @return the horizontal alignment for the cell
+ */
+ public int getHorizontalAlignment() {
+ return column.getAlignment();
+ }
+
+ /** Sets the horizontal alignment for the cell. It could be
+ * Element.ALIGN_CENTER
for example.
+ * @param horizontalAlignment The horizontal alignment
+ */
+ public void setHorizontalAlignment(int horizontalAlignment) {
+ column.setAlignment(horizontalAlignment);
+ }
+
+ /** Gets the vertical alignment for the cell.
+ * @return the vertical alignment for the cell
+ */
+ public int getVerticalAlignment() {
+ return verticalAlignment;
+ }
+
+ /** Sets the vertical alignment for the cell. It could be
+ * Element.ALIGN_MIDDLE
for example.
+ * @param verticalAlignment The vertical alignment
+ */
+ public void setVerticalAlignment(int verticalAlignment) {
+ if (table != null)
+ table.setExtendLastRow(verticalAlignment == Element.ALIGN_TOP);
+ this.verticalAlignment = verticalAlignment;
+ }
+
+ /** Gets the effective left padding. This will include
+ * the left border width if {@link #isUseBorderPadding()} is true.
+ * @return effective value of property paddingLeft.
+ */
+ public float getEffectivePaddingLeft() {
+ return paddingLeft + (isUseBorderPadding() ? (getBorderWidthLeft()/(isUseVariableBorders()?1f:2f)) : 0);
+ }
+
+ /**
+ * @return Value of property paddingLeft.
+ */
+ public float getPaddingLeft() {
+ return paddingLeft;
+ }
+
+ /**
+ * Setter for property paddingLeft.
+ * @param paddingLeft New value of property paddingLeft.
+ */
+ public void setPaddingLeft(float paddingLeft) {
+ this.paddingLeft = paddingLeft;
+ }
+
+ /** Gets the effective right padding. This will include
+ * the right border width if {@link #isUseBorderPadding()} is true.
+ * @return effective value of property paddingRight.
+ */
+ public float getEffectivePaddingRight() {
+ return paddingRight + (isUseBorderPadding() ? (getBorderWidthRight()/(isUseVariableBorders()?1f:2f)) : 0);
+ }
+
+ /**
+ * Getter for property paddingRight.
+ * @return Value of property paddingRight.
+ */
+ public float getPaddingRight() {
+ return paddingRight;
+ }
+
+ /**
+ * Setter for property paddingRight.
+ * @param paddingRight New value of property paddingRight.
+ */
+ public void setPaddingRight(float paddingRight) {
+ this.paddingRight = paddingRight;
+ }
+
+ /** Gets the effective top padding. This will include
+ * the top border width if {@link #isUseBorderPadding()} is true.
+ * @return effective value of property paddingTop.
+ */
+ public float getEffectivePaddingTop() {
+ return paddingTop + (isUseBorderPadding() ? (getBorderWidthTop()/(isUseVariableBorders()?1f:2f)) : 0);
+ }
+
+ /**
+ * Getter for property paddingTop.
+ * @return Value of property paddingTop.
+ */
+ public float getPaddingTop() {
+ return paddingTop;
+ }
+
+ /**
+ * Setter for property paddingTop.
+ * @param paddingTop New value of property paddingTop.
+ */
+ public void setPaddingTop(float paddingTop) {
+ this.paddingTop = paddingTop;
+ }
+
+ /** Gets the effective bottom padding. This will include
+ * the bottom border width if {@link #isUseBorderPadding()} is true.
+ * @return effective value of property paddingBottom.
+ */
+ public float getEffectivePaddingBottom() {
+ return paddingBottom + (isUseBorderPadding() ? (getBorderWidthBottom()/(isUseVariableBorders()?1f:2f)) : 0);
+ }
+
+ /**
+ * Getter for property paddingBottom.
+ * @return Value of property paddingBottom.
+ */
+ public float getPaddingBottom() {
+ return paddingBottom;
+ }
+
+ /**
+ * Setter for property paddingBottom.
+ * @param paddingBottom New value of property paddingBottom.
+ */
+ public void setPaddingBottom(float paddingBottom) {
+ this.paddingBottom = paddingBottom;
+ }
+
+ /**
+ * Sets the padding of the contents in the cell (space between content and border).
+ * @param padding
+ */
+ public void setPadding(float padding) {
+ paddingBottom = padding;
+ paddingTop = padding;
+ paddingLeft = padding;
+ paddingRight = padding;
+ }
+
+ /**
+ * If true, then effective padding will include border widths
+ * @return true if effective padding includes border widths
+ */
+ public boolean isUseBorderPadding() {
+ return useBorderPadding;
+ }
+
+ /**
+ * Adjusts effective padding to include border widths.
+ * @param use adjust effective padding if true
+ */
+ public void setUseBorderPadding(boolean use) {
+ useBorderPadding = use;
+ }
+
+ /**
+ * Sets the leading fixed and variable. The resultant leading will be
+ * fixedLeading+multipliedLeading*maxFontSize where maxFontSize is the
+ * size of the bigest font in the line.
+ * @param fixedLeading the fixed leading
+ * @param multipliedLeading the variable leading
+ */
+ public void setLeading(float fixedLeading, float multipliedLeading) {
+ column.setLeading(fixedLeading, multipliedLeading);
+ }
+
+ /**
+ * Gets the fixed leading
+ * @return the leading
+ */
+ public float getLeading() {
+ return column.getLeading();
+ }
+
+ /**
+ * Gets the variable leading
+ * @return the leading
+ */
+ public float getMultipliedLeading() {
+ return column.getMultipliedLeading();
+ }
+
+ /**
+ * Sets the first paragraph line indent.
+ * @param indent the indent
+ */
+ public void setIndent(float indent) {
+ column.setIndent(indent);
+ }
+
+ /**
+ * Gets the first paragraph line indent.
+ * @return the indent
+ */
+ public float getIndent() {
+ return column.getIndent();
+ }
+
+ /**
+ * Gets the extra space between paragraphs.
+ * @return the extra space between paragraphs
+ */
+ public float getExtraParagraphSpace() {
+ return column.getExtraParagraphSpace();
+ }
+
+ /**
+ * Sets the extra space between paragraphs.
+ * @param extraParagraphSpace the extra space between paragraphs
+ */
+ public void setExtraParagraphSpace(float extraParagraphSpace) {
+ column.setExtraParagraphSpace(extraParagraphSpace);
+ }
+
+ /**
+ * Getter for property fixedHeight.
+ * @return Value of property fixedHeight.
+ */
+ public float getFixedHeight() {
+ return fixedHeight;
+ }
+
+ /**
+ * Setter for property fixedHeight.
+ * @param fixedHeight New value of property fixedHeight.
+ */
+ public void setFixedHeight(float fixedHeight) {
+ this.fixedHeight = fixedHeight;
+ minimumHeight = 0;
+ }
+
+ /**
+ * Getter for property noWrap.
+ * @return Value of property noWrap.
+ */
+ public boolean isNoWrap() {
+ return noWrap;
+ }
+
+ /**
+ * Setter for property noWrap.
+ * @param noWrap New value of property noWrap.
+ */
+ public void setNoWrap(boolean noWrap) {
+ this.noWrap = noWrap;
+ }
+
+ /**
+ * Getter for property table.
+ * @return Value of property table.
+ */
+ PdfPTable getTable() {
+ return table;
+ }
+
+ void setTable(PdfPTable table) {
+ this.table = table;
+ column.setText(null);
+ image = null;
+ if (table != null) {
+ table.setExtendLastRow(verticalAlignment == Element.ALIGN_TOP);
+ column.addElement(table);
+ table.setWidthPercentage(100);
+ }
+ }
+
+ /** Getter for property minimumHeight.
+ * @return Value of property minimumHeight.
+ */
+ public float getMinimumHeight() {
+ return minimumHeight;
+ }
+
+ /** Setter for property minimumHeight.
+ * @param minimumHeight New value of property minimumHeight.
+ */
+ public void setMinimumHeight(float minimumHeight) {
+ this.minimumHeight = minimumHeight;
+ fixedHeight = 0;
+ }
+
+ /** Getter for property colspan.
+ * @return Value of property colspan.
+ */
+ public int getColspan() {
+ return colspan;
+ }
+
+ /** Setter for property colspan.
+ * @param colspan New value of property colspan.
+ */
+ public void setColspan(int colspan) {
+ this.colspan = colspan;
+ }
+
+ /**
+ * Sets the following paragraph lines indent.
+ * @param indent the indent
+ */
+ public void setFollowingIndent(float indent) {
+ column.setFollowingIndent(indent);
+ }
+
+ /**
+ * Gets the following paragraph lines indent.
+ * @return the indent
+ */
+ public float getFollowingIndent() {
+ return column.getFollowingIndent();
+ }
+
+ /**
+ * Sets the right paragraph lines indent.
+ * @param indent the indent
+ */
+ public void setRightIndent(float indent) {
+ column.setRightIndent(indent);
+ }
+
+ /**
+ * Gets the right paragraph lines indent.
+ * @return the indent
+ */
+ public float getRightIndent() {
+ return column.getRightIndent();
+ }
+
+ /** Gets the space/character extra spacing ratio for
+ * fully justified text.
+ * @return the space/character extra spacing ratio
+ */
+ public float getSpaceCharRatio() {
+ return column.getSpaceCharRatio();
+ }
+
+ /** Sets the ratio between the extra word spacing and the extra character spacing
+ * when the text is fully justified.
+ * Extra word spacing will grow spaceCharRatio
times more than extra character spacing.
+ * If the ratio is PdfWriter.NO_SPACE_CHAR_RATIO
then the extra character spacing
+ * will be zero.
+ * @param spaceCharRatio the ratio between the extra word spacing and the extra character spacing
+ */
+ public void setSpaceCharRatio(float spaceCharRatio) {
+ column.setSpaceCharRatio(spaceCharRatio);
+ }
+
+ /**
+ * Sets the run direction of the text content in the cell (PdfWriter.RUN_DIRECTION_DEFAULT, PdfWriter.RUN_DIRECTION_NO_BIDI, PdfWriter.RUN_DIRECTION_LTR or PdfWriter.RUN_DIRECTION_RTL).
+ * @param runDirection
+ */
+ public void setRunDirection(int runDirection) {
+ column.setRunDirection(runDirection);
+ }
+
+ /**
+ * Gets the run direction of the text content in the cell
+ * @return One of the following values: PdfWriter.RUN_DIRECTION_DEFAULT, PdfWriter.RUN_DIRECTION_NO_BIDI, PdfWriter.RUN_DIRECTION_LTR or PdfWriter.RUN_DIRECTION_RTL.
+ */
+ public int getRunDirection() {
+ return column.getRunDirection();
+ }
+
+ /** Getter for property image.
+ * @return Value of property image.
+ *
+ */
+ public Image getImage() {
+ return this.image;
+ }
+
+ /** Setter for property image.
+ * @param image New value of property image.
+ *
+ */
+ public void setImage(Image image) {
+ column.setText(null);
+ table = null;
+ this.image = image;
+ }
+
+ /** Gets the cell event for this cell.
+ * @return the cell event
+ *
+ */
+ public PdfPCellEvent getCellEvent() {
+ return this.cellEvent;
+ }
+
+ /** Sets the cell event for this cell.
+ * @param event the cell event
+ *
+ */
+ public void setCellEvent(PdfPCellEvent event) {
+ if (event == null) this.cellEvent = null;
+ else if (this.cellEvent == null) this.cellEvent = event;
+ else if (this.cellEvent instanceof PdfPCellEventForwarder) ((PdfPCellEventForwarder)this.cellEvent).addCellEvent(event);
+ else {
+ PdfPCellEventForwarder forward = new PdfPCellEventForwarder();
+ forward.addCellEvent(this.cellEvent);
+ forward.addCellEvent(event);
+ this.cellEvent = forward;
+ }
+ }
+
+ /** Gets the arabic shaping options.
+ * @return the arabic shaping options
+ */
+ public int getArabicOptions() {
+ return column.getArabicOptions();
+ }
+
+ /** Sets the arabic shaping options. The option can be AR_NOVOWEL,
+ * AR_COMPOSEDTASHKEEL and AR_LIG.
+ * @param arabicOptions the arabic shaping options
+ */
+ public void setArabicOptions(int arabicOptions) {
+ column.setArabicOptions(arabicOptions);
+ }
+
+ /** Gets state of first line height based on max ascender
+ * @return true if an ascender is to be used.
+ */
+ public boolean isUseAscender() {
+ return column.isUseAscender();
+ }
+
+ /** Enables/ Disables adjustment of first line height based on max ascender.
+ *
+ * @param use adjust height if true
+ */
+ public void setUseAscender(boolean use) {
+ column.setUseAscender(use);
+ }
+
+
+ /** Getter for property useDescender.
+ * @return Value of property useDescender.
+ *
+ */
+ public boolean isUseDescender() {
+ return this.useDescender;
+ }
+
+ /** Setter for property useDescender.
+ * @param useDescender New value of property useDescender.
+ *
+ */
+ public void setUseDescender(boolean useDescender) {
+ this.useDescender = useDescender;
+ }
+
+ /**
+ * Gets the ColumnText with the content of the cell.
+ * @return a columntext object
+ */
+ public ColumnText getColumn() {
+ return column;
+ }
+
+ /**
+ * Sets the columntext in the cell.
+ * @param column
+ */
+ public void setColumn(ColumnText column) {
+ this.column = column;
+ }
+
+ /**
+ * The rotation of the cell. Possible values are
+ * 0, 90, 180 and 270.
+ */
+ private int rotation;
+
+ /**
+ * Gets the rotation of the cell.
+ * @return the rotation of the cell.
+ */
+ public int getRotation() {
+ return this.rotation;
+ }
+
+ /**
+ * Sets the rotation of the cell. Possible values are
+ * 0, 90, 180 and 270.
+ * @param rotation the rotation of the cell
+ */
+ public void setRotation(int rotation) {
+ rotation %= 360;
+ if (rotation < 0)
+ rotation += 360;
+ if ((rotation % 90) != 0)
+ throw new IllegalArgumentException("Rotation must be a multiple of 90.");
+ this.rotation = rotation;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPCellEvent.java b/src/main/java/com/lowagie/text/pdf/PdfPCellEvent.java
new file mode 100644
index 0000000..94b7573
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPCellEvent.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2003 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.Rectangle;
+
+/** An event called for a single cell.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public interface PdfPCellEvent {
+ /** This method is called at the end of the cell rendering. The text or graphics are added to
+ * one of the 4 PdfContentByte
contained in
+ * canvases
.
+ * The indexes to canvases
are:
+ *
+ * The layers are placed in sequence on top of each other.
+ * PdfPTable.BASECANVAS
- the original PdfContentByte
. Anything placed here
+ * will be under the cell.
+ * PdfPTable.BACKGROUNDCANVAS
- the layer where the background goes to.
+ * PdfPTable.LINECANVAS
- the layer where the lines go to.
+ * PdfPTable.TEXTCANVAS
- the layer where the text go to. Anything placed here
+ * will be over the cell.
+ * PdfContentByte
+ */
+ public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases);
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPKCS7.java b/src/main/java/com/lowagie/text/pdf/PdfPKCS7.java
new file mode 100644
index 0000000..d9f3473
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPKCS7.java
@@ -0,0 +1,1276 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.MessageDigest;
+import java.security.SignatureException;
+import java.security.cert.CRL;
+import java.security.cert.CRLException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509CRL;
+import java.security.cert.X509Certificate;
+import java.security.KeyStore;
+import java.io.File;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import com.lowagie.text.ExceptionConverter;
+
+import com.lowagie.bc.asn1.ASN1InputStream;
+import com.lowagie.bc.asn1.DERObject;
+import com.lowagie.bc.asn1.ASN1Sequence;
+import com.lowagie.bc.asn1.ASN1Set;
+import com.lowagie.bc.asn1.DERObjectIdentifier;
+import com.lowagie.bc.asn1.DEROctetString;
+import com.lowagie.bc.asn1.DERTaggedObject;
+import com.lowagie.bc.asn1.DERInteger;
+import com.lowagie.bc.asn1.ASN1TaggedObject;
+import com.lowagie.bc.asn1.DERConstructedSet;
+import com.lowagie.bc.asn1.DERSequence;
+import com.lowagie.bc.asn1.DERNull;
+import com.lowagie.bc.asn1.ASN1EncodableVector;
+import com.lowagie.bc.asn1.DERSet;
+import com.lowagie.bc.asn1.DERString;
+import com.lowagie.bc.asn1.DERUTCTime;
+import com.lowagie.bc.asn1.ASN1OutputStream;
+
+/**
+ * This class does all the processing related to signing and verifying a PKCS#7
+ * signature.
+ * null
for the default provider
+ * @throws SecurityException on error
+ * @throws CRLException on error
+ * @throws InvalidKeyException on error
+ * @throws CertificateException on error
+ * @throws NoSuchProviderException on error
+ * @throws NoSuchAlgorithmException on error
+ * @throws IOException on error
+ */
+ public PdfPKCS7(byte[] contentsKey, byte[] certsKey, String provider) throws SecurityException, CRLException, InvalidKeyException, CertificateException, NoSuchProviderException, NoSuchAlgorithmException, IOException {
+ CertificateFactory cf;
+ if (provider == null)
+ cf = CertificateFactory.getInstance("X.509");
+ else
+ cf = CertificateFactory.getInstance("X.509", provider);
+ if (provider == null)
+ certs = cf.generateCertificates(new ByteArrayInputStream(certsKey));
+ signCert = (X509Certificate)certs.iterator().next();
+ crls = new ArrayList();
+ ASN1InputStream in = new ASN1InputStream(new ByteArrayInputStream(contentsKey));
+ digest = ((DEROctetString)in.readObject()).getOctets();
+ if (provider == null)
+ sig = Signature.getInstance("SHA1withRSA");
+ else
+ sig = Signature.getInstance("SHA1withRSA", provider);
+ sig.initVerify(signCert.getPublicKey());
+ }
+
+ /**
+ * Verifies a signature using the sub-filter adbe.pkcs7.detached or
+ * adbe.pkcs7.sha1.
+ * @param contentsKey the /Contents key
+ * @param provider the provider or null
for the default provider
+ * @throws SecurityException on error
+ * @throws CRLException on error
+ * @throws InvalidKeyException on error
+ * @throws CertificateException on error
+ * @throws NoSuchProviderException on error
+ * @throws NoSuchAlgorithmException on error
+ */
+ public PdfPKCS7(byte[] contentsKey, String provider) throws SecurityException, CRLException, InvalidKeyException, CertificateException, NoSuchProviderException, NoSuchAlgorithmException {
+ ASN1InputStream din = new ASN1InputStream(new ByteArrayInputStream(contentsKey));
+
+ //
+ // Basic checks to make sure it's a PKCS#7 SignedData Object
+ //
+ DERObject pkcs;
+
+ try {
+ pkcs = din.readObject();
+ }
+ catch (IOException e) {
+ throw new SecurityException("can't decode PKCS7SignedData object");
+ }
+ if (!(pkcs instanceof ASN1Sequence)) {
+ throw new SecurityException("Not a valid PKCS#7 object - not a sequence");
+ }
+ ASN1Sequence signedData = (ASN1Sequence)pkcs;
+ DERObjectIdentifier objId = (DERObjectIdentifier)signedData.getObjectAt(0);
+ if (!objId.getId().equals(ID_PKCS7_SIGNED_DATA))
+ throw new SecurityException("Not a valid PKCS#7 object - not signed data");
+ ASN1Sequence content = (ASN1Sequence)((DERTaggedObject)signedData.getObjectAt(1)).getObject();
+ // the positions that we care are:
+ // 0 - version
+ // 1 - digestAlgorithms
+ // 2 - possible ID_PKCS7_DATA
+ // (the certificates and crls are taken out by other means)
+ // last - signerInfos
+
+ // the version
+ version = ((DERInteger)content.getObjectAt(0)).getValue().intValue();
+
+ // the digestAlgorithms
+ digestalgos = new HashSet();
+ Enumeration e = ((ASN1Set)content.getObjectAt(1)).getObjects();
+ while (e.hasMoreElements())
+ {
+ ASN1Sequence s = (ASN1Sequence)e.nextElement();
+ DERObjectIdentifier o = (DERObjectIdentifier)s.getObjectAt(0);
+ digestalgos.add(o.getId());
+ }
+
+ // the certificates and crls
+ CertificateFactory cf;
+ if (provider == null)
+ cf = CertificateFactory.getInstance("X.509");
+ else
+ cf = CertificateFactory.getInstance("X.509", provider);
+ certs = cf.generateCertificates(new ByteArrayInputStream(contentsKey));
+ crls = cf.generateCRLs(new ByteArrayInputStream(contentsKey));
+
+ // the possible ID_PKCS7_DATA
+ ASN1Sequence rsaData = (ASN1Sequence)content.getObjectAt(2);
+ if (rsaData.size() > 1) {
+ DEROctetString rsaDataContent = (DEROctetString)((DERTaggedObject)rsaData.getObjectAt(1)).getObject();
+ RSAdata = rsaDataContent.getOctets();
+ }
+
+ // the signerInfos
+ int next = 3;
+ while (content.getObjectAt(next) instanceof DERTaggedObject)
+ ++next;
+ ASN1Set signerInfos = (ASN1Set)content.getObjectAt(next);
+ if (signerInfos.size() != 1)
+ throw new SecurityException("This PKCS#7 object has multiple SignerInfos - only one is supported at this time");
+ ASN1Sequence signerInfo = (ASN1Sequence)signerInfos.getObjectAt(0);
+ // the positions that we care are
+ // 0 - version
+ // 1 - the signing certificate serial number
+ // 2 - the digest algorithm
+ // 3 or 4 - digestEncryptionAlgorithm
+ // 4 or 5 - encryptedDigest
+ signerversion = ((DERInteger)signerInfo.getObjectAt(0)).getValue().intValue();
+ // Get the signing certificate
+ ASN1Sequence issuerAndSerialNumber = (ASN1Sequence)signerInfo.getObjectAt(1);
+ BigInteger serialNumber = ((DERInteger)issuerAndSerialNumber.getObjectAt(1)).getValue();
+ for (Iterator i = certs.iterator(); i.hasNext();) {
+ X509Certificate cert = (X509Certificate)i.next();
+ if (serialNumber.equals(cert.getSerialNumber())) {
+ signCert = cert;
+ break;
+ }
+ }
+ if (signCert == null) {
+ throw new SecurityException("Can't find signing certificate with serial " + serialNumber.toString(16));
+ }
+ digestAlgorithm = ((DERObjectIdentifier)((ASN1Sequence)signerInfo.getObjectAt(2)).getObjectAt(0)).getId();
+ next = 3;
+ if (signerInfo.getObjectAt(next) instanceof ASN1TaggedObject) {
+ ASN1TaggedObject tagsig = (ASN1TaggedObject)signerInfo.getObjectAt(next);
+ ASN1Sequence sseq = (ASN1Sequence)tagsig.getObject();
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+ ASN1OutputStream dout = new ASN1OutputStream(bOut);
+ try {
+ ASN1EncodableVector attribute = new ASN1EncodableVector();
+ for (int k = 0; k < sseq.size(); ++k) {
+ attribute.add(sseq.getObjectAt(k));
+ }
+ dout.writeObject(new DERSet(attribute));
+ dout.close();
+ }
+ catch (IOException ioe){}
+ sigAttr = bOut.toByteArray();
+
+ for (int k = 0; k < sseq.size(); ++k) {
+ ASN1Sequence seq2 = (ASN1Sequence)sseq.getObjectAt(k);
+ if (((DERObjectIdentifier)seq2.getObjectAt(0)).getId().equals(ID_MESSAGE_DIGEST)) {
+ ASN1Set set = (ASN1Set)seq2.getObjectAt(1);
+ digestAttr = ((DEROctetString)set.getObjectAt(0)).getOctets();
+ break;
+ }
+ }
+ if (digestAttr == null)
+ throw new SecurityException("Authenticated attribute is missing the digest.");
+ ++next;
+ }
+ digestEncryptionAlgorithm = ((DERObjectIdentifier)((ASN1Sequence)signerInfo.getObjectAt(next++)).getObjectAt(0)).getId();
+ digest = ((DEROctetString)signerInfo.getObjectAt(next)).getOctets();
+ if (RSAdata != null || digestAttr != null) {
+ if (provider == null || provider.startsWith("SunPKCS11"))
+ messageDigest = MessageDigest.getInstance(getHashAlgorithm());
+ else
+ messageDigest = MessageDigest.getInstance(getHashAlgorithm(), provider);
+ }
+ if (provider == null)
+ sig = Signature.getInstance(getDigestAlgorithm());
+ else
+ sig = Signature.getInstance(getDigestAlgorithm(), provider);
+ sig.initVerify(signCert.getPublicKey());
+ }
+
+ /**
+ * Generates a signature.
+ * @param privKey the private key
+ * @param certChain the certificate chain
+ * @param crlList the certificate revocation list
+ * @param hashAlgorithm the hash algorithm
+ * @param provider the provider or null
for the default provider
+ * @param hasRSAdata true
if the sub-filter is adbe.pkcs7.sha1
+ * @throws SecurityException on error
+ * @throws InvalidKeyException on error
+ * @throws NoSuchProviderException on error
+ * @throws NoSuchAlgorithmException on error
+ */
+ public PdfPKCS7(PrivateKey privKey, Certificate[] certChain, CRL[] crlList,
+ String hashAlgorithm, String provider, boolean hasRSAdata)
+ throws SecurityException, InvalidKeyException, NoSuchProviderException,
+ NoSuchAlgorithmException
+ {
+ this.privKey = privKey;
+
+ if (hashAlgorithm.equals("MD5")) {
+ digestAlgorithm = ID_MD5;
+ }
+ else if (hashAlgorithm.equals("MD2")) {
+ digestAlgorithm = ID_MD2;
+ }
+ else if (hashAlgorithm.equals("SHA")) {
+ digestAlgorithm = ID_SHA1;
+ }
+ else if (hashAlgorithm.equals("SHA1")) {
+ digestAlgorithm = ID_SHA1;
+ }
+ else {
+ throw new NoSuchAlgorithmException("Unknown Hash Algorithm "+hashAlgorithm);
+ }
+
+ version = signerversion = 1;
+ certs = new ArrayList();
+ crls = new ArrayList();
+ digestalgos = new HashSet();
+ digestalgos.add(digestAlgorithm);
+
+ //
+ // Copy in the certificates and crls used to sign the private key.
+ //
+ signCert = (X509Certificate)certChain[0];
+ for (int i = 0;i < certChain.length;i++) {
+ certs.add(certChain[i]);
+ }
+
+ if (crlList != null) {
+ for (int i = 0;i < crlList.length;i++) {
+ crls.add(crlList[i]);
+ }
+ }
+
+ if (privKey != null) {
+ //
+ // Now we have private key, find out what the digestEncryptionAlgorithm is.
+ //
+ digestEncryptionAlgorithm = privKey.getAlgorithm();
+ if (digestEncryptionAlgorithm.equals("RSA")) {
+ digestEncryptionAlgorithm = ID_RSA;
+ }
+ else if (digestEncryptionAlgorithm.equals("DSA")) {
+ digestEncryptionAlgorithm = ID_DSA;
+ }
+ else {
+ throw new NoSuchAlgorithmException("Unknown Key Algorithm "+digestEncryptionAlgorithm);
+ }
+ }
+ if (hasRSAdata) {
+ RSAdata = new byte[0];
+ if (provider == null || provider.startsWith("SunPKCS11"))
+ messageDigest = MessageDigest.getInstance(getHashAlgorithm());
+ else
+ messageDigest = MessageDigest.getInstance(getHashAlgorithm(), provider);
+ }
+
+ if (privKey != null) {
+ if (provider == null)
+ sig = Signature.getInstance(getDigestAlgorithm());
+ else
+ sig = Signature.getInstance(getDigestAlgorithm(), provider);
+
+ sig.initSign(privKey);
+ }
+ }
+
+ /**
+ * Update the digest with the specified bytes. This method is used both for signing and verifying
+ * @param buf the data buffer
+ * @param off the offset in the data buffer
+ * @param len the data length
+ * @throws SignatureException on error
+ */
+ public void update(byte[] buf, int off, int len) throws SignatureException {
+ if (RSAdata != null || digestAttr != null)
+ messageDigest.update(buf, off, len);
+ else
+ sig.update(buf, off, len);
+ }
+
+ /**
+ * Verify the digest.
+ * @throws SignatureException on error
+ * @return true
if the signature checks out, false
otherwise
+ */
+ public boolean verify() throws SignatureException {
+ if (verified)
+ return verifyResult;
+ if (sigAttr != null) {
+ sig.update(sigAttr);
+ if (RSAdata != null) {
+ byte msd[] = messageDigest.digest();
+ messageDigest.update(msd);
+ }
+ verifyResult = (Arrays.equals(messageDigest.digest(), digestAttr) && sig.verify(digest));
+ }
+ else {
+ if (RSAdata != null)
+ sig.update(messageDigest.digest());
+ verifyResult = sig.verify(digest);
+ }
+ verified = true;
+ return verifyResult;
+ }
+
+ /**
+ * Get the X.509 certificates associated with this PKCS#7 object
+ * @return the X.509 certificates associated with this PKCS#7 object
+ */
+ public Certificate[] getCertificates() {
+ return (X509Certificate[])certs.toArray(new X509Certificate[0]);
+ }
+
+ /**
+ * Get the X.509 certificate revocation lists associated with this PKCS#7 object
+ * @return the X.509 certificate revocation lists associated with this PKCS#7 object
+ */
+ public Collection getCRLs() {
+ return crls;
+ }
+
+ /**
+ * Get the X.509 certificate actually used to sign the digest.
+ * @return the X.509 certificate actually used to sign the digest
+ */
+ public X509Certificate getSigningCertificate() {
+ return signCert;
+ }
+
+ /**
+ * Get the version of the PKCS#7 object. Always 1
+ * @return the version of the PKCS#7 object. Always 1
+ */
+ public int getVersion() {
+ return version;
+ }
+
+ /**
+ * Get the version of the PKCS#7 "SignerInfo" object. Always 1
+ * @return the version of the PKCS#7 "SignerInfo" object. Always 1
+ */
+ public int getSigningInfoVersion() {
+ return signerversion;
+ }
+
+ /**
+ * Get the algorithm used to calculate the message digest
+ * @return the algorithm used to calculate the message digest
+ */
+ public String getDigestAlgorithm() {
+ String dea = digestEncryptionAlgorithm;
+
+ if (digestEncryptionAlgorithm.equals(ID_RSA)) {
+ dea = "RSA";
+ }
+ else if (digestEncryptionAlgorithm.equals(ID_DSA)) {
+ dea = "DSA";
+ }
+
+ return getHashAlgorithm() + "with" + dea;
+ }
+
+ /**
+ * Returns the algorithm.
+ * @return the digest algorithm
+ */
+ public String getHashAlgorithm() {
+ String da = digestAlgorithm;
+
+ if (digestAlgorithm.equals(ID_MD5) || digestAlgorithm.equals(ID_MD5RSA)) {
+ da = "MD5";
+ }
+ else if (digestAlgorithm.equals(ID_MD2) || digestAlgorithm.equals(ID_MD2RSA)) {
+ da = "MD2";
+ }
+ else if (digestAlgorithm.equals(ID_SHA1) || digestAlgorithm.equals(ID_SHA1RSA)) {
+ da = "SHA1";
+ }
+ return da;
+ }
+
+ /**
+ * Loads the default root certificates at <java.home>/lib/security/cacerts
+ * with the default provider.
+ * @return a KeyStore
+ */
+ public static KeyStore loadCacertsKeyStore() {
+ return loadCacertsKeyStore(null);
+ }
+
+ /**
+ * Loads the default root certificates at <java.home>/lib/security/cacerts.
+ * @param provider the provider or null
for the default provider
+ * @return a KeyStore
+ */
+ public static KeyStore loadCacertsKeyStore(String provider) {
+ File file = new File(System.getProperty("java.home"), "lib");
+ file = new File(file, "security");
+ file = new File(file, "cacerts");
+ FileInputStream fin = null;
+ try {
+ fin = new FileInputStream(file);
+ KeyStore k;
+ if (provider == null)
+ k = KeyStore.getInstance("JKS");
+ else
+ k = KeyStore.getInstance("JKS", provider);
+ k.load(fin, null);
+ return k;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ finally {
+ try{fin.close();}catch(Exception ex){}
+ }
+ }
+
+ /**
+ * Verifies a single certificate.
+ * @param cert the certificate to verify
+ * @param crls the certificate revocation list or null
+ * @param calendar the date or null
for the current date
+ * @return a String
with the error description or null
+ * if no error
+ */
+ public static String verifyCertificate(X509Certificate cert, Collection crls, Calendar calendar) {
+ if (calendar == null)
+ calendar = new GregorianCalendar();
+ if (cert.hasUnsupportedCriticalExtension())
+ return "Has unsupported critical extension";
+ try {
+ cert.checkValidity(calendar.getTime());
+ }
+ catch (Exception e) {
+ return e.getMessage();
+ }
+ if (crls != null) {
+ for (Iterator it = crls.iterator(); it.hasNext();) {
+ if (((CRL)it.next()).isRevoked(cert))
+ return "Certificate revoked";
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Verifies a certificate chain against a KeyStore.
+ * @param certs the certificate chain
+ * @param keystore the KeyStore
+ * @param crls the certificate revocation list or null
+ * @param calendar the date or null
for the current date
+ * @return null
if the certificate chain could be validade or a
+ * Object[]{cert,error}
where cert
is the
+ * failed certificate and error
is the error message
+ */
+ public static Object[] verifyCertificates(Certificate certs[], KeyStore keystore, Collection crls, Calendar calendar) {
+ if (calendar == null)
+ calendar = new GregorianCalendar();
+ for (int k = 0; k < certs.length; ++k) {
+ X509Certificate cert = (X509Certificate)certs[k];
+ String err = verifyCertificate(cert, crls, calendar);
+ if (err != null)
+ return new Object[]{cert, err};
+ try {
+ for (Enumeration aliases = keystore.aliases(); aliases.hasMoreElements();) {
+ try {
+ String alias = (String)aliases.nextElement();
+ if (!keystore.isCertificateEntry(alias))
+ continue;
+ X509Certificate certStoreX509 = (X509Certificate)keystore.getCertificate(alias);
+ if (verifyCertificate(certStoreX509, crls, calendar) != null)
+ continue;
+ try {
+ cert.verify(certStoreX509.getPublicKey());
+ return null;
+ }
+ catch (Exception e) {
+ continue;
+ }
+ }
+ catch (Exception ex) {
+ }
+ }
+ }
+ catch (Exception e) {
+ }
+ int j;
+ for (j = 0; j < certs.length; ++j) {
+ if (j == k)
+ continue;
+ X509Certificate certNext = (X509Certificate)certs[j];
+ try {
+ cert.verify(certNext.getPublicKey());
+ break;
+ }
+ catch (Exception e) {
+ }
+ }
+ if (j == certs.length)
+ return new Object[]{cert, "Cannot be verified against the KeyStore or the certificate chain"};
+ }
+ return new Object[]{null, "Invalid state. Possible circular certificate chain"};
+ }
+
+ /**
+ * Get the "issuer" from the TBSCertificate bytes that are passed in
+ * @param enc a TBSCertificate in a byte array
+ * @return a DERObject
+ */
+ private static DERObject getIssuer(byte[] enc) {
+ try {
+ ASN1InputStream in = new ASN1InputStream(new ByteArrayInputStream(enc));
+ ASN1Sequence seq = (ASN1Sequence)in.readObject();
+ return (DERObject)seq.getObjectAt(seq.getObjectAt(0) instanceof DERTaggedObject ? 3 : 2);
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Get the "subject" from the TBSCertificate bytes that are passed in
+ * @param enc A TBSCertificate in a byte array
+ * @return a DERObject
+ */
+ private static DERObject getSubject(byte[] enc) {
+ try {
+ ASN1InputStream in = new ASN1InputStream(new ByteArrayInputStream(enc));
+ ASN1Sequence seq = (ASN1Sequence)in.readObject();
+ return (DERObject)seq.getObjectAt(seq.getObjectAt(0) instanceof DERTaggedObject ? 5 : 4);
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Get the issuer fields from an X509 Certificate
+ * @param cert an X509Certificate
+ * @return an X509Name
+ */
+ public static X509Name getIssuerFields(X509Certificate cert) {
+ try {
+ return new X509Name((ASN1Sequence)getIssuer(cert.getTBSCertificate()));
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Get the subject fields from an X509 Certificate
+ * @param cert an X509Certificate
+ * @return an X509Name
+ */
+ public static X509Name getSubjectFields(X509Certificate cert) {
+ try {
+ return new X509Name((ASN1Sequence)getSubject(cert.getTBSCertificate()));
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Gets the bytes for the PKCS#1 object.
+ * @return a byte array
+ */
+ public byte[] getEncodedPKCS1() {
+ try {
+ if (externalDigest != null)
+ digest = externalDigest;
+ else
+ digest = sig.sign();
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ ASN1OutputStream dout = new ASN1OutputStream(bOut);
+ dout.writeObject(new DEROctetString(digest));
+ dout.close();
+
+ return bOut.toByteArray();
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Sets the digest/signature to an external calculated value.
+ * @param digest the digest. This is the actual signature
+ * @param RSAdata the extra data that goes into the data tag in PKCS#7
+ * @param digestEncryptionAlgorithm the encryption algorithm. It may must be null
if the digest
+ * is also null
. If the digest
is not null
+ * then it may be "RSA" or "DSA"
+ */
+ public void setExternalDigest(byte digest[], byte RSAdata[], String digestEncryptionAlgorithm) {
+ externalDigest = digest;
+ externalRSAdata = RSAdata;
+ if (digestEncryptionAlgorithm != null) {
+ if (digestEncryptionAlgorithm.equals("RSA")) {
+ this.digestEncryptionAlgorithm = ID_RSA;
+ }
+ else if (digestEncryptionAlgorithm.equals("DSA")) {
+ this.digestEncryptionAlgorithm = ID_DSA;
+ }
+ else
+ throw new ExceptionConverter(new NoSuchAlgorithmException("Unknown Key Algorithm "+digestEncryptionAlgorithm));
+ }
+ }
+
+ /**
+ * Gets the bytes for the PKCS7SignedData object.
+ * @return the bytes for the PKCS7SignedData object
+ */
+ public byte[] getEncodedPKCS7() {
+ return getEncodedPKCS7(null, null);
+ }
+
+ /**
+ * Gets the bytes for the PKCS7SignedData object. Optionally the authenticatedAttributes
+ * in the signerInfo can also be set. If either of the parameters is null
, none will be used.
+ * @param secondDigest the digest in the authenticatedAttributes
+ * @param signingTime the signing time in the authenticatedAttributes
+ * @return the bytes for the PKCS7SignedData object
+ */
+ public byte[] getEncodedPKCS7(byte secondDigest[], Calendar signingTime) {
+ try {
+ if (externalDigest != null) {
+ digest = externalDigest;
+ if (RSAdata != null)
+ RSAdata = externalRSAdata;
+ }
+ else if (externalRSAdata != null && RSAdata != null) {
+ RSAdata = externalRSAdata;
+ sig.update(RSAdata);
+ digest = sig.sign();
+ }
+ else {
+ if (RSAdata != null) {
+ RSAdata = messageDigest.digest();
+ sig.update(RSAdata);
+ }
+ digest = sig.sign();
+ }
+
+ // Create the set of Hash algorithms
+ DERConstructedSet digestAlgorithms = new DERConstructedSet();
+ for(Iterator it = digestalgos.iterator(); it.hasNext();) {
+ ASN1EncodableVector algos = new ASN1EncodableVector();
+ algos.add(new DERObjectIdentifier((String)it.next()));
+ algos.add(new DERNull());
+ digestAlgorithms.addObject(new DERSequence(algos));
+ }
+
+ // Create the contentInfo.
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(new DERObjectIdentifier(ID_PKCS7_DATA));
+ if (RSAdata != null)
+ v.add(new DERTaggedObject(0, new DEROctetString(RSAdata)));
+ DERSequence contentinfo = new DERSequence(v);
+
+ // Get all the certificates
+ //
+ v = new ASN1EncodableVector();
+ for (Iterator i = certs.iterator(); i.hasNext();) {
+ ASN1InputStream tempstream = new ASN1InputStream(new ByteArrayInputStream(((X509Certificate)i.next()).getEncoded()));
+ v.add(tempstream.readObject());
+ }
+
+ DERSet dercertificates = new DERSet(v);
+
+ // Create signerinfo structure.
+ //
+ ASN1EncodableVector signerinfo = new ASN1EncodableVector();
+
+ // Add the signerInfo version
+ //
+ signerinfo.add(new DERInteger(signerversion));
+
+ v = new ASN1EncodableVector();
+ v.add(getIssuer(signCert.getTBSCertificate()));
+ v.add(new DERInteger(signCert.getSerialNumber()));
+ signerinfo.add(new DERSequence(v));
+
+ // Add the digestAlgorithm
+ v = new ASN1EncodableVector();
+ v.add(new DERObjectIdentifier(digestAlgorithm));
+ v.add(new DERNull());
+ signerinfo.add(new DERSequence(v));
+
+ // add the authenticated attribute if present
+ if (secondDigest != null && signingTime != null) {
+ ASN1EncodableVector attribute = new ASN1EncodableVector();
+ v = new ASN1EncodableVector();
+ v.add(new DERObjectIdentifier(ID_CONTENT_TYPE));
+ v.add(new DERSet(new DERObjectIdentifier(ID_PKCS7_DATA)));
+ attribute.add(new DERSequence(v));
+ v = new ASN1EncodableVector();
+ v.add(new DERObjectIdentifier(ID_SIGNING_TIME));
+ v.add(new DERSet(new DERUTCTime(signingTime.getTime())));
+ attribute.add(new DERSequence(v));
+ v = new ASN1EncodableVector();
+ v.add(new DERObjectIdentifier(ID_MESSAGE_DIGEST));
+ v.add(new DERSet(new DEROctetString(secondDigest)));
+ attribute.add(new DERSequence(v));
+ signerinfo.add(new DERTaggedObject(false, 0, new DERSet(attribute)));
+ }
+ // Add the digestEncryptionAlgorithm
+ v = new ASN1EncodableVector();
+ v.add(new DERObjectIdentifier(digestEncryptionAlgorithm));
+ v.add(new DERNull());
+ signerinfo.add(new DERSequence(v));
+
+ // Add the digest
+ signerinfo.add(new DEROctetString(digest));
+
+
+ // Finally build the body out of all the components above
+ ASN1EncodableVector body = new ASN1EncodableVector();
+ body.add(new DERInteger(version));
+ body.add(digestAlgorithms);
+ body.add(contentinfo);
+ body.add(new DERTaggedObject(false, 0, dercertificates));
+
+ if (crls.size() > 0) {
+ v = new ASN1EncodableVector();
+ for (Iterator i = crls.iterator();i.hasNext();) {
+ ASN1InputStream t = new ASN1InputStream(new ByteArrayInputStream((((X509CRL)i.next()).getEncoded())));
+ v.add(t.readObject());
+ }
+ DERSet dercrls = new DERSet(v);
+ body.add(new DERTaggedObject(false, 1, dercrls));
+ }
+
+ // Only allow one signerInfo
+ body.add(new DERSet(new DERSequence(signerinfo)));
+
+ // Now we have the body, wrap it in it's PKCS7Signed shell
+ // and return it
+ //
+ ASN1EncodableVector whole = new ASN1EncodableVector();
+ whole.add(new DERObjectIdentifier(ID_PKCS7_SIGNED_DATA));
+ whole.add(new DERTaggedObject(0, new DERSequence(body)));
+
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ ASN1OutputStream dout = new ASN1OutputStream(bOut);
+ dout.writeObject(new DERSequence(whole));
+ dout.close();
+
+ return bOut.toByteArray();
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+
+ /**
+ * When using authenticatedAttributes the authentication process is different.
+ * The document digest is generated and put inside the attribute. The signing is done over the DER encoded
+ * authenticatedAttributes. This method provides that encoding and the parameters must be
+ * exactly the same as in {@link #getEncodedPKCS7(byte[],Calendar)}.
+ *
+ * Calendar cal = Calendar.getInstance();
+ * PdfPKCS7 pk7 = new PdfPKCS7(key, chain, null, "SHA1", null, false);
+ * MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
+ * byte buf[] = new byte[8192];
+ * int n;
+ * InputStream inp = sap.getRangeStream();
+ * while ((n = inp.read(buf)) > 0) {
+ * messageDigest.update(buf, 0, n);
+ * }
+ * byte hash[] = messageDigest.digest();
+ * byte sh[] = pk7.getAuthenticatedAttributeBytes(hash, cal);
+ * pk7.update(sh, 0, sh.length);
+ * byte sg[] = pk7.getEncodedPKCS7(hash, cal);
+ *
+ * @param secondDigest the content digest
+ * @param signingTime the signing time
+ * @return the byte array representation of the authenticatedAttributes ready to be signed
+ */
+ public byte[] getAuthenticatedAttributeBytes(byte secondDigest[], Calendar signingTime) {
+ try {
+ ASN1EncodableVector attribute = new ASN1EncodableVector();
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(new DERObjectIdentifier(ID_CONTENT_TYPE));
+ v.add(new DERSet(new DERObjectIdentifier(ID_PKCS7_DATA)));
+ attribute.add(new DERSequence(v));
+ v = new ASN1EncodableVector();
+ v.add(new DERObjectIdentifier(ID_SIGNING_TIME));
+ v.add(new DERSet(new DERUTCTime(signingTime.getTime())));
+ attribute.add(new DERSequence(v));
+ v = new ASN1EncodableVector();
+ v.add(new DERObjectIdentifier(ID_MESSAGE_DIGEST));
+ v.add(new DERSet(new DEROctetString(secondDigest)));
+ attribute.add(new DERSequence(v));
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ ASN1OutputStream dout = new ASN1OutputStream(bOut);
+ dout.writeObject(new DERSet(attribute));
+ dout.close();
+
+ return bOut.toByteArray();
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+ /**
+ * Getter for property reason.
+ * @return Value of property reason.
+ */
+ public String getReason() {
+ return this.reason;
+ }
+
+ /**
+ * Setter for property reason.
+ * @param reason New value of property reason.
+ */
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+
+ /**
+ * Getter for property location.
+ * @return Value of property location.
+ */
+ public String getLocation() {
+ return this.location;
+ }
+
+ /**
+ * Setter for property location.
+ * @param location New value of property location.
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Getter for property signDate.
+ * @return Value of property signDate.
+ */
+ public Calendar getSignDate() {
+ return this.signDate;
+ }
+
+ /**
+ * Setter for property signDate.
+ * @param signDate New value of property signDate.
+ */
+ public void setSignDate(Calendar signDate) {
+ this.signDate = signDate;
+ }
+
+ /**
+ * Getter for property sigName.
+ * @return Value of property sigName.
+ */
+ public String getSignName() {
+ return this.signName;
+ }
+
+ /**
+ * Setter for property sigName.
+ * @param signName New value of property sigName.
+ */
+ public void setSignName(String signName) {
+ this.signName = signName;
+ }
+
+ /**
+ * a class that holds an X509 name
+ */
+ public static class X509Name {
+ /**
+ * country code - StringType(SIZE(2))
+ */
+ public static final DERObjectIdentifier C = new DERObjectIdentifier("2.5.4.6");
+
+ /**
+ * organization - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier O = new DERObjectIdentifier("2.5.4.10");
+
+ /**
+ * organizational unit name - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier OU = new DERObjectIdentifier("2.5.4.11");
+
+ /**
+ * Title
+ */
+ public static final DERObjectIdentifier T = new DERObjectIdentifier("2.5.4.12");
+
+ /**
+ * common name - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier CN = new DERObjectIdentifier("2.5.4.3");
+
+ /**
+ * device serial number name - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier SN = new DERObjectIdentifier("2.5.4.5");
+
+ /**
+ * locality name - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier L = new DERObjectIdentifier("2.5.4.7");
+
+ /**
+ * state, or province name - StringType(SIZE(1..64))
+ */
+ public static final DERObjectIdentifier ST = new DERObjectIdentifier("2.5.4.8");
+
+ /** Naming attribute of type X520name */
+ public static final DERObjectIdentifier SURNAME = new DERObjectIdentifier("2.5.4.4");
+ /** Naming attribute of type X520name */
+ public static final DERObjectIdentifier GIVENNAME = new DERObjectIdentifier("2.5.4.42");
+ /** Naming attribute of type X520name */
+ public static final DERObjectIdentifier INITIALS = new DERObjectIdentifier("2.5.4.43");
+ /** Naming attribute of type X520name */
+ public static final DERObjectIdentifier GENERATION = new DERObjectIdentifier("2.5.4.44");
+ /** Naming attribute of type X520name */
+ public static final DERObjectIdentifier UNIQUE_IDENTIFIER = new DERObjectIdentifier("2.5.4.45");
+
+ /**
+ * Email address (RSA PKCS#9 extension) - IA5String.
+ * PdfPSXObject
. All
+ * the members are copied by reference but the buffer stays different.
+ * @return a copy of this PdfPSXObject
+ */
+
+ public PdfContentByte getDuplicate() {
+ PdfPSXObject tpl = new PdfPSXObject();
+ tpl.writer = writer;
+ tpl.pdf = pdf;
+ tpl.thisReference = thisReference;
+ tpl.pageResources = pageResources;
+ tpl.separator = separator;
+ return tpl;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPTable.java b/src/main/java/com/lowagie/text/pdf/PdfPTable.java
new file mode 100644
index 0000000..280bcf0
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPTable.java
@@ -0,0 +1,1081 @@
+/*
+ * $Id: PdfPTable.java,v 1.66 2005/11/19 18:17:51 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.ArrayList;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Element;
+import com.lowagie.text.Image;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.ElementListener;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.pdf.events.PdfPTableEventForwarder;
+
+/** This is a table that can be put at an absolute position but can also
+ * be added to the document as the class Table
.
+ * In the last case when crossing pages the table always break at full rows; if a
+ * row is bigger than the page it is dropped silently to avoid infinite loops.
+ * PdfcontentByte
.
+ */
+ public static final int BASECANVAS = 0;
+ /** The index of the duplicate PdfContentByte
where the background will be drawn.
+ */
+ public static final int BACKGROUNDCANVAS = 1;
+ /** The index of the duplicate PdfContentByte
where the border lines will be drawn.
+ */
+ public static final int LINECANVAS = 2;
+ /** The index of the duplicate PdfContentByte
where the text will be drawn.
+ */
+ public static final int TEXTCANVAS = 3;
+
+ protected ArrayList rows = new ArrayList();
+ protected float totalHeight = 0;
+ protected PdfPCell currentRow[];
+ protected int currentRowIdx = 0;
+ protected PdfPCell defaultCell = new PdfPCell((Phrase)null);
+ protected float totalWidth = 0;
+ protected float relativeWidths[];
+ protected float absoluteWidths[];
+ protected PdfPTableEvent tableEvent;
+
+/** Holds value of property headerRows. */
+ protected int headerRows;
+
+/** Holds value of property widthPercentage. */
+ protected float widthPercentage = 80;
+
+/** Holds value of property horizontalAlignment. */
+ private int horizontalAlignment = Element.ALIGN_CENTER;
+
+/** Holds value of property skipFirstHeader. */
+ private boolean skipFirstHeader = false;
+
+ protected boolean isColspan = false;
+
+ protected int runDirection = PdfWriter.RUN_DIRECTION_DEFAULT;
+
+ /**
+ * Holds value of property lockedWidth.
+ */
+ private boolean lockedWidth = false;
+
+ /**
+ * Holds value of property splitRows.
+ */
+ private boolean splitRows = true;
+
+/** The spacing before the table. */
+ protected float spacingBefore;
+
+/** The spacing after the table. */
+ protected float spacingAfter;
+
+ /**
+ * Holds value of property extendLastRow.
+ */
+ private boolean extendLastRow;
+
+ /**
+ * Holds value of property headersInEvent.
+ */
+ private boolean headersInEvent;
+
+ /**
+ * Holds value of property splitLate.
+ */
+ private boolean splitLate = true;
+
+ /**
+ * Defines if the table should be kept
+ * on one page if possible
+ */
+ private boolean keepTogether;
+
+ /**
+ * Holds value of property footerRows.
+ */
+ private int footerRows;
+
+ protected PdfPTable() {
+ }
+
+ /** Constructs a PdfPTable
with the relative column widths.
+ * @param relativeWidths the relative column widths
+ */
+ public PdfPTable(float relativeWidths[]) {
+ if (relativeWidths == null)
+ throw new NullPointerException("The widths array in PdfPTable constructor can not be null.");
+ if (relativeWidths.length == 0)
+ throw new IllegalArgumentException("The widths array in PdfPTable constructor can not have zero length.");
+ this.relativeWidths = new float[relativeWidths.length];
+ System.arraycopy(relativeWidths, 0, this.relativeWidths, 0, relativeWidths.length);
+ absoluteWidths = new float[relativeWidths.length];
+ calculateWidths();
+ currentRow = new PdfPCell[absoluteWidths.length];
+ keepTogether = false;
+ }
+
+ /** Constructs a PdfPTable
with numColumns
columns.
+ * @param numColumns the number of columns
+ */
+ public PdfPTable(int numColumns) {
+ if (numColumns <= 0)
+ throw new IllegalArgumentException("The number of columns in PdfPTable constructor must be greater than zero.");
+ relativeWidths = new float[numColumns];
+ for (int k = 0; k < numColumns; ++k)
+ relativeWidths[k] = 1;
+ absoluteWidths = new float[relativeWidths.length];
+ calculateWidths();
+ currentRow = new PdfPCell[absoluteWidths.length];
+ keepTogether = false;
+ }
+
+ /** Constructs a copy of a PdfPTable
.
+ * @param table the PdfPTable
to be copied
+ */
+ public PdfPTable(PdfPTable table) {
+ copyFormat(table);
+ for (int k = 0; k < currentRow.length; ++k) {
+ if (table.currentRow[k] == null)
+ break;
+ currentRow[k] = new PdfPCell(table.currentRow[k]);
+ }
+ for (int k = 0; k < table.rows.size(); ++k) {
+ PdfPRow row = (PdfPRow)(table.rows.get(k));
+ if (row != null)
+ row = new PdfPRow(row);
+ rows.add(row);
+ }
+ }
+
+ /**
+ * Makes a shallow copy of a table (format without content).
+ * @param table
+ * @return a shallow copy of the table
+ */
+ public static PdfPTable shallowCopy(PdfPTable table) {
+ PdfPTable nt = new PdfPTable();
+ nt.copyFormat(table);
+ return nt;
+ }
+
+ /**
+ * Copies the format of the sourceTable without copying the content.
+ * @param sourceTable
+ */
+ private void copyFormat(PdfPTable sourceTable) {
+ relativeWidths = new float[sourceTable.relativeWidths.length];
+ absoluteWidths = new float[sourceTable.relativeWidths.length];
+ System.arraycopy(sourceTable.relativeWidths, 0, relativeWidths, 0, relativeWidths.length);
+ System.arraycopy(sourceTable.absoluteWidths, 0, absoluteWidths, 0, relativeWidths.length);
+ totalWidth = sourceTable.totalWidth;
+ totalHeight = sourceTable.totalHeight;
+ currentRowIdx = 0;
+ tableEvent = sourceTable.tableEvent;
+ runDirection = sourceTable.runDirection;
+ defaultCell = new PdfPCell(sourceTable.defaultCell);
+ currentRow = new PdfPCell[sourceTable.currentRow.length];
+ isColspan = sourceTable.isColspan;
+ splitRows = sourceTable.splitRows;
+ spacingAfter = sourceTable.spacingAfter;
+ spacingBefore = sourceTable.spacingBefore;
+ headerRows = sourceTable.headerRows;
+ footerRows = sourceTable.footerRows;
+ lockedWidth = sourceTable.lockedWidth;
+ extendLastRow = sourceTable.extendLastRow;
+ headersInEvent = sourceTable.headersInEvent;
+ widthPercentage = sourceTable.widthPercentage;
+ splitLate = sourceTable.splitLate;
+ skipFirstHeader = sourceTable.skipFirstHeader;
+ horizontalAlignment = sourceTable.horizontalAlignment;
+ keepTogether = sourceTable.keepTogether;
+ }
+
+ /** Sets the relative widths of the table.
+ * @param relativeWidths the relative widths of the table.
+ * @throws DocumentException if the number of widths is different than the number
+ * of columns
+ */
+ public void setWidths(float relativeWidths[]) throws DocumentException {
+ if (relativeWidths.length != this.relativeWidths.length)
+ throw new DocumentException("Wrong number of columns.");
+ this.relativeWidths = new float[relativeWidths.length];
+ System.arraycopy(relativeWidths, 0, this.relativeWidths, 0, relativeWidths.length);
+ absoluteWidths = new float[relativeWidths.length];
+ totalHeight = 0;
+ calculateWidths();
+ calculateHeights();
+ }
+
+ /** Sets the relative widths of the table.
+ * @param relativeWidths the relative widths of the table.
+ * @throws DocumentException if the number of widths is different than the number
+ * of columns
+ */
+ public void setWidths(int relativeWidths[]) throws DocumentException {
+ float tb[] = new float[relativeWidths.length];
+ for (int k = 0; k < relativeWidths.length; ++k)
+ tb[k] = relativeWidths[k];
+ setWidths(tb);
+ }
+
+ private void calculateWidths() {
+ if (totalWidth <= 0)
+ return;
+ float total = 0;
+ for (int k = 0; k < absoluteWidths.length; ++k) {
+ total += relativeWidths[k];
+ }
+ for (int k = 0; k < absoluteWidths.length; ++k) {
+ absoluteWidths[k] = totalWidth * relativeWidths[k] / total;
+ }
+ }
+
+ /** Sets the full width of the table.
+ * @param totalWidth the full width of the table.
+ */
+ public void setTotalWidth(float totalWidth) {
+ if (this.totalWidth == totalWidth)
+ return;
+ this.totalWidth = totalWidth;
+ totalHeight = 0;
+ calculateWidths();
+ calculateHeights();
+ }
+
+ /** Sets the full width of the table from the absolute column width.
+ * @param columnWidth the absolute width of each column
+ * @throws DocumentException if the number of widths is different than the number
+ * of columns
+ */
+ public void setTotalWidth(float columnWidth[]) throws DocumentException {
+ if (columnWidth.length != this.relativeWidths.length)
+ throw new DocumentException("Wrong number of columns.");
+ totalWidth = 0;
+ for (int k = 0; k < columnWidth.length; ++k)
+ totalWidth += columnWidth[k];
+ setWidths(columnWidth);
+ }
+
+ /** Sets the percentage width of the table from the absolute column width.
+ * @param columnWidth the absolute width of each column
+ * @param pageSize the page size
+ * @throws DocumentException
+ */
+ public void setWidthPercentage(float columnWidth[], Rectangle pageSize) throws DocumentException {
+ if (columnWidth.length != this.relativeWidths.length)
+ throw new IllegalArgumentException("Wrong number of columns.");
+ float totalWidth = 0;
+ for (int k = 0; k < columnWidth.length; ++k)
+ totalWidth += columnWidth[k];
+ widthPercentage = totalWidth / (pageSize.right() - pageSize.left()) * 100f;
+ setWidths(columnWidth);
+ }
+
+ /** Gets the full width of the table.
+ * @return the full width of the table
+ */
+ public float getTotalWidth() {
+ return totalWidth;
+ }
+
+ void calculateHeights() {
+ if (totalWidth <= 0)
+ return;
+ totalHeight = 0;
+ for (int k = 0; k < rows.size(); ++k) {
+ PdfPRow row = (PdfPRow)rows.get(k);
+ if (row != null) {
+ row.setWidths(absoluteWidths);
+ totalHeight += row.getMaxHeights();
+ }
+ }
+ }
+
+ /**
+ * Calculates the heights of the table.
+ */
+ public void calculateHeightsFast() {
+ if (totalWidth <= 0)
+ return;
+ totalHeight = 0;
+ for (int k = 0; k < rows.size(); ++k) {
+ PdfPRow row = (PdfPRow)rows.get(k);
+ if (row != null)
+ totalHeight += row.getMaxHeights();
+ }
+ }
+
+ /** Gets the default PdfPCell
that will be used as
+ * reference for all the addCell
methods except
+ * addCell(PdfPCell)
.
+ * @return default PdfPCell
+ */
+ public PdfPCell getDefaultCell() {
+ return defaultCell;
+ }
+
+ /** Adds a cell element.
+ * @param cell the cell element
+ */
+ public void addCell(PdfPCell cell) {
+ PdfPCell ncell = new PdfPCell(cell);
+ int colspan = ncell.getColspan();
+ colspan = Math.max(colspan, 1);
+ colspan = Math.min(colspan, currentRow.length - currentRowIdx);
+ ncell.setColspan(colspan);
+ if (colspan != 1)
+ isColspan = true;
+ int rdir = ncell.getRunDirection();
+ if (rdir == PdfWriter.RUN_DIRECTION_DEFAULT)
+ ncell.setRunDirection(runDirection);
+ currentRow[currentRowIdx] = ncell;
+ currentRowIdx += colspan;
+ if (currentRowIdx >= currentRow.length) {
+ if (runDirection == PdfWriter.RUN_DIRECTION_RTL) {
+ PdfPCell rtlRow[] = new PdfPCell[absoluteWidths.length];
+ int rev = currentRow.length;
+ for (int k = 0; k < currentRow.length; ++k) {
+ PdfPCell rcell = currentRow[k];
+ int cspan = rcell.getColspan();
+ rev -= cspan;
+ rtlRow[rev] = rcell;
+ k += cspan - 1;
+ }
+ currentRow = rtlRow;
+ }
+ PdfPRow row = new PdfPRow(currentRow);
+ if (totalWidth > 0) {
+ row.setWidths(absoluteWidths);
+ totalHeight += row.getMaxHeights();
+ }
+ rows.add(row);
+ currentRow = new PdfPCell[absoluteWidths.length];
+ currentRowIdx = 0;
+ }
+ }
+
+ /** Adds a cell element.
+ * @param text the text for the cell
+ */
+ public void addCell(String text) {
+ addCell(new Phrase(text));
+ }
+
+ /**
+ * Adds a nested table.
+ * @param table the table to be added to the cell
+ */
+ public void addCell(PdfPTable table) {
+ defaultCell.setTable(table);
+ addCell(defaultCell);
+ defaultCell.setTable(null);
+ }
+
+ /**
+ * Adds an Image as Cell.
+ * @param image the Image
to add to the table. This image will fit in the cell
+ */
+ public void addCell(Image image) {
+ defaultCell.setImage(image);
+ addCell(defaultCell);
+ defaultCell.setImage(null);
+ }
+
+ /**
+ * Adds a cell element.
+ * @param phrase the Phrase
to be added to the cell
+ */
+ public void addCell(Phrase phrase) {
+ defaultCell.setPhrase(phrase);
+ addCell(defaultCell);
+ defaultCell.setPhrase(null);
+ }
+
+ /**
+ * Writes the selected rows to the document.
+ * canvases
is obtained from beginWritingRows()
.
+ * @param rowStart the first row to be written, zero index
+ * @param rowEnd the last row to be written + 1. If it is -1 all the
+ * rows to the end are written
+ * @param xPos the x write coodinate
+ * @param yPos the y write coodinate
+ * @param canvases an array of 4 PdfContentByte
obtained from
+ * beginWrittingRows()
+ * @return the y coordinate position of the bottom of the last row
+ * @see #beginWritingRows(com.lowagie.text.pdf.PdfContentByte)
+ */
+ public float writeSelectedRows(int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte[] canvases) {
+ return writeSelectedRows(0, -1, rowStart, rowEnd, xPos, yPos, canvases);
+ }
+
+ /** Writes the selected rows and columns to the document.
+ * This method does not clip the columns; this is only important
+ * if there are columns with colspan at boundaries.
+ * canvases
is obtained from beginWritingRows()
.
+ * PdfContentByte
obtained from
+ * beginWrittingRows()
+ * @return the y coordinate position of the bottom of the last row
+ * @see #beginWritingRows(com.lowagie.text.pdf.PdfContentByte)
+ */
+ public float writeSelectedRows(int colStart, int colEnd, int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte[] canvases) {
+ if (totalWidth <= 0)
+ throw new RuntimeException("The table width must be greater than zero.");
+ int size = rows.size();
+ if (rowEnd < 0)
+ rowEnd = size;
+ rowEnd = Math.min(rowEnd, size);
+ if (rowStart < 0)
+ rowStart = 0;
+ if (rowStart >= rowEnd)
+ return yPos;
+ if (colEnd < 0)
+ colEnd = absoluteWidths.length;
+ colEnd = Math.min(colEnd, absoluteWidths.length);
+ if (colStart < 0)
+ colStart = 0;
+ colStart = Math.min(colStart, absoluteWidths.length);
+ float yPosStart = yPos;
+ for (int k = rowStart; k < rowEnd; ++k) {
+ PdfPRow row = (PdfPRow)rows.get(k);
+ if (row != null) {
+ row.writeCells(colStart, colEnd, xPos, yPos, canvases);
+ yPos -= row.getMaxHeights();
+ }
+ }
+ if (tableEvent != null && colStart == 0 && colEnd == absoluteWidths.length) {
+ float heights[] = new float[rowEnd - rowStart + 1];
+ heights[0] = yPosStart;
+ for (int k = rowStart; k < rowEnd; ++k) {
+ PdfPRow row = (PdfPRow)rows.get(k);
+ float hr = 0;
+ if (row != null)
+ hr = row.getMaxHeights();
+ heights[k - rowStart + 1] = heights[k - rowStart] - hr;
+ }
+ tableEvent.tableLayout(this, getEventWidths(xPos, rowStart, rowEnd, headersInEvent), heights, headersInEvent ? headerRows : 0, rowStart, canvases);
+ }
+ return yPos;
+ }
+
+ /**
+ * Writes the selected rows to the document.
+ *
+ * @param rowStart the first row to be written, zero index
+ * @param rowEnd the last row to be written + 1. If it is -1 all the
+ * rows to the end are written
+ * @param xPos the x write coodinate
+ * @param yPos the y write coodinate
+ * @param canvas the PdfContentByte
where the rows will
+ * be written to
+ * @return the y coordinate position of the bottom of the last row
+ */
+ public float writeSelectedRows(int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte canvas) {
+ return writeSelectedRows(0, -1, rowStart, rowEnd, xPos, yPos, canvas);
+ }
+
+ /**
+ * Writes the selected rows to the document.
+ * This method clips the columns; this is only important
+ * if there are columns with colspan at boundaries.
+ * PdfContentByte
where the rows will
+ * be written to
+ * @return the y coordinate position of the bottom of the last row
+ */
+ public float writeSelectedRows(int colStart, int colEnd, int rowStart, int rowEnd, float xPos, float yPos, PdfContentByte canvas) {
+ if (colEnd < 0)
+ colEnd = absoluteWidths.length;
+ colEnd = Math.min(colEnd, absoluteWidths.length);
+ if (colStart < 0)
+ colStart = 0;
+ colStart = Math.min(colStart, absoluteWidths.length);
+ if (colStart != 0 || colEnd != absoluteWidths.length) {
+ float w = 0;
+ for (int k = colStart; k < colEnd; ++k)
+ w += absoluteWidths[k];
+ canvas.saveState();
+ float lx = 0;
+ float rx = 0;
+ if (colStart == 0)
+ lx = 10000;
+ if (colEnd == absoluteWidths.length)
+ rx = 10000;
+ canvas.rectangle(xPos - lx, -10000, w + lx + rx, 20000);
+ canvas.clip();
+ canvas.newPath();
+ }
+ PdfContentByte[] canvases = beginWritingRows(canvas);
+ float y = writeSelectedRows(colStart, colEnd, rowStart, rowEnd, xPos, yPos, canvases);
+ endWritingRows(canvases);
+ if (colStart != 0 || colEnd != absoluteWidths.length)
+ canvas.restoreState();
+ return y;
+ }
+
+ /** Gets and initializes the 4 layers where the table is written to. The text or graphics are added to
+ * one of the 4 PdfContentByte
returned with the following order:
+ *
PdfPtable.BASECANVAS
- the original PdfContentByte
. Anything placed here
+ * will be under the table.
+ * PdfPtable.BACKGROUNDCANVAS
- the layer where the background goes to.
+ * PdfPtable.LINECANVAS
- the layer where the lines go to.
+ * PdfPtable.TEXTCANVAS
- the layer where the text go to. Anything placed here
+ * will be over the table.
+ * PdfContentByte
where the rows will
+ * be written to
+ * @return an array of 4 PdfContentByte
+ * @see #writeSelectedRows(int, int, float, float, PdfContentByte[])
+ */
+ public static PdfContentByte[] beginWritingRows(PdfContentByte canvas) {
+ return new PdfContentByte[]{
+ canvas,
+ canvas.getDuplicate(),
+ canvas.getDuplicate(),
+ canvas.getDuplicate(),
+ };
+ }
+
+ /** Finishes writing the table.
+ * @param canvases the array returned by beginWritingRows()
+ */
+ public static void endWritingRows(PdfContentByte[] canvases) {
+ PdfContentByte canvas = canvases[BASECANVAS];
+ canvas.saveState();
+ canvas.add(canvases[BACKGROUNDCANVAS]);
+ canvas.restoreState();
+ canvas.saveState();
+ canvas.setLineCap(2);
+ canvas.resetRGBColorStroke();
+ canvas.add(canvases[LINECANVAS]);
+ canvas.restoreState();
+ canvas.add(canvases[TEXTCANVAS]);
+ }
+
+ /** Gets the number of rows in this table.
+ * @return the number of rows in this table
+ */
+ public int size() {
+ return rows.size();
+ }
+
+ /** Gets the total height of the table.
+ * @return the total height of the table
+ */
+ public float getTotalHeight() {
+ return totalHeight;
+ }
+
+ /** Gets the height of a particular row.
+ * @param idx the row index (starts at 0)
+ * @return the height of a particular row
+ */
+ public float getRowHeight(int idx) {
+ if (totalWidth <= 0 || idx < 0 || idx >= rows.size())
+ return 0;
+ PdfPRow row = (PdfPRow)rows.get(idx);
+ if (row == null)
+ return 0;
+ return row.getMaxHeights();
+ }
+
+ /** Gets the height of the rows that constitute the header as defined by
+ * setHeaderRows()
.
+ * @return the height of the rows that constitute the header
+ */
+ public float getHeaderHeight() {
+ float total = 0;
+ int size = Math.min(rows.size(), headerRows);
+ for (int k = 0; k < size; ++k) {
+ PdfPRow row = (PdfPRow)rows.get(k);
+ if (row != null)
+ total += row.getMaxHeights();
+ }
+ return total;
+ }
+
+ /** Deletes a row from the table.
+ * @param rowNumber the row to be deleted
+ * @return true
if the row was deleted
+ */
+ public boolean deleteRow(int rowNumber) {
+ if (rowNumber < 0 || rowNumber >= rows.size()) {
+ return false;
+ }
+ if (totalWidth > 0) {
+ PdfPRow row = (PdfPRow)rows.get(rowNumber);
+ if (row != null)
+ totalHeight -= row.getMaxHeights();
+ }
+ rows.remove(rowNumber);
+ return true;
+ }
+
+ /** Deletes the last row in the table.
+ * @return true
if the last row was deleted
+ */
+ public boolean deleteLastRow() {
+ return deleteRow(rows.size() - 1);
+ }
+
+ /**
+ * Removes all of the rows except headers
+ */
+ public void deleteBodyRows() {
+ ArrayList rows2 = new ArrayList();
+ for (int k = 0; k < headerRows; ++k)
+ rows2.add(rows.get(k));
+ rows = rows2;
+ totalHeight = 0;
+ if (totalWidth > 0)
+ totalHeight = getHeaderHeight();
+ }
+
+ /** Gets the number of the rows that constitute the header.
+ * @return the number of the rows that constitute the header
+ */
+ public int getHeaderRows() {
+ return headerRows;
+ }
+
+ /** Sets the number of the top rows that constitute the header.
+ * This header has only meaning if the table is added to Document
+ * and the table crosses pages.
+ * @param headerRows the number of the top rows that constitute the header
+ */
+ public void setHeaderRows(int headerRows) {
+ if (headerRows < 0)
+ headerRows = 0;
+ this.headerRows = headerRows;
+ }
+
+ /**
+ * Gets all the chunks in this element.
+ *
+ * @return an ArrayList
+ */
+ public ArrayList getChunks() {
+ return new ArrayList();
+ }
+
+ /**
+ * Gets the type of the text element.
+ *
+ * @return a type
+ */
+ public int type() {
+ return Element.PTABLE;
+ }
+
+ /**
+ * Processes the element by adding it (or the different parts) to an
+ * ElementListener
.
+ *
+ * @param listener an ElementListener
+ * @return true
if the element was processed successfully
+ */
+ public boolean process(ElementListener listener) {
+ try {
+ return listener.add(this);
+ }
+ catch(DocumentException de) {
+ return false;
+ }
+ }
+
+ /** Gets the width percentage that the table will occupy in the page.
+ * @return the width percentage that the table will occupy in the page
+ */
+ public float getWidthPercentage() {
+ return widthPercentage;
+ }
+
+ /** Sets the width percentage that the table will occupy in the page.
+ * @param widthPercentage the width percentage that the table will occupy in the page
+ */
+ public void setWidthPercentage(float widthPercentage) {
+ this.widthPercentage = widthPercentage;
+ }
+
+ /** Gets the horizontal alignment of the table relative to the page.
+ * @return the horizontal alignment of the table relative to the page
+ */
+ public int getHorizontalAlignment() {
+ return horizontalAlignment;
+ }
+
+ /** Sets the horizontal alignment of the table relative to the page.
+ * It only has meaning if the width percentage is less than
+ * 100%.
+ * @param horizontalAlignment the horizontal alignment of the table relative to the page
+ */
+ public void setHorizontalAlignment(int horizontalAlignment) {
+ this.horizontalAlignment = horizontalAlignment;
+ }
+
+ /**
+ * Gets a row with a given index
+ * (added by Jin-Hsia Yang).
+ * @param idx
+ * @return the row at position idx
+ */
+ public PdfPRow getRow(int idx) {
+ return (PdfPRow)rows.get(idx);
+ }
+
+ /**
+ * Gets an arraylist with all the rows in the table.
+ * @return an arraylist
+ */
+ public ArrayList getRows() {
+ return rows;
+ }
+
+ /** Sets the table event for this table.
+ * @param event the table event for this table
+ */
+ public void setTableEvent(PdfPTableEvent event) {
+ if (event == null) this.tableEvent = null;
+ else if (this.tableEvent == null) this.tableEvent = event;
+ else if (this.tableEvent instanceof PdfPTableEventForwarder) ((PdfPTableEventForwarder)this.tableEvent).addTableEvent(event);
+ else {
+ PdfPTableEventForwarder forward = new PdfPTableEventForwarder();
+ forward.addTableEvent(this.tableEvent);
+ forward.addTableEvent(event);
+ this.tableEvent = forward;
+ }
+ }
+
+ /** Gets the table event for this page.
+ * @return the table event for this page
+ */
+ public PdfPTableEvent getTableEvent() {
+ return tableEvent;
+ }
+
+ /** Gets the absolute sizes of each column width.
+ * @return he absolute sizes of each column width
+ */
+ public float[] getAbsoluteWidths() {
+ return absoluteWidths;
+ }
+
+ float [][] getEventWidths(float xPos, int firstRow, int lastRow, boolean includeHeaders) {
+ if (includeHeaders) {
+ firstRow = Math.max(firstRow, headerRows);
+ lastRow = Math.max(lastRow, headerRows);
+ }
+ float widths[][] = new float[(includeHeaders ? headerRows : 0) + lastRow - firstRow][];
+ if (isColspan) {
+ int n = 0;
+ if (includeHeaders) {
+ for (int k = 0; k < headerRows; ++k) {
+ PdfPRow row = (PdfPRow)rows.get(k);
+ if (row == null)
+ ++n;
+ else
+ widths[n++] = row.getEventWidth(xPos);
+ }
+ }
+ for (; firstRow < lastRow; ++firstRow) {
+ PdfPRow row = (PdfPRow)rows.get(firstRow);
+ if (row == null)
+ ++n;
+ else
+ widths[n++] = row.getEventWidth(xPos);
+ }
+ }
+ else {
+ float width[] = new float[absoluteWidths.length + 1];
+ width[0] = xPos;
+ for (int k = 0; k < absoluteWidths.length; ++k)
+ width[k + 1] = width[k] + absoluteWidths[k];
+ for (int k = 0; k < widths.length; ++k)
+ widths[k] = width;
+ }
+ return widths;
+ }
+
+
+ /** Getter for property skipFirstHeader.
+ * @return Value of property skipFirstHeader.
+ */
+ public boolean isSkipFirstHeader() {
+ return skipFirstHeader;
+ }
+
+ /** Skips the printing of the first header. Used when printing
+ * tables in succession belonging to the same printed table aspect.
+ * @param skipFirstHeader New value of property skipFirstHeader.
+ */
+ public void setSkipFirstHeader(boolean skipFirstHeader) {
+ this.skipFirstHeader = skipFirstHeader;
+ }
+
+ /**
+ * Sets the run direction of the contents of the table.
+ * @param runDirection
+ */
+ public void setRunDirection(int runDirection) {
+ if (runDirection < PdfWriter.RUN_DIRECTION_DEFAULT || runDirection > PdfWriter.RUN_DIRECTION_RTL)
+ throw new RuntimeException("Invalid run direction: " + runDirection);
+ this.runDirection = runDirection;
+ }
+
+ /**
+ * Returns the run direction of the contents in the table.
+ * @return One of the following values: PdfWriter.RUN_DIRECTION_DEFAULT, PdfWriter.RUN_DIRECTION_NO_BIDI, PdfWriter.RUN_DIRECTION_LTR or PdfWriter.RUN_DIRECTION_RTL.
+ */
+ public int getRunDirection() {
+ return runDirection;
+ }
+
+ /**
+ * Getter for property lockedWidth.
+ * @return Value of property lockedWidth.
+ */
+ public boolean isLockedWidth() {
+ return this.lockedWidth;
+ }
+
+ /**
+ * Uses the value in setTotalWidth()
in Document.add()
.
+ * @param lockedWidth true
to use the value in setTotalWidth()
in Document.add()
+ */
+ public void setLockedWidth(boolean lockedWidth) {
+ this.lockedWidth = lockedWidth;
+ }
+
+ /**
+ * Gets the split value.
+ * @return true to split; false otherwise
+ */
+ public boolean isSplitRows() {
+ return this.splitRows;
+ }
+
+ /**
+ * When set the rows that won't fit in the page will be split.
+ * Note that it takes at least twice the memory to handle a split table row
+ * than a normal table. true
by default.
+ * @param splitRows true to split; false otherwise
+ */
+ public void setSplitRows(boolean splitRows) {
+ this.splitRows = splitRows;
+ }
+
+/**
+ * Sets the spacing before this table.
+ *
+ * @param spacing the new spacing
+ */
+
+ public void setSpacingBefore(float spacing) {
+ this.spacingBefore = spacing;
+ }
+
+/**
+ * Sets the spacing after this table.
+ *
+ * @param spacing the new spacing
+ */
+
+ public void setSpacingAfter(float spacing) {
+ this.spacingAfter = spacing;
+ }
+
+/**
+ * Gets the spacing before this table.
+ *
+ * @return the spacing
+ */
+
+ public float spacingBefore() {
+ return spacingBefore;
+ }
+
+/**
+ * Gets the spacing before this table.
+ *
+ * @return the spacing
+ */
+
+ public float spacingAfter() {
+ return spacingAfter;
+ }
+
+ /**
+ * Gets the value of the last row extension.
+ * @return true if the last row will extend; false otherwise
+ */
+ public boolean isExtendLastRow() {
+ return this.extendLastRow;
+ }
+
+ /**
+ * When set the last row will be extended to fill all the remaining space to the
+ * bottom boundary.
+ * @param extendLastRow true to extend the last row; false otherwise
+ */
+ public void setExtendLastRow(boolean extendLastRow) {
+ this.extendLastRow = extendLastRow;
+ }
+
+ /**
+ * Gets the header status inclusion in PdfPTableEvent.
+ * @return true if the headers are included; false otherwise
+ */
+ public boolean isHeadersInEvent() {
+ return this.headersInEvent;
+ }
+
+ /**
+ * When set the PdfPTableEvent will include the headers.
+ * @param headersInEvent true to include the headers; false otherwise
+ */
+ public void setHeadersInEvent(boolean headersInEvent) {
+ this.headersInEvent = headersInEvent;
+ }
+
+ /**
+ * Gets the property splitLate.
+ * @return the property splitLate
+ */
+ public boolean isSplitLate() {
+ return this.splitLate;
+ }
+
+ /**
+ * If true the row will only split if it's the first one in an empty page.
+ * It's true by default.
+ *
+ * table.setHeaderRows(3);
+ * table.setFooterRows(1);
+ *
+ * PdfPTable
.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public interface PdfPTableEvent {
+
+ /** This method is called at the end of the table rendering. The text or graphics are added to
+ * one of the 4 PdfContentByte
contained in
+ * canvases
.
+ * The indexes to canvases
are:
+ *
+ * The layers are placed in sequence on top of each other.
+ * PdfPTable.BASECANVAS
- the original PdfContentByte
. Anything placed here
+ * will be under the table.
+ * PdfPTable.BACKGROUNDCANVAS
- the layer where the background goes to.
+ * PdfPTable.LINECANVAS
- the layer where the lines go to.
+ * PdfPTable.TEXTCANVAS
- the layer where the text go to. Anything placed here
+ * will be over the table.
+ * widths
and heights
have the coordinates of the cells.
+ * The size of the widths
array is the number of rows.
+ * Each sub-array in widths
corresponds to the x column border positions where
+ * the first element is the x coordinate of the left table border and the last
+ * element is the x coordinate of the right table border.
+ * If colspan is not used all the sub-arrays in widths
+ * are the same.
+ * For the heights
the first element is the y coordinate of the top table border and the last
+ * element is the y coordinate of the bottom table border.
+ * @param table the PdfPTable
in use
+ * @param widths an array of arrays with the cells' x positions. It has the length of the number
+ * of rows
+ * @param heights an array with the cells' y positions. It has a length of the number
+ * of rows + 1
+ * @param headerRows the number of rows defined for the header.
+ * @param rowStart the first row number after the header
+ * @param canvases an array of PdfContentByte
+ */
+ public void tableLayout(PdfPTable table, float widths[][], float heights[], int headerRows, int rowStart, PdfContentByte[] canvases);
+
+}
+
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPage.java b/src/main/java/com/lowagie/text/pdf/PdfPage.java
new file mode 100644
index 0000000..80e5472
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPage.java
@@ -0,0 +1,197 @@
+/*
+ * $Id: PdfPage.java,v 1.48 2006/02/16 16:17:52 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import java.util.HashMap;
+/**
+ * PdfPage
is the PDF Page-object.
+ *
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 6.4 (page 73-81)
+ *
+ * @see PdfPages
+ */
+
+public class PdfPage extends PdfDictionary {
+
+ private static final String boxStrings[] = {"crop", "trim", "art", "bleed"};
+ private static final PdfName boxNames[] = {PdfName.CROPBOX, PdfName.TRIMBOX, PdfName.ARTBOX, PdfName.BLEEDBOX};
+ // membervariables
+
+/** value of the Rotate key for a page in PORTRAIT */
+ public static final PdfNumber PORTRAIT = new PdfNumber(0);
+
+/** value of the Rotate key for a page in LANDSCAPE */
+ public static final PdfNumber LANDSCAPE = new PdfNumber(90);
+
+/** value of the Rotate key for a page in INVERTEDPORTRAIT */
+ public static final PdfNumber INVERTEDPORTRAIT = new PdfNumber(180);
+
+/** value of the Rotate key for a page in SEASCAPE */
+ public static final PdfNumber SEASCAPE = new PdfNumber(270);
+
+/** value of the MediaBox key */
+ PdfRectangle mediaBox;
+
+ // constructors
+
+/**
+ * Constructs a PdfPage
.
+ *
+ * @param mediaBox a value for the MediaBox key
+ * @param resources an indirect reference to a PdfResources
-object
+ * @param rotate a value for the Rotate key
+ */
+
+// PdfPage(PdfRectangle mediaBox, Rectangle cropBox, PdfIndirectReference resources, PdfNumber rotate) {
+// super(PAGE);
+// this.mediaBox = mediaBox;
+// put(PdfName.MEDIABOX, mediaBox);
+// put(PdfName.RESOURCES, resources);
+// if (rotate != null) {
+// put(PdfName.ROTATE, rotate);
+// }
+// if (cropBox != null)
+// put(PdfName.CROPBOX, new PdfRectangle(cropBox));
+// }
+
+/**
+ * Constructs a PdfPage
.
+ *
+ * @param mediaBox a value for the MediaBox key
+ * @param resources an indirect reference to a PdfResources
-object
+ * @param rotate a value for the Rotate key
+ */
+
+ PdfPage(PdfRectangle mediaBox, HashMap boxSize, PdfDictionary resources, int rotate) {
+ super(PAGE);
+ this.mediaBox = mediaBox;
+ put(PdfName.MEDIABOX, mediaBox);
+ put(PdfName.RESOURCES, resources);
+ if (rotate != 0) {
+ put(PdfName.ROTATE, new PdfNumber(rotate));
+ }
+ for (int k = 0; k < boxStrings.length; ++k) {
+ PdfObject rect = (PdfObject)boxSize.get(boxStrings[k]);
+ if (rect != null)
+ put(boxNames[k], rect);
+ }
+ }
+
+/**
+ * Constructs a PdfPage
.
+ *
+ * @param mediaBox a value for the MediaBox key
+ * @param resources an indirect reference to a PdfResources
-object
+ */
+
+// PdfPage(PdfRectangle mediaBox, Rectangle cropBox, PdfIndirectReference resources) {
+// this(mediaBox, cropBox, resources, null);
+// }
+
+/**
+ * Constructs a PdfPage
.
+ *
+ * @param mediaBox a value for the MediaBox key
+ * @param resources an indirect reference to a PdfResources
-object
+ */
+
+ PdfPage(PdfRectangle mediaBox, HashMap boxSize, PdfDictionary resources) {
+ this(mediaBox, boxSize, resources, 0);
+ }
+
+/**
+ * Checks if this page element is a tree of pages.
+ * false
.
+ *
+ * @return false
because this is a single page
+ */
+
+ public boolean isParent() {
+ return false;
+ }
+
+ // methods
+
+/**
+ * Adds an indirect reference pointing to a PdfContents
-object.
+ *
+ * @param contents an indirect reference to a PdfContents
-object
+ */
+
+ void add(PdfIndirectReference contents) {
+ put(PdfName.CONTENTS, contents);
+ }
+
+/**
+ * Rotates the mediabox, but not the text in it.
+ *
+ * @return a PdfRectangle
+ */
+
+ PdfRectangle rotateMediaBox() {
+ this.mediaBox = mediaBox.rotate();
+ put(PdfName.MEDIABOX, this.mediaBox);
+ return this.mediaBox;
+ }
+
+/**
+ * Returns the MediaBox of this Page.
+ *
+ * @return a PdfRectangle
+ */
+
+ PdfRectangle getMediaBox() {
+ return mediaBox;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPageElement.java b/src/main/java/com/lowagie/text/pdf/PdfPageElement.java
new file mode 100644
index 0000000..86edcae
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPageElement.java
@@ -0,0 +1,78 @@
+/*
+ * $Id: PdfPageElement.java,v 1.56 2005/05/04 14:33:11 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * The PdfPageElement
interface has to be implemented by PdfPage
and PdfPages
.
+ *
+ * @see PdfPage
+ * @see PdfPages
+ */
+
+interface PdfPageElement {
+
+/**
+ * Set the value for the Parent key in the Page or Pages Dictionary.
+ *
+ * @param reference an indirect reference to a PdfPages
-object
+ */
+
+ public void setParent(PdfIndirectReference reference);
+
+/**
+ * Checks if this page element is a tree of pages.
+ *
+ * @return true
if it's a tree of pages;
+ * false
if it's a single page
+ */
+
+ public boolean isParent();
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPageEvent.java b/src/main/java/com/lowagie/text/pdf/PdfPageEvent.java
new file mode 100644
index 0000000..5ac89c8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPageEvent.java
@@ -0,0 +1,190 @@
+/*
+ * $Id: PdfPageEvent.java,v 1.56 2005/05/04 14:31:45 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import com.lowagie.text.Document;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Paragraph;
+
+/**
+ * Allows a class to catch several document events.
+ *PdfWriter
for this document
+ * @param document the document
+ */
+ public void onOpenDocument(PdfWriter writer, Document document);
+
+/**
+ * Called when a page is initialized.
+ * onEndPage
to avoid
+ * infinite loops.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ */
+ public void onStartPage(PdfWriter writer, Document document);
+
+/**
+ * Called when a page is finished, just before being written to the document.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ */
+ public void onEndPage(PdfWriter writer, Document document);
+
+/**
+ * Called when the document is closed.
+ * PdfWriter
for this document
+ * @param document the document
+ */
+ public void onCloseDocument(PdfWriter writer, Document document);
+
+/**
+ * Called when a Paragraph is written.
+ * paragraphPosition
will hold the height at which the
+ * paragraph will be written to. This is useful to insert bookmarks with
+ * more control.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param paragraphPosition the position the paragraph will be written to
+ */
+ public void onParagraph(PdfWriter writer, Document document, float paragraphPosition);
+
+/**
+ * Called when a Paragraph is written.
+ * paragraphPosition
will hold the height of the end of the paragraph.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param paragraphPosition the position of the end of the paragraph
+ */
+ public void onParagraphEnd(PdfWriter writer,Document document,float paragraphPosition);
+
+/**
+ * Called when a Chapter is written.
+ * position
will hold the height at which the
+ * chapter will be written to.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param paragraphPosition the position the chapter will be written to
+ * @param title the title of the Chapter
+ */
+ public void onChapter(PdfWriter writer,Document document,float paragraphPosition, Paragraph title);
+
+/**
+ * Called when the end of a Chapter is reached.
+ * position
will hold the height of the end of the chapter.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param paragraphPosition the position the chapter will be written to
+ */
+ public void onChapterEnd(PdfWriter writer,Document document,float paragraphPosition);
+
+/**
+ * Called when a Section is written.
+ * position
will hold the height at which the
+ * section will be written to.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param paragraphPosition the position the section will be written to
+ * @param depth the number depth of the section
+ * @param title the title of the section
+ */
+ public void onSection(PdfWriter writer,Document document,float paragraphPosition, int depth, Paragraph title);
+
+/**
+ * Called when the end of a Section is reached.
+ * position
will hold the height of the section end.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param paragraphPosition the position the section will be written to
+ */
+ public void onSectionEnd(PdfWriter writer,Document document,float paragraphPosition);
+
+/**
+ * Called when a Chunk
with a generic tag is written.
+ * Chunk
location to generate
+ * bookmarks, for example.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param rect the Rectangle
containing the Chunk
+ * @param text the text of the tag
+ */
+ public void onGenericTag(PdfWriter writer, Document document, Rectangle rect, String text);
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPageEventHelper.java b/src/main/java/com/lowagie/text/pdf/PdfPageEventHelper.java
new file mode 100644
index 0000000..e1921ed
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPageEventHelper.java
@@ -0,0 +1,202 @@
+/*
+ * $Id: PdfPageEventHelper.java,v 1.56 2005/05/04 14:31:45 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import com.lowagie.text.Document;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Paragraph;
+
+/**
+ * Helps the use of PdfPageEvent
by implementing all the interface methods.
+ * A class can extend PdfPageEventHelper
and only implement the
+ * needed methods.
+ *PdfWriter
for this document
+ * @param document the document
+ */
+ public void onOpenDocument(PdfWriter writer,Document document) {
+ }
+
+/**
+ * Called when a page is initialized.
+ * onEndPage
to avoid
+ * infinite loops.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ */
+ public void onStartPage(PdfWriter writer,Document document) {
+ }
+
+/**
+ * Called when a page is finished, just before being written to the document.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ */
+ public void onEndPage(PdfWriter writer,Document document) {
+ }
+
+/**
+ * Called when the document is closed.
+ * PdfWriter
for this document
+ * @param document the document
+ */
+ public void onCloseDocument(PdfWriter writer,Document document) {
+ }
+
+/**
+ * Called when a Paragraph is written.
+ * paragraphPosition
will hold the height at which the
+ * paragraph will be written to. This is useful to insert bookmarks with
+ * more control.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param paragraphPosition the position the paragraph will be written to
+ */
+ public void onParagraph(PdfWriter writer,Document document,float paragraphPosition) {
+ }
+
+/**
+ * Called when a Paragraph is written.
+ * paragraphPosition
will hold the height of the end of the paragraph.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param paragraphPosition the position of the end of the paragraph
+ */
+ public void onParagraphEnd(PdfWriter writer,Document document,float paragraphPosition) {
+ }
+
+/**
+ * Called when a Chapter is written.
+ * position
will hold the height at which the
+ * chapter will be written to.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param paragraphPosition the position the chapter will be written to
+ * @param title the title of the Chapter
+ */
+ public void onChapter(PdfWriter writer,Document document,float paragraphPosition,Paragraph title) {
+ }
+
+/**
+ * Called when the end of a Chapter is reached.
+ * position
will hold the height of the end of the chapter.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param position the position of the end of the chapter.
+ */
+ public void onChapterEnd(PdfWriter writer,Document document,float position) {
+ }
+
+/**
+ * Called when a Section is written.
+ * position
will hold the height at which the
+ * section will be written to.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param paragraphPosition the position the section will be written to
+ * @param depth the number depth of the Section
+ * @param title the title of the section
+ */
+ public void onSection(PdfWriter writer,Document document,float paragraphPosition,int depth,Paragraph title) {
+ }
+
+/**
+ * Called when the end of a Section is reached.
+ * position
will hold the height of the section end.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param position the position of the end of the section
+ */
+ public void onSectionEnd(PdfWriter writer,Document document,float position) {
+ }
+
+/**
+ * Called when a Chunk
with a generic tag is written.
+ * Chunk
location to generate
+ * bookmarks, for example.
+ *
+ * @param writer the PdfWriter
for this document
+ * @param document the document
+ * @param rect the Rectangle
containing the Chunk
+ * @param text the text of the tag
+ */
+ public void onGenericTag(PdfWriter writer,Document document,Rectangle rect,String text) {
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPageLabels.java b/src/main/java/com/lowagie/text/pdf/PdfPageLabels.java
new file mode 100644
index 0000000..2c3bf28
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPageLabels.java
@@ -0,0 +1,191 @@
+/*
+ * $Id: PdfPageLabels.java,v 1.46 2005/05/04 14:32:31 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.Comparator;
+import java.util.TreeMap;
+import java.util.Iterator;
+
+/** Page labels are used to identify each
+ * page visually on the screen or in print.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfPageLabels implements Comparator {
+
+ /** Logical pages will have the form 1,2,3,...
+ */
+ public static int DECIMAL_ARABIC_NUMERALS = 0;
+ /** Logical pages will have the form I,II,III,IV,...
+ */
+ public static int UPPERCASE_ROMAN_NUMERALS = 1;
+ /** Logical pages will have the form i,ii,iii,iv,...
+ */
+ public static int LOWERCASE_ROMAN_NUMERALS = 2;
+ /** Logical pages will have the form of uppercase letters
+ * (A to Z for the first 26 pages, AA to ZZ for the next 26, and so on)
+ */
+ public static int UPPERCASE_LETTERS = 3;
+ /** Logical pages will have the form of uppercase letters
+ * (a to z for the first 26 pages, aa to zz for the next 26, and so on)
+ */
+ public static int LOWERCASE_LETTERS = 4;
+ /** No logical page numbers are generated but fixed text may
+ * still exist
+ */
+ public static int EMPTY = 5;
+ /** Dictionary values to set the logical page styles
+ */
+ static PdfName numberingStyle[] = new PdfName[]{PdfName.D, PdfName.R,
+ new PdfName("r"), PdfName.A, new PdfName("a")};
+ /** The sequence of logical pages. Will contain at least a value for page 1
+ */
+ TreeMap map;
+
+ /** Creates a new PdfPageLabel with a default logical page 1
+ */
+ public PdfPageLabels() {
+ map = new TreeMap(this);
+ addPageLabel(1, DECIMAL_ARABIC_NUMERALS, null, 1);
+ }
+
+ /** Compares two Integer
.
+ * @param obj the first Integer
+ * @param obj1 the second Integer
+ * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second
+ */
+ public int compare(Object obj, Object obj1) {
+ int v1 = ((Integer)obj).intValue();
+ int v2 = ((Integer)obj1).intValue();
+ if (v1 < v2)
+ return -1;
+ if (v1 == v2)
+ return 0;
+ return 1;
+ }
+
+ /** Not used
+ * @param obj not used
+ * @return always true
+ */
+ public boolean equals(Object obj) {
+ return true;
+ }
+
+ /** Adds or replaces a page label.
+ * @param page the real page to start the numbering. First page is 1
+ * @param numberStyle the numbering style such as LOWERCASE_ROMAN_NUMERALS
+ * @param text the text to prefix the number. Can be null
or empty
+ * @param firstPage the first logical page number
+ */
+ public void addPageLabel(int page, int numberStyle, String text, int firstPage) {
+ if (page < 1 || firstPage < 1)
+ throw new IllegalArgumentException("In a page label the page numbers must be greater or equal to 1.");
+ PdfName pdfName = null;
+ if (numberStyle >= 0 && numberStyle < numberingStyle.length)
+ pdfName = numberingStyle[numberStyle];
+ Integer iPage = new Integer(page);
+ Object obj = new Object[]{iPage, pdfName, text, new Integer(firstPage)};
+ map.put(iPage, obj);
+ }
+
+ /** Adds or replaces a page label. The first logical page has the default
+ * of 1.
+ * @param page the real page to start the numbering. First page is 1
+ * @param numberStyle the numbering style such as LOWERCASE_ROMAN_NUMERALS
+ * @param text the text to prefix the number. Can be null
or empty
+ */
+ public void addPageLabel(int page, int numberStyle, String text) {
+ addPageLabel(page, numberStyle, text, 1);
+ }
+
+ /** Adds or replaces a page label. There is no text prefix and the first
+ * logical page has the default of 1.
+ * @param page the real page to start the numbering. First page is 1
+ * @param numberStyle the numbering style such as LOWERCASE_ROMAN_NUMERALS
+ */
+ public void addPageLabel(int page, int numberStyle) {
+ addPageLabel(page, numberStyle, null, 1);
+ }
+
+ /** Removes a page label. The first page label can not be removed, only changed.
+ * @param page the real page to remove
+ */
+ public void removePageLabel(int page) {
+ if (page <= 1)
+ return;
+ map.remove(new Integer(page));
+ }
+
+ /** Gets the page label dictionary to insert into the document.
+ * @return the page label dictionary
+ */
+ PdfDictionary getDictionary() {
+ PdfDictionary dic = new PdfDictionary();
+ PdfArray array = new PdfArray();
+ for (Iterator it = map.values().iterator(); it.hasNext();) {
+ Object obj[] = (Object[])it.next();
+ PdfDictionary subDic = new PdfDictionary();
+ PdfName pName = (PdfName)obj[1];
+ if (pName != null)
+ subDic.put(PdfName.S, pName);
+ String text = (String)obj[2];
+ if (text != null)
+ subDic.put(PdfName.P, new PdfString(text, PdfObject.TEXT_UNICODE));
+ int st = ((Integer)obj[3]).intValue();
+ if (st != 1)
+ subDic.put(PdfName.ST, new PdfNumber(st));
+ array.add(new PdfNumber(((Integer)obj[0]).intValue() - 1));
+ array.add(subDic);
+ }
+ dic.put(PdfName.NUMS, array);
+ return dic;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPages.java b/src/main/java/com/lowagie/text/pdf/PdfPages.java
new file mode 100644
index 0000000..cdc85a3
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPages.java
@@ -0,0 +1,205 @@
+/*
+ * $Id: PdfPages.java,v 1.58 2006/02/16 16:17:52 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.ExceptionConverter;
+
+/**
+ * PdfPages
is the PDF Pages-object.
+ *
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 6.3 (page 71-73)
+ *
+ * @see PdfPage
+ */
+
+public class PdfPages {
+
+ private ArrayList pages = new ArrayList();
+ private ArrayList parents = new ArrayList();
+ private int leafSize = 10;
+ private PdfWriter writer;
+ private PdfIndirectReference topParent;
+
+ // constructors
+
+/**
+ * Constructs a PdfPages
-object.
+ */
+
+ PdfPages(PdfWriter writer) {
+ this.writer = writer;
+ }
+
+ void addPage(PdfDictionary page) {
+ try {
+ if ((pages.size() % leafSize) == 0)
+ parents.add(writer.getPdfIndirectReference());
+ PdfIndirectReference parent = (PdfIndirectReference)parents.get(parents.size() - 1);
+ page.put(PdfName.PARENT, parent);
+ PdfIndirectReference current = writer.getCurrentPage();
+ writer.addToBody(page, current);
+ pages.add(current);
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ PdfIndirectReference addPageRef(PdfIndirectReference pageRef) {
+ try {
+ if ((pages.size() % leafSize) == 0)
+ parents.add(writer.getPdfIndirectReference());
+ pages.add(pageRef);
+ return (PdfIndirectReference)parents.get(parents.size() - 1);
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ // returns the top parent to include in the catalog
+ PdfIndirectReference writePageTree() throws IOException {
+ if (pages.size() == 0)
+ throw new IOException("The document has no pages.");
+ int leaf = 1;
+ ArrayList tParents = parents;
+ ArrayList tPages = pages;
+ ArrayList nextParents = new ArrayList();
+ while (true) {
+ leaf *= leafSize;
+ int stdCount = leafSize;
+ int rightCount = tPages.size() % leafSize;
+ if (rightCount == 0)
+ rightCount = leafSize;
+ for (int p = 0; p < tParents.size(); ++p) {
+ int count;
+ int thisLeaf = leaf;
+ if (p == tParents.size() - 1) {
+ count = rightCount;
+ thisLeaf = pages.size() % leaf;
+ if (thisLeaf == 0)
+ thisLeaf = leaf;
+ }
+ else
+ count = stdCount;
+ PdfDictionary top = new PdfDictionary(PdfName.PAGES);
+ top.put(PdfName.COUNT, new PdfNumber(thisLeaf));
+ PdfArray kids = new PdfArray();
+ ArrayList internal = kids.getArrayList();
+ internal.addAll(tPages.subList(p * stdCount, p * stdCount + count));
+ top.put(PdfName.KIDS, kids);
+ if (tParents.size() > 1) {
+ if ((p % leafSize) == 0)
+ nextParents.add(writer.getPdfIndirectReference());
+ top.put(PdfName.PARENT, (PdfIndirectReference)nextParents.get(p / leafSize));
+ }
+ writer.addToBody(top, (PdfIndirectReference)tParents.get(p));
+ }
+ if (tParents.size() == 1) {
+ topParent = (PdfIndirectReference)tParents.get(0);
+ return topParent;
+ }
+ tPages = tParents;
+ tParents = nextParents;
+ nextParents = new ArrayList();
+ }
+ }
+
+ PdfIndirectReference getTopParent() {
+ return topParent;
+ }
+
+ void setLinearMode(PdfIndirectReference topParent) {
+ if (parents.size() > 1)
+ throw new RuntimeException("Linear page mode can only be called with a single parent.");
+ if (topParent != null) {
+ this.topParent = topParent;
+ parents.clear();
+ parents.add(topParent);
+ }
+ leafSize = 10000000;
+ }
+
+ void addPage(PdfIndirectReference page) {
+ pages.add(page);
+ }
+
+ int reorderPages(int order[]) throws DocumentException {
+ if (order == null)
+ return pages.size();
+ if (parents.size() > 1)
+ throw new DocumentException("Page reordering requires a single parent in the page tree. Call PdfWriter.setLinearMode() after open.");
+ if (order.length != pages.size())
+ throw new DocumentException("Page reordering requires an array with the same size as the number of pages.");
+ int max = pages.size();
+ boolean temp[] = new boolean[max];
+ for (int k = 0; k < max; ++k) {
+ int p = order[k];
+ if (p < 1 || p > max)
+ throw new DocumentException("Page reordering requires pages between 1 and " + max + ". Found " + p + ".");
+ if (temp[p - 1])
+ throw new DocumentException("Page reordering requires no page repetition. Page " + p + " is repeated.");
+ temp[p - 1] = true;
+ }
+ Object copy[] = pages.toArray();
+ for (int k = 0; k < max; ++k) {
+ pages.set(k, copy[order[k] - 1]);
+ }
+ return max;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPattern.java b/src/main/java/com/lowagie/text/pdf/PdfPattern.java
new file mode 100644
index 0000000..f2aab31
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPattern.java
@@ -0,0 +1,83 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.ExceptionConverter;
+
+/**
+ * A PdfPattern
defines a ColorSpace
+ *
+ * @see PdfStream
+ */
+
+public class PdfPattern extends PdfStream {
+
+ PdfPattern(PdfPatternPainter painter) {
+ super();
+ PdfNumber one = new PdfNumber(1);
+ PdfArray matrix = painter.getMatrix();
+ if ( matrix != null ) {
+ put(PdfName.MATRIX, matrix);
+ }
+ put(PdfName.TYPE, PdfName.PATTERN);
+ put(PdfName.BBOX, new PdfRectangle(painter.getBoundingBox()));
+ put(PdfName.RESOURCES, painter.getResources());
+ put(PdfName.TILINGTYPE, one);
+ put(PdfName.PATTERNTYPE, one);
+ if (painter.isStencil())
+ put(PdfName.PAINTTYPE, new PdfNumber(2));
+ else
+ put(PdfName.PAINTTYPE, one);
+ put(PdfName.XSTEP, new PdfNumber(painter.getXStep()));
+ put(PdfName.YSTEP, new PdfNumber(painter.getYStep()));
+ bytes = painter.toPdf(null);
+ put(PdfName.LENGTH, new PdfNumber(bytes.length));
+ try {
+ flateCompress();
+ } catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPatternPainter.java b/src/main/java/com/lowagie/text/pdf/PdfPatternPainter.java
new file mode 100644
index 0000000..7c5a094
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPatternPainter.java
@@ -0,0 +1,391 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Image;
+import com.lowagie.text.Rectangle;
+import java.awt.Color;
+
+/**
+ * Implements the pattern.
+ */
+
+public class PdfPatternPainter extends PdfTemplate {
+
+ protected float xstep, ystep;
+ protected boolean stencil = false;
+ protected Color defaultColor;
+
+ /**
+ *Creates a PdfPattern
.
+ */
+
+ private PdfPatternPainter() {
+ super(null);
+ type = TYPE_PATTERN;
+ }
+
+ /**
+ * Creates new PdfPattern
+ *
+ * @param wr the PdfWriter
+ */
+
+ PdfPatternPainter(PdfWriter wr) {
+ super(wr);
+ type = TYPE_PATTERN;
+ }
+
+ PdfPatternPainter(PdfWriter wr, Color defaultColor) {
+ this(wr);
+ stencil = true;
+ if (defaultColor == null)
+ this.defaultColor = Color.gray;
+ else
+ this.defaultColor = defaultColor;
+ }
+
+ /**
+ * Sets the horizontal interval of this pattern.
+ *
+ * @param xstep the xstep in horizontal painting
+ */
+
+ public void setXStep(float xstep) {
+ this.xstep = xstep;
+ }
+
+ /**
+ * Sets the vertical interval of this pattern.
+ *
+ * @param ystep in vertical painting
+ */
+
+ public void setYStep(float ystep) {
+ this.ystep = ystep;
+ }
+
+ /**
+ * Returns the horizontal interval when repeating the pattern.
+ * @return a value
+ */
+ public float getXStep() {
+ return this.xstep;
+ }
+
+ /**
+ * Returns the vertical interval when repeating the pattern.
+ * @return a value
+ */
+ public float getYStep() {
+ return this.ystep;
+ }
+
+ /**
+ * Tells you if this pattern is colored/uncolored (stencil = uncolored, you need to set a default color).
+ * @return true if the pattern is an uncolored tiling pattern (stencil).
+ */
+ public boolean isStencil() {
+ return stencil;
+ }
+
+ /**
+ * Sets the transformation matrix for the pattern.
+ * @param a
+ * @param b
+ * @param c
+ * @param d
+ * @param e
+ * @param f
+ */
+ public void setPatternMatrix(float a, float b, float c, float d, float e, float f) {
+ setMatrix(a, b, c, d, e, f);
+ }
+ /**
+ * Gets the stream representing this pattern
+ *
+ * @return the stream representing this pattern
+ */
+
+ PdfPattern getPattern() {
+ return new PdfPattern(this);
+ }
+
+ /**
+ * Gets a duplicate of this PdfPatternPainter
. All
+ * the members are copied by reference but the buffer stays different.
+ * @return a copy of this PdfPatternPainter
+ */
+
+ public PdfContentByte getDuplicate() {
+ PdfPatternPainter tpl = new PdfPatternPainter();
+ tpl.writer = writer;
+ tpl.pdf = pdf;
+ tpl.thisReference = thisReference;
+ tpl.pageResources = pageResources;
+ tpl.bBox = new Rectangle(bBox);
+ tpl.xstep = xstep;
+ tpl.ystep = ystep;
+ tpl.matrix = matrix;
+ tpl.stencil = stencil;
+ tpl.defaultColor = defaultColor;
+ return tpl;
+ }
+
+ /**
+ * Returns the default color of the pattern.
+ * @return a Color
+ */
+ public Color getDefaultColor() {
+ return defaultColor;
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setGrayFill(float)
+ */
+ public void setGrayFill(float gray) {
+ checkNoColor();
+ super.setGrayFill(gray);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#resetGrayFill()
+ */
+ public void resetGrayFill() {
+ checkNoColor();
+ super.resetGrayFill();
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setGrayStroke(float)
+ */
+ public void setGrayStroke(float gray) {
+ checkNoColor();
+ super.setGrayStroke(gray);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#resetGrayStroke()
+ */
+ public void resetGrayStroke() {
+ checkNoColor();
+ super.resetGrayStroke();
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setRGBColorFillF(float, float, float)
+ */
+ public void setRGBColorFillF(float red, float green, float blue) {
+ checkNoColor();
+ super.setRGBColorFillF(red, green, blue);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#resetRGBColorFill()
+ */
+ public void resetRGBColorFill() {
+ checkNoColor();
+ super.resetRGBColorFill();
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setRGBColorStrokeF(float, float, float)
+ */
+ public void setRGBColorStrokeF(float red, float green, float blue) {
+ checkNoColor();
+ super.setRGBColorStrokeF(red, green, blue);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#resetRGBColorStroke()
+ */
+ public void resetRGBColorStroke() {
+ checkNoColor();
+ super.resetRGBColorStroke();
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setCMYKColorFillF(float, float, float, float)
+ */
+ public void setCMYKColorFillF(float cyan, float magenta, float yellow, float black) {
+ checkNoColor();
+ super.setCMYKColorFillF(cyan, magenta, yellow, black);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#resetCMYKColorFill()
+ */
+ public void resetCMYKColorFill() {
+ checkNoColor();
+ super.resetCMYKColorFill();
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setCMYKColorStrokeF(float, float, float, float)
+ */
+ public void setCMYKColorStrokeF(float cyan, float magenta, float yellow, float black) {
+ checkNoColor();
+ super.setCMYKColorStrokeF(cyan, magenta, yellow, black);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#resetCMYKColorStroke()
+ */
+ public void resetCMYKColorStroke() {
+ checkNoColor();
+ super.resetCMYKColorStroke();
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#addImage(com.lowagie.text.Image, float, float, float, float, float, float)
+ */
+ public void addImage(Image image, float a, float b, float c, float d, float e, float f) throws DocumentException {
+ if (stencil && !image.isMask())
+ checkNoColor();
+ super.addImage(image, a, b, c, d, e, f);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setCMYKColorFill(int, int, int, int)
+ */
+ public void setCMYKColorFill(int cyan, int magenta, int yellow, int black) {
+ checkNoColor();
+ super.setCMYKColorFill(cyan, magenta, yellow, black);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setCMYKColorStroke(int, int, int, int)
+ */
+ public void setCMYKColorStroke(int cyan, int magenta, int yellow, int black) {
+ checkNoColor();
+ super.setCMYKColorStroke(cyan, magenta, yellow, black);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setRGBColorFill(int, int, int)
+ */
+ public void setRGBColorFill(int red, int green, int blue) {
+ checkNoColor();
+ super.setRGBColorFill(red, green, blue);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setRGBColorStroke(int, int, int)
+ */
+ public void setRGBColorStroke(int red, int green, int blue) {
+ checkNoColor();
+ super.setRGBColorStroke(red, green, blue);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setColorStroke(java.awt.Color)
+ */
+ public void setColorStroke(Color color) {
+ checkNoColor();
+ super.setColorStroke(color);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setColorFill(java.awt.Color)
+ */
+ public void setColorFill(Color color) {
+ checkNoColor();
+ super.setColorFill(color);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setColorFill(com.lowagie.text.pdf.PdfSpotColor, float)
+ */
+ public void setColorFill(PdfSpotColor sp, float tint) {
+ checkNoColor();
+ super.setColorFill(sp, tint);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setColorStroke(com.lowagie.text.pdf.PdfSpotColor, float)
+ */
+ public void setColorStroke(PdfSpotColor sp, float tint) {
+ checkNoColor();
+ super.setColorStroke(sp, tint);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setPatternFill(com.lowagie.text.pdf.PdfPatternPainter)
+ */
+ public void setPatternFill(PdfPatternPainter p) {
+ checkNoColor();
+ super.setPatternFill(p);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setPatternFill(com.lowagie.text.pdf.PdfPatternPainter, java.awt.Color, float)
+ */
+ public void setPatternFill(PdfPatternPainter p, Color color, float tint) {
+ checkNoColor();
+ super.setPatternFill(p, color, tint);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setPatternStroke(com.lowagie.text.pdf.PdfPatternPainter, java.awt.Color, float)
+ */
+ public void setPatternStroke(PdfPatternPainter p, Color color, float tint) {
+ checkNoColor();
+ super.setPatternStroke(p, color, tint);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfContentByte#setPatternStroke(com.lowagie.text.pdf.PdfPatternPainter)
+ */
+ public void setPatternStroke(PdfPatternPainter p) {
+ checkNoColor();
+ super.setPatternStroke(p);
+ }
+
+ void checkNoColor() {
+ if (stencil)
+ throw new RuntimeException("Colors are not allowed in uncolored tile patterns.");
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfPrinterGraphics2D.java b/src/main/java/com/lowagie/text/pdf/PdfPrinterGraphics2D.java
new file mode 100644
index 0000000..06a0aa3
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfPrinterGraphics2D.java
@@ -0,0 +1,73 @@
+/*
+ * $Id: PdfPrinterGraphics2D.java,v 1.3 2005/02/17 09:20:53 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2004 Paulo Soares and Alexandru Carstoiu
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999-2005 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000-2005 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.awt.print.PrinterGraphics;
+import java.awt.print.PrinterJob;
+
+/**
+ * This is an extension class for the sole purpose of implementing the
+ * {@link java.awt.print.PrinterGraphics PrinterGraphics} interface.
+ */
+public class PdfPrinterGraphics2D extends PdfGraphics2D implements PrinterGraphics
+{
+ private PrinterJob printerJob;
+
+ public PdfPrinterGraphics2D(PdfContentByte cb, float width, float height, FontMapper fontMapper,
+ boolean onlyShapes, boolean convertImagesToJPEG, float quality, PrinterJob printerJob) {
+ super(cb, width, height, fontMapper, onlyShapes, convertImagesToJPEG, quality);
+ this.printerJob = printerJob;
+ }
+
+ public PrinterJob getPrinterJob() {
+ return printerJob;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfReader.java b/src/main/java/com/lowagie/text/pdf/PdfReader.java
new file mode 100644
index 0000000..da46174
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfReader.java
@@ -0,0 +1,3172 @@
+/*
+ * $Id: PdfReader.java,v 1.76 2006/05/31 16:23:26 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.zip.InflaterInputStream;
+import java.util.Arrays;
+import java.util.Collections;
+
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.PageSize;
+import com.lowagie.text.StringCompare;
+import com.lowagie.text.ExceptionConverter;
+
+/** Reads a PDF document.
+ * @author Paulo Soares (psoares@consiste.pt)
+ * @author Kazuya Ujihara
+ */
+public class PdfReader {
+
+ static final PdfName pageInhCandidates[] = {
+ PdfName.MEDIABOX, PdfName.ROTATE, PdfName.RESOURCES, PdfName.CROPBOX
+ };
+
+ static final PdfName vpnames[] = {PdfName.HIDETOOLBAR, PdfName.HIDEMENUBAR,
+ PdfName.HIDEWINDOWUI, PdfName.FITWINDOW, PdfName.CENTERWINDOW, PdfName.DISPLAYDOCTITLE};
+ static final int vpints[] = {PdfWriter.HideToolbar, PdfWriter.HideMenubar,
+ PdfWriter.HideWindowUI, PdfWriter.FitWindow, PdfWriter.CenterWindow, PdfWriter.DisplayDocTitle};
+
+ static final byte endstream[] = PdfEncodings.convertToBytes("endstream", null);
+ static final byte endobj[] = PdfEncodings.convertToBytes("endobj", null);
+ protected PRTokeniser tokens;
+ // Each xref pair is a position
+ // type 0 -> -1, 0
+ // type 1 -> offset, 0
+ // type 2 -> index, obj num
+ protected int xref[];
+ protected HashMap objStmMark;
+ protected IntHashtable objStmToOffset;
+ protected boolean newXrefType;
+ private ArrayList xrefObj;
+ PdfDictionary rootPages;
+ protected PdfDictionary trailer;
+ //protected ArrayList pages;
+ protected PdfDictionary catalog;
+ protected PageRefs pageRefs;
+ protected PRAcroForm acroForm = null;
+ protected boolean acroFormParsed = false;
+ protected ArrayList pageInh;
+ protected boolean encrypted = false;
+ protected boolean rebuilt = false;
+ protected int freeXref;
+ protected boolean tampered = false;
+ protected int lastXref;
+ protected int eofPos;
+ protected char pdfVersion;
+ protected PdfEncryption decrypt;
+ protected byte password[] = null; //added by ujihara for decryption
+ protected ArrayList strings = new ArrayList();
+ protected boolean sharedStreams = true;
+ protected boolean consolidateNamedDestinations = false;
+ protected int rValue;
+ protected int pValue;
+ private int objNum;
+ private int objGen;
+ private boolean visited[];
+ private IntHashtable newHits;
+ private int fileLength;
+ private boolean hybridXref;
+ private int lastXrefPartial = -1;
+ private boolean partial;
+ private PRIndirectReference cryptoRef;
+
+ /**
+ * Holds value of property appendable.
+ */
+ private boolean appendable;
+
+ protected PdfReader() {
+ }
+
+ /** Reads and parses a PDF document.
+ * @param filename the file name of the document
+ * @throws IOException on error
+ */
+ public PdfReader(String filename) throws IOException {
+ this(filename, null);
+ }
+
+ /** Reads and parses a PDF document.
+ * @param filename the file name of the document
+ * @param ownerPassword the password to read the document
+ * @throws IOException on error
+ */
+ public PdfReader(String filename, byte ownerPassword[]) throws IOException {
+ password = ownerPassword;
+ tokens = new PRTokeniser(filename);
+ readPdf();
+ }
+
+ /** Reads and parses a PDF document.
+ * @param pdfIn the byte array with the document
+ * @throws IOException on error
+ */
+ public PdfReader(byte pdfIn[]) throws IOException {
+ this(pdfIn, null);
+ }
+
+ /** Reads and parses a PDF document.
+ * @param pdfIn the byte array with the document
+ * @param ownerPassword the password to read the document
+ * @throws IOException on error
+ */
+ public PdfReader(byte pdfIn[], byte ownerPassword[]) throws IOException {
+ password = ownerPassword;
+ tokens = new PRTokeniser(pdfIn);
+ readPdf();
+ }
+
+ /** Reads and parses a PDF document.
+ * @param url the URL of the document
+ * @throws IOException on error
+ */
+ public PdfReader(URL url) throws IOException {
+ this(url, null);
+ }
+
+ /** Reads and parses a PDF document.
+ * @param url the URL of the document
+ * @param ownerPassword the password to read the document
+ * @throws IOException on error
+ */
+ public PdfReader(URL url, byte ownerPassword[]) throws IOException {
+ password = ownerPassword;
+ tokens = new PRTokeniser(new RandomAccessFileOrArray(url));
+ readPdf();
+ }
+
+ /**
+ * Reads and parses a PDF document.
+ * @param is the InputStream
containing the document. The stream is read to the
+ * end but is not closed
+ * @param ownerPassword the password to read the document
+ * @throws IOException on error
+ */
+ public PdfReader(InputStream is, byte ownerPassword[]) throws IOException {
+ password = ownerPassword;
+ tokens = new PRTokeniser(new RandomAccessFileOrArray(is));
+ readPdf();
+ }
+
+ /**
+ * Reads and parses a PDF document.
+ * @param is the InputStream
containing the document. The stream is read to the
+ * end but is not closed
+ * @throws IOException on error
+ */
+ public PdfReader(InputStream is) throws IOException {
+ this(is, null);
+ }
+
+ /**
+ * Reads and parses a pdf document. Contrary to the other constructors only the xref is read
+ * into memory. The reader is said to be working in "partial" mode as only parts of the pdf
+ * are read as needed. The pdf is left open but may be closed at any time with
+ * PdfReader.close()
, reopen is automatic.
+ * @param raf the document location
+ * @param ownerPassword the password or null
for no password
+ * @throws IOException on error
+ */
+ public PdfReader(RandomAccessFileOrArray raf, byte ownerPassword[]) throws IOException {
+ password = ownerPassword;
+ partial = true;
+ tokens = new PRTokeniser(raf);
+ readPdfPartial();
+ }
+
+ /** Creates an independent duplicate.
+ * @param reader the PdfReader
to duplicate
+ */
+ public PdfReader(PdfReader reader) {
+ this.appendable = reader.appendable;
+ this.consolidateNamedDestinations = reader.consolidateNamedDestinations;
+ this.encrypted = reader.encrypted;
+ this.rebuilt = reader.rebuilt;
+ this.sharedStreams = reader.sharedStreams;
+ this.tampered = reader.tampered;
+ this.password = reader.password;
+ this.pdfVersion = reader.pdfVersion;
+ this.eofPos = reader.eofPos;
+ this.freeXref = reader.freeXref;
+ this.lastXref = reader.lastXref;
+ this.tokens = new PRTokeniser(reader.tokens.getSafeFile());
+ if (reader.decrypt != null)
+ this.decrypt = new PdfEncryption(reader.decrypt);
+ this.pValue = reader.pValue;
+ this.rValue = reader.rValue;
+ this.xrefObj = new ArrayList(reader.xrefObj);
+ for (int k = 0; k < reader.xrefObj.size(); ++k) {
+ this.xrefObj.set(k, duplicatePdfObject((PdfObject)reader.xrefObj.get(k), this));
+ }
+ this.pageRefs = new PageRefs(reader.pageRefs, this);
+ this.trailer = (PdfDictionary)duplicatePdfObject(reader.trailer, this);
+ this.catalog = (PdfDictionary)getPdfObject(trailer.get(PdfName.ROOT));
+ this.rootPages = (PdfDictionary)getPdfObject(catalog.get(PdfName.PAGES));
+ this.fileLength = reader.fileLength;
+ this.partial = reader.partial;
+ this.hybridXref = reader.hybridXref;
+ this.objStmToOffset = reader.objStmToOffset;
+ this.xref = reader.xref;
+ this.cryptoRef = (PRIndirectReference)duplicatePdfObject(reader.cryptoRef, this);
+ }
+
+ /** Gets a new file instance of the original PDF
+ * document.
+ * @return a new file instance of the original PDF document
+ */
+ public RandomAccessFileOrArray getSafeFile() {
+ return tokens.getSafeFile();
+ }
+
+ protected PdfReaderInstance getPdfReaderInstance(PdfWriter writer) {
+ return new PdfReaderInstance(this, writer);
+ }
+
+ /** Gets the number of pages in the document.
+ * @return the number of pages in the document
+ */
+ public int getNumberOfPages() {
+ return pageRefs.size();
+ }
+
+ /** Returns the document's catalog. This dictionary is not a copy,
+ * any changes will be reflected in the catalog.
+ * @return the document's catalog
+ */
+ public PdfDictionary getCatalog() {
+ return catalog;
+ }
+
+ /** Returns the document's acroform, if it has one.
+ * @return the document's acroform
+ */
+ public PRAcroForm getAcroForm() {
+ if (!acroFormParsed) {
+ acroFormParsed = true;
+ PdfObject form = catalog.get(PdfName.ACROFORM);
+ if (form != null) {
+ try {
+ acroForm = new PRAcroForm(this);
+ acroForm.readAcroForm((PdfDictionary)getPdfObject(form));
+ }
+ catch (Exception e) {
+ acroForm = null;
+ }
+ }
+ }
+ return acroForm;
+ }
+ /**
+ * Gets the page rotation. This value can be 0, 90, 180 or 270.
+ * @param index the page number. The first page is 1
+ * @return the page rotation
+ */
+ public int getPageRotation(int index) {
+ return getPageRotation(pageRefs.getPageNRelease(index));
+ }
+
+ int getPageRotation(PdfDictionary page) {
+ PdfNumber rotate = (PdfNumber)getPdfObject(page.get(PdfName.ROTATE));
+ if (rotate == null)
+ return 0;
+ else {
+ int n = rotate.intValue();
+ n %= 360;
+ return n < 0 ? n + 360 : n;
+ }
+ }
+ /** Gets the page size, taking rotation into account. This
+ * is a Rectangle
with the value of the /MediaBox and the /Rotate key.
+ * @param index the page number. The first page is 1
+ * @return a Rectangle
+ */
+ public Rectangle getPageSizeWithRotation(int index) {
+ return getPageSizeWithRotation(pageRefs.getPageNRelease(index));
+ }
+
+ /**
+ * Gets the rotated page from a page dictionary.
+ * @param page the page dictionary
+ * @return the rotated page
+ */
+ public Rectangle getPageSizeWithRotation(PdfDictionary page) {
+ Rectangle rect = getPageSize(page);
+ int rotation = getPageRotation(page);
+ while (rotation > 0) {
+ rect = rect.rotate();
+ rotation -= 90;
+ }
+ return rect;
+ }
+
+ /** Gets the page size without taking rotation into account. This
+ * is the value of the /MediaBox key.
+ * @param index the page number. The first page is 1
+ * @return the page size
+ */
+ public Rectangle getPageSize(int index) {
+ return getPageSize(pageRefs.getPageNRelease(index));
+ }
+
+ /**
+ * Gets the page from a page dictionary
+ * @param page the page dictionary
+ * @return the page
+ */
+ public Rectangle getPageSize(PdfDictionary page) {
+ PdfArray mediaBox = (PdfArray)getPdfObject(page.get(PdfName.MEDIABOX));
+ return getNormalizedRectangle(mediaBox);
+ }
+
+ /** Gets the crop box without taking rotation into account. This
+ * is the value of the /CropBox key. The crop box is the part
+ * of the document to be displayed or printed. It usually is the same
+ * as the media box but may be smaller. If the page doesn't have a crop
+ * box the page size will be returned.
+ * @param index the page number. The first page is 1
+ * @return the crop box
+ */
+ public Rectangle getCropBox(int index) {
+ PdfDictionary page = pageRefs.getPageNRelease(index);
+ PdfArray cropBox = (PdfArray)getPdfObjectRelease(page.get(PdfName.CROPBOX));
+ if (cropBox == null)
+ return getPageSize(page);
+ return getNormalizedRectangle(cropBox);
+ }
+
+ /** Gets the box size. Allowed names are: "crop", "trim", "art", "bleed" and "media".
+ * @param index the page number. The first page is 1
+ * @param boxName the box name
+ * @return the box rectangle or null
+ */
+ public Rectangle getBoxSize(int index, String boxName) {
+ PdfDictionary page = pageRefs.getPageNRelease(index);
+ PdfArray box = null;
+ if (boxName.equals("trim"))
+ box = (PdfArray)getPdfObjectRelease(page.get(PdfName.TRIMBOX));
+ else if (boxName.equals("art"))
+ box = (PdfArray)getPdfObjectRelease(page.get(PdfName.ARTBOX));
+ else if (boxName.equals("bleed"))
+ box = (PdfArray)getPdfObjectRelease(page.get(PdfName.BLEEDBOX));
+ else if (boxName.equals("crop"))
+ box = (PdfArray)getPdfObjectRelease(page.get(PdfName.CROPBOX));
+ else if (boxName.equals("media"))
+ box = (PdfArray)getPdfObjectRelease(page.get(PdfName.MEDIABOX));
+ if (box == null)
+ return null;
+ return getNormalizedRectangle(box);
+ }
+
+ /** Returns the content of the document information dictionary as a HashMap
+ * of String
.
+ * @return content of the document information dictionary
+ */
+ public HashMap getInfo() {
+ HashMap map = new HashMap();
+ PdfDictionary info = (PdfDictionary)getPdfObject(trailer.get(PdfName.INFO));
+ if (info == null)
+ return map;
+ for (Iterator it = info.getKeys().iterator(); it.hasNext();) {
+ PdfName key = (PdfName)it.next();
+ PdfObject obj = getPdfObject(info.get(key));
+ if (obj == null)
+ continue;
+ String value = obj.toString();
+ switch (obj.type()) {
+ case PdfObject.STRING: {
+ value = ((PdfString)obj).toUnicodeString();
+ break;
+ }
+ case PdfObject.NAME: {
+ value = PdfName.decodeName(value);
+ break;
+ }
+ }
+ map.put(PdfName.decodeName(key.toString()), value);
+ }
+ return map;
+ }
+
+ /** Normalizes a Rectangle
so that llx and lly are smaller than urx and ury.
+ * @param box the original rectangle
+ * @return a normalized Rectangle
+ */
+ public static Rectangle getNormalizedRectangle(PdfArray box) {
+ ArrayList rect = box.getArrayList();
+ float llx = ((PdfNumber)rect.get(0)).floatValue();
+ float lly = ((PdfNumber)rect.get(1)).floatValue();
+ float urx = ((PdfNumber)rect.get(2)).floatValue();
+ float ury = ((PdfNumber)rect.get(3)).floatValue();
+ return new Rectangle(Math.min(llx, urx), Math.min(lly, ury),
+ Math.max(llx, urx), Math.max(lly, ury));
+ }
+
+ protected void readPdf() throws IOException {
+ try {
+ fileLength = tokens.getFile().length();
+ pdfVersion = tokens.checkPdfHeader();
+ try {
+ readXref();
+ }
+ catch (Exception e) {
+ try {
+ rebuilt = true;
+ rebuildXref();
+ lastXref = -1;
+ }
+ catch (Exception ne) {
+ throw new IOException("Rebuild failed: " + ne.getMessage() + "; Original message: " + e.getMessage());
+ }
+ }
+ try {
+ readDocObj();
+ }
+ catch (IOException ne) {
+ if (rebuilt)
+ throw ne;
+ rebuilt = true;
+ encrypted = false;
+ rebuildXref();
+ lastXref = -1;
+ readDocObj();
+ }
+
+ strings.clear();
+ readPages();
+ eliminateSharedStreams();
+ removeUnusedObjects();
+ }
+ finally {
+ try {
+ tokens.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ }
+
+ protected void readPdfPartial() throws IOException {
+ try {
+ fileLength = tokens.getFile().length();
+ pdfVersion = tokens.checkPdfHeader();
+ try {
+ readXref();
+ }
+ catch (Exception e) {
+ try {
+ rebuilt = true;
+ rebuildXref();
+ lastXref = -1;
+ }
+ catch (Exception ne) {
+ throw new IOException("Rebuild failed: " + ne.getMessage() + "; Original message: " + e.getMessage());
+ }
+ }
+ readDocObjPartial();
+ readPages();
+ }
+ catch (IOException e) {
+ try{tokens.close();}catch(Exception ee){}
+ throw e;
+ }
+ }
+
+ private boolean equalsArray(byte ar1[], byte ar2[], int size) {
+ for (int k = 0; k < size; ++k) {
+ if (ar1[k] != ar2[k])
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @throws IOException
+ */
+ private void readDecryptedDocObj() throws IOException {
+ if (encrypted)
+ return;
+ PdfObject encDic = trailer.get(PdfName.ENCRYPT);
+ if (encDic == null || encDic.toString().equals("null"))
+ return;
+ encrypted = true;
+ PdfDictionary enc = (PdfDictionary)getPdfObject(encDic);
+
+ String s;
+ PdfObject o;
+
+ PdfArray documentIDs = (PdfArray)getPdfObject(trailer.get(PdfName.ID));
+ byte documentID[] = null;
+ if (documentIDs != null) {
+ o = (PdfObject)documentIDs.getArrayList().get(0);
+ s = o.toString();
+ documentID = com.lowagie.text.DocWriter.getISOBytes(s);
+ }
+
+ s = enc.get(PdfName.U).toString();
+ byte uValue[] = com.lowagie.text.DocWriter.getISOBytes(s);
+ s = enc.get(PdfName.O).toString();
+ byte oValue[] = com.lowagie.text.DocWriter.getISOBytes(s);
+
+ o = enc.get(PdfName.R);
+ if (!o.isNumber()) throw new IOException("Illegal R value.");
+ rValue = ((PdfNumber)o).intValue();
+ if (rValue != 2 && rValue != 3) throw new IOException("Unknown encryption type (" + rValue + ")");
+
+ o = enc.get(PdfName.P);
+ if (!o.isNumber()) throw new IOException("Illegal P value.");
+ pValue = ((PdfNumber)o).intValue();
+
+ // get the Keylength if Revision is 3
+ int lengthValue;
+ if ( rValue == 3 ){
+ o = enc.get(PdfName.LENGTH);
+ if (!o.isNumber())
+ throw new IOException("Illegal Length value.");
+ lengthValue = ( (PdfNumber) o).intValue();
+ if (lengthValue > 128 || lengthValue < 40 || lengthValue % 8 != 0)
+ throw new IOException("Illegal Length value.");
+ } else {
+ // Keylength is 40 bit in revision 2
+ lengthValue=40;
+ }
+
+
+
+ decrypt = new PdfEncryption();
+
+ //check by user password
+ decrypt.setupByUserPassword(documentID, password, oValue, pValue, lengthValue, rValue);
+ if (!equalsArray(uValue, decrypt.userKey, rValue == 3 ? 16 : 32)) {
+ //check by owner password
+ decrypt.setupByOwnerPassword(documentID, password, uValue, oValue, pValue, lengthValue, rValue);
+ if (!equalsArray(uValue, decrypt.userKey, rValue == 3 ? 16 : 32)) {
+ throw new IOException("Bad user password");
+ }
+ }
+ for (int k = 0; k < strings.size(); ++k) {
+ PdfString str = (PdfString)strings.get(k);
+ str.decrypt(this);
+ }
+ if (encDic.isIndirect()) {
+ cryptoRef = (PRIndirectReference)encDic;
+ xrefObj.set(cryptoRef.getNumber(), null);
+ }
+ }
+
+ /**
+ * @param obj
+ * @return a PdfObject
+ */
+ public static PdfObject getPdfObjectRelease(PdfObject obj) {
+ PdfObject obj2 = getPdfObject(obj);
+ releaseLastXrefPartial(obj);
+ return obj2;
+ }
+
+
+ /**
+ * Reads a PdfObject
resolving an indirect reference
+ * if needed.
+ * @param obj the PdfObject
to read
+ * @return the resolved PdfObject
+ */
+ public static PdfObject getPdfObject(PdfObject obj) {
+ if (obj == null)
+ return null;
+ if (!obj.isIndirect())
+ return obj;
+ try {
+ PRIndirectReference ref = (PRIndirectReference)obj;
+ int idx = ref.getNumber();
+ boolean appendable = ref.getReader().appendable;
+ obj = ref.getReader().getPdfObject(idx);
+ if (obj == null) {
+ if (appendable) {
+ obj = new PdfNull();
+ obj.setIndRef(ref);
+ return obj;
+ }
+ else
+ return PdfNull.PDFNULL;
+ }
+ else {
+ if (appendable) {
+ switch (obj.type()) {
+ case PdfObject.NULL:
+ obj = new PdfNull();
+ break;
+ case PdfObject.BOOLEAN:
+ obj = new PdfBoolean(((PdfBoolean)obj).booleanValue());
+ break;
+ case PdfObject.NAME:
+ obj = new PdfName(obj.getBytes());
+ break;
+ }
+ obj.setIndRef(ref);
+ }
+ return obj;
+ }
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Reads a PdfObject
resolving an indirect reference
+ * if needed. If the reader was opened in partial mode the object will be released
+ * to save memory.
+ * @param obj the PdfObject
to read
+ * @param parent
+ * @return a PdfObject
+ */
+ public static PdfObject getPdfObjectRelease(PdfObject obj, PdfObject parent) {
+ PdfObject obj2 = getPdfObject(obj, parent);
+ releaseLastXrefPartial(obj);
+ return obj2;
+ }
+
+ /**
+ * @param obj
+ * @param parent
+ * @return a PdfObject
+ */
+ public static PdfObject getPdfObject(PdfObject obj, PdfObject parent) {
+ if (obj == null)
+ return null;
+ if (!obj.isIndirect()) {
+ PRIndirectReference ref = null;
+ if (parent != null && (ref = parent.getIndRef()) != null && ref.getReader().isAppendable()) {
+ switch (obj.type()) {
+ case PdfObject.NULL:
+ obj = new PdfNull();
+ break;
+ case PdfObject.BOOLEAN:
+ obj = new PdfBoolean(((PdfBoolean)obj).booleanValue());
+ break;
+ case PdfObject.NAME:
+ obj = new PdfName(obj.getBytes());
+ break;
+ }
+ obj.setIndRef(ref);
+ }
+ return obj;
+ }
+ return getPdfObject(obj);
+ }
+
+ /**
+ * @param idx
+ * @return a PdfObject
+ */
+ public PdfObject getPdfObjectRelease(int idx) {
+ PdfObject obj = getPdfObject(idx);
+ releaseLastXrefPartial();
+ return obj;
+ }
+
+ /**
+ * @param idx
+ * @return aPdfObject
+ */
+ public PdfObject getPdfObject(int idx) {
+ try {
+ lastXrefPartial = -1;
+ if (idx < 0 || idx >= xrefObj.size())
+ return null;
+ PdfObject obj = (PdfObject)xrefObj.get(idx);
+ if (!partial || obj != null)
+ return obj;
+ if (idx * 2 >= xref.length)
+ return null;
+ obj = readSingleObject(idx);
+ lastXrefPartial = -1;
+ if (obj != null)
+ lastXrefPartial = idx;
+ return obj;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ *
+ */
+ public void resetLastXrefPartial() {
+ lastXrefPartial = -1;
+ }
+
+ /**
+ *
+ */
+ public void releaseLastXrefPartial() {
+ if (partial && lastXrefPartial != -1) {
+ xrefObj.set(lastXrefPartial, null);
+ lastXrefPartial = -1;
+ }
+ }
+
+ /**
+ * @param obj
+ */
+ public static void releaseLastXrefPartial(PdfObject obj) {
+ if (obj == null)
+ return;
+ if (!obj.isIndirect())
+ return;
+ PRIndirectReference ref = (PRIndirectReference)obj;
+ PdfReader reader = ref.getReader();
+ if (reader.partial && reader.lastXrefPartial != -1 && reader.lastXrefPartial == ref.getNumber()) {
+ reader.xrefObj.set(reader.lastXrefPartial, null);
+ }
+ reader.lastXrefPartial = -1;
+ }
+
+ private void setXrefPartialObject(int idx, PdfObject obj) {
+ if (!partial || idx < 0)
+ return;
+ xrefObj.set(idx, obj);
+ }
+
+ /**
+ * @param obj
+ * @return an indirect reference
+ */
+ public PRIndirectReference addPdfObject(PdfObject obj) {
+ xrefObj.add(obj);
+ return new PRIndirectReference(this, xrefObj.size() - 1);
+ }
+
+ protected void readPages() throws IOException {
+ pageInh = new ArrayList();
+ catalog = (PdfDictionary)getPdfObject(trailer.get(PdfName.ROOT));
+ rootPages = (PdfDictionary)getPdfObject(catalog.get(PdfName.PAGES));
+ pageRefs = new PageRefs(this);
+ }
+
+ protected void readDocObjPartial() throws IOException {
+ xrefObj = new ArrayList(xref.length / 2);
+ xrefObj.addAll(Collections.nCopies(xref.length / 2, null));
+ readDecryptedDocObj();
+ if (objStmToOffset != null) {
+ int keys[] = objStmToOffset.getKeys();
+ for (int k = 0; k < keys.length; ++k) {
+ int n = keys[k];
+ objStmToOffset.put(n, xref[n * 2]);
+ xref[n * 2] = -1;
+ }
+ }
+ }
+
+ protected PdfObject readSingleObject(int k) throws IOException {
+ strings.clear();
+ int k2 = k * 2;
+ int pos = xref[k2];
+ if (pos < 0)
+ return null;
+ if (xref[k2 + 1] > 0)
+ pos = objStmToOffset.get(xref[k2 + 1]);
+ tokens.seek(pos);
+ tokens.nextValidToken();
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
+ tokens.throwError("Invalid object number.");
+ objNum = tokens.intValue();
+ tokens.nextValidToken();
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
+ tokens.throwError("Invalid generation number.");
+ objGen = tokens.intValue();
+ tokens.nextValidToken();
+ if (!tokens.getStringValue().equals("obj"))
+ tokens.throwError("Token 'obj' expected.");
+ PdfObject obj;
+ try {
+ obj = readPRObject();
+ for (int j = 0; j < strings.size(); ++j) {
+ PdfString str = (PdfString)strings.get(j);
+ str.decrypt(this);
+ }
+ if (obj.isStream()) {
+ checkPRStreamLength((PRStream)obj);
+ }
+ }
+ catch (Exception e) {
+ obj = null;
+ }
+ if (xref[k2 + 1] > 0) {
+ obj = readOneObjStm((PRStream)obj, xref[k2]);
+ }
+ xrefObj.set(k, obj);
+ return obj;
+ }
+
+ protected PdfObject readOneObjStm(PRStream stream, int idx) throws IOException {
+ int first = ((PdfNumber)getPdfObject(stream.get(PdfName.FIRST))).intValue();
+ byte b[] = getStreamBytes(stream, tokens.getFile());
+ PRTokeniser saveTokens = tokens;
+ tokens = new PRTokeniser(b);
+ try {
+ int address = 0;
+ boolean ok = true;
+ ++idx;
+ for (int k = 0; k < idx; ++k) {
+ ok = tokens.nextToken();
+ if (!ok)
+ break;
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) {
+ ok = false;
+ break;
+ }
+ ok = tokens.nextToken();
+ if (!ok)
+ break;
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) {
+ ok = false;
+ break;
+ }
+ address = tokens.intValue() + first;
+ }
+ if (!ok)
+ throw new IOException("Error reading ObjStm");
+ tokens.seek(address);
+ return readPRObject();
+ }
+ finally {
+ tokens = saveTokens;
+ }
+ }
+
+ /**
+ * @return the percentage of the cross reference table that has been read
+ */
+ public double dumpPerc() {
+ int total = 0;
+ for (int k = 0; k < xrefObj.size(); ++k) {
+ if (xrefObj.get(k) != null)
+ ++total;
+ }
+ return (total * 100.0 / xrefObj.size());
+ }
+
+ protected void readDocObj() throws IOException {
+ ArrayList streams = new ArrayList();
+ xrefObj = new ArrayList(xref.length / 2);
+ xrefObj.addAll(Collections.nCopies(xref.length / 2, null));
+ for (int k = 2; k < xref.length; k += 2) {
+ int pos = xref[k];
+ if (pos <= 0 || xref[k + 1] > 0)
+ continue;
+ tokens.seek(pos);
+ tokens.nextValidToken();
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
+ tokens.throwError("Invalid object number.");
+ objNum = tokens.intValue();
+ tokens.nextValidToken();
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
+ tokens.throwError("Invalid generation number.");
+ objGen = tokens.intValue();
+ tokens.nextValidToken();
+ if (!tokens.getStringValue().equals("obj"))
+ tokens.throwError("Token 'obj' expected.");
+ PdfObject obj;
+ try {
+ obj = readPRObject();
+ if (obj.isStream()) {
+ streams.add(obj);
+ }
+ }
+ catch (Exception e) {
+ obj = null;
+ }
+ xrefObj.set(k / 2, obj);
+ }
+ for (int k = 0; k < streams.size(); ++k) {
+ checkPRStreamLength((PRStream)streams.get(k));
+ }
+ readDecryptedDocObj();
+ if (objStmMark != null) {
+ for (Iterator i = objStmMark.entrySet().iterator(); i.hasNext();) {
+ Map.Entry entry = (Map.Entry)i.next();
+ int n = ((Integer)entry.getKey()).intValue();
+ IntHashtable h = (IntHashtable)entry.getValue();
+ readObjStm((PRStream)xrefObj.get(n), h);
+ xrefObj.set(n, null);
+ }
+ objStmMark = null;
+ }
+ xref = null;
+ }
+
+ private void checkPRStreamLength(PRStream stream) throws IOException {
+ int fileLength = tokens.length();
+ int start = stream.getOffset();
+ boolean calc = false;
+ int streamLength = 0;
+ PdfObject obj = getPdfObjectRelease(stream.get(PdfName.LENGTH));
+ if (obj != null && obj.type() == PdfObject.NUMBER) {
+ streamLength = ((PdfNumber)obj).intValue();
+ if (streamLength + start > fileLength - 20)
+ calc = true;
+ else {
+ tokens.seek(start + streamLength);
+ String line = tokens.readString(20);
+ if (!line.startsWith("\nendstream") &&
+ !line.startsWith("\r\nendstream") &&
+ !line.startsWith("\rendstream") &&
+ !line.startsWith("endstream"))
+ calc = true;
+ }
+ }
+ else
+ calc = true;
+ if (calc) {
+ byte tline[] = new byte[16];
+ tokens.seek(start);
+ while (true) {
+ int pos = tokens.getFilePointer();
+ if (!tokens.readLineSegment(tline))
+ break;
+ if (equalsn(tline, endstream)) {
+ streamLength = pos - start;
+ break;
+ }
+ if (equalsn(tline, endobj)) {
+ tokens.seek(pos - 16);
+ String s = tokens.readString(16);
+ int index = s.indexOf("endstream");
+ if (index >= 0)
+ pos = pos - 16 + index;
+ streamLength = pos - start;
+ break;
+ }
+ }
+ }
+ stream.setLength(streamLength);
+ }
+
+ protected void readObjStm(PRStream stream, IntHashtable map) throws IOException {
+ int first = ((PdfNumber)getPdfObject(stream.get(PdfName.FIRST))).intValue();
+ int n = ((PdfNumber)getPdfObject(stream.get(PdfName.N))).intValue();
+ byte b[] = getStreamBytes(stream, tokens.getFile());
+ PRTokeniser saveTokens = tokens;
+ tokens = new PRTokeniser(b);
+ try {
+ int address[] = new int[n];
+ int objNumber[] = new int[n];
+ boolean ok = true;
+ for (int k = 0; k < n; ++k) {
+ ok = tokens.nextToken();
+ if (!ok)
+ break;
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) {
+ ok = false;
+ break;
+ }
+ objNumber[k] = tokens.intValue();
+ ok = tokens.nextToken();
+ if (!ok)
+ break;
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) {
+ ok = false;
+ break;
+ }
+ address[k] = tokens.intValue() + first;
+ }
+ if (!ok)
+ throw new IOException("Error reading ObjStm");
+ for (int k = 0; k < n; ++k) {
+ if (map.containsKey(k)) {
+ tokens.seek(address[k]);
+ PdfObject obj = readPRObject();
+ xrefObj.set(objNumber[k], obj);
+ }
+ }
+ }
+ finally {
+ tokens = saveTokens;
+ }
+ }
+
+ /**
+ * Eliminates the reference to the object freeing the memory used by it and clearing
+ * the xref entry.
+ * @param obj the object. If it's an indirect reference it will be eliminated
+ * @return the object or the already erased dereferenced object
+ */
+ public static PdfObject killIndirect(PdfObject obj) {
+ if (obj == null || obj.isNull())
+ return null;
+ PdfObject ret = getPdfObjectRelease(obj);
+ if (obj.isIndirect()) {
+ PRIndirectReference ref = (PRIndirectReference)obj;
+ PdfReader reader = ref.getReader();
+ int n = ref.getNumber();
+ reader.xrefObj.set(n, null);
+ if (reader.partial)
+ reader.xref[n * 2] = -1;
+ }
+ return ret;
+ }
+
+ private void ensureXrefSize(int size) {
+ if (size == 0)
+ return;
+ if (xref == null)
+ xref = new int[size];
+ else {
+ if (xref.length < size) {
+ int xref2[] = new int[size];
+ System.arraycopy(xref, 0, xref2, 0, xref.length);
+ xref = xref2;
+ }
+ }
+ }
+
+ protected void readXref() throws IOException {
+ hybridXref = false;
+ newXrefType = false;
+ tokens.seek(tokens.getStartxref());
+ tokens.nextToken();
+ if (!tokens.getStringValue().equals("startxref"))
+ throw new IOException("startxref not found.");
+ tokens.nextToken();
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
+ throw new IOException("startxref is not followed by a number.");
+ int startxref = tokens.intValue();
+ lastXref = startxref;
+ eofPos = tokens.getFilePointer();
+ try {
+ if (readXRefStream(startxref)) {
+ newXrefType = true;
+ return;
+ }
+ }
+ catch (Exception e) {}
+ xref = null;
+ tokens.seek(startxref);
+ trailer = readXrefSection();
+ PdfDictionary trailer2 = trailer;
+ while (true) {
+ PdfNumber prev = (PdfNumber)trailer2.get(PdfName.PREV);
+ if (prev == null)
+ break;
+ tokens.seek(prev.intValue());
+ trailer2 = readXrefSection();
+ }
+ }
+
+ protected PdfDictionary readXrefSection() throws IOException {
+ tokens.nextValidToken();
+ if (!tokens.getStringValue().equals("xref"))
+ tokens.throwError("xref subsection not found");
+ int start = 0;
+ int end = 0;
+ int pos = 0;
+ int gen = 0;
+ while (true) {
+ tokens.nextValidToken();
+ if (tokens.getStringValue().equals("trailer"))
+ break;
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
+ tokens.throwError("Object number of the first object in this xref subsection not found");
+ start = tokens.intValue();
+ tokens.nextValidToken();
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
+ tokens.throwError("Number of entries in this xref subsection not found");
+ end = tokens.intValue() + start;
+ if (start == 1) { // fix incorrect start number
+ int back = tokens.getFilePointer();
+ tokens.nextValidToken();
+ pos = tokens.intValue();
+ tokens.nextValidToken();
+ gen = tokens.intValue();
+ if (pos == 0 && gen == 65535) {
+ --start;
+ --end;
+ }
+ tokens.seek(back);
+ }
+ ensureXrefSize(end * 2);
+ for (int k = start; k < end; ++k) {
+ tokens.nextValidToken();
+ pos = tokens.intValue();
+ tokens.nextValidToken();
+ gen = tokens.intValue();
+ tokens.nextValidToken();
+ int p = k * 2;
+ if (tokens.getStringValue().equals("n")) {
+ if (xref[p] == 0 && xref[p + 1] == 0) {
+// if (pos == 0)
+// tokens.throwError("File position 0 cross-reference entry in this xref subsection");
+ xref[p] = pos;
+ }
+ }
+ else if (tokens.getStringValue().equals("f")) {
+ if (xref[p] == 0 && xref[p + 1] == 0)
+ xref[p] = -1;
+ }
+ else
+ tokens.throwError("Invalid cross-reference entry in this xref subsection");
+ }
+ }
+ PdfDictionary trailer = (PdfDictionary)readPRObject();
+ PdfNumber xrefSize = (PdfNumber)trailer.get(PdfName.SIZE);
+ ensureXrefSize(xrefSize.intValue() * 2);
+ PdfObject xrs = trailer.get(PdfName.XREFSTM);
+ if (xrs != null && xrs.isNumber()) {
+ int loc = ((PdfNumber)xrs).intValue();
+ try {
+ readXRefStream(loc);
+ newXrefType = true;
+ hybridXref = true;
+ }
+ catch (IOException e) {
+ xref = null;
+ throw e;
+ }
+ }
+ return trailer;
+ }
+
+ protected boolean readXRefStream(int ptr) throws IOException {
+ tokens.seek(ptr);
+ int thisStream = 0;
+ if (!tokens.nextToken())
+ return false;
+ if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
+ return false;
+ thisStream = tokens.intValue();
+ if (!tokens.nextToken() || tokens.getTokenType() != PRTokeniser.TK_NUMBER)
+ return false;
+ if (!tokens.nextToken() || !tokens.getStringValue().equals("obj"))
+ return false;
+ PdfObject object = readPRObject();
+ PRStream stm = null;
+ if (object.isStream()) {
+ stm = (PRStream)object;
+ if (!PdfName.XREF.equals(stm.get(PdfName.TYPE)))
+ return false;
+ }
+ if (trailer == null) {
+ trailer = new PdfDictionary();
+ trailer.putAll(stm);
+ }
+ stm.setLength(((PdfNumber)stm.get(PdfName.LENGTH)).intValue());
+ int size = ((PdfNumber)stm.get(PdfName.SIZE)).intValue();
+ PdfArray index;
+ PdfObject obj = stm.get(PdfName.INDEX);
+ if (obj == null) {
+ index = new PdfArray();
+ index.add(new int[]{0, size});
+ }
+ else
+ index = (PdfArray)obj;
+ PdfArray w = (PdfArray)stm.get(PdfName.W);
+ int prev = -1;
+ obj = stm.get(PdfName.PREV);
+ if (obj != null)
+ prev = ((PdfNumber)obj).intValue();
+ // Each xref pair is a position
+ // type 0 -> -1, 0
+ // type 1 -> offset, 0
+ // type 2 -> index, obj num
+ ensureXrefSize(size * 2);
+ if (objStmMark == null && !partial)
+ objStmMark = new HashMap();
+ if (objStmToOffset == null && partial)
+ objStmToOffset = new IntHashtable();
+ byte b[] = getStreamBytes(stm, tokens.getFile());
+ int bptr = 0;
+ ArrayList wa = w.getArrayList();
+ int wc[] = new int[3];
+ for (int k = 0; k < 3; ++k)
+ wc[k] = ((PdfNumber)wa.get(k)).intValue();
+ ArrayList sections = index.getArrayList();
+ for (int idx = 0; idx < sections.size(); idx += 2) {
+ int start = ((PdfNumber)sections.get(idx)).intValue();
+ int length = ((PdfNumber)sections.get(idx + 1)).intValue();
+ ensureXrefSize((start + length) * 2);
+ while (length-- > 0) {
+ int type = 1;
+ if (wc[0] > 0) {
+ type = 0;
+ for (int k = 0; k < wc[0]; ++k)
+ type = (type << 8) + (b[bptr++] & 0xff);
+ }
+ int field2 = 0;
+ for (int k = 0; k < wc[1]; ++k)
+ field2 = (field2 << 8) + (b[bptr++] & 0xff);
+ int field3 = 0;
+ for (int k = 0; k < wc[2]; ++k)
+ field3 = (field3 << 8) + (b[bptr++] & 0xff);
+ int base = start * 2;
+ if (xref[base] == 0 && xref[base + 1] == 0) {
+ switch (type) {
+ case 0:
+ xref[base] = -1;
+ break;
+ case 1:
+ xref[base] = field2;
+ break;
+ case 2:
+ xref[base] = field3;
+ xref[base + 1] = field2;
+ if (partial) {
+ objStmToOffset.put(field2, 0);
+ }
+ else {
+ Integer on = new Integer(field2);
+ IntHashtable seq = (IntHashtable)objStmMark.get(on);
+ if (seq == null) {
+ seq = new IntHashtable();
+ seq.put(field3, 1);
+ objStmMark.put(on, seq);
+ }
+ else
+ seq.put(field3, 1);
+ }
+ break;
+ }
+ }
+ ++start;
+ }
+ }
+ thisStream *= 2;
+ if (thisStream < xref.length)
+ xref[thisStream] = -1;
+
+ if (prev == -1)
+ return true;
+ return readXRefStream(prev);
+ }
+
+ protected void rebuildXref() throws IOException {
+ hybridXref = false;
+ newXrefType = false;
+ tokens.seek(0);
+ int xr[][] = new int[1024][];
+ int top = 0;
+ trailer = null;
+ byte line[] = new byte[64];
+ for (;;) {
+ int pos = tokens.getFilePointer();
+ if (!tokens.readLineSegment(line))
+ break;
+ if (line[0] == 't') {
+ if (!PdfEncodings.convertToString(line, null).startsWith("trailer"))
+ continue;
+ tokens.seek(pos);
+ tokens.nextToken();
+ pos = tokens.getFilePointer();
+ try {
+ PdfDictionary dic = (PdfDictionary)readPRObject();
+ if (dic.get(PdfName.ROOT) != null)
+ trailer = dic;
+ else
+ tokens.seek(pos);
+ }
+ catch (Exception e) {
+ tokens.seek(pos);
+ }
+ }
+ else if (line[0] >= '0' && line[0] <= '9') {
+ int obj[] = PRTokeniser.checkObjectStart(line);
+ if (obj == null)
+ continue;
+ int num = obj[0];
+ int gen = obj[1];
+ if (num >= xr.length) {
+ int newLength = num * 2;
+ int xr2[][] = new int[newLength][];
+ System.arraycopy(xr, 0, xr2, 0, top);
+ xr = xr2;
+ }
+ if (num >= top)
+ top = num + 1;
+ if (xr[num] == null || gen >= xr[num][1]) {
+ obj[0] = pos;
+ xr[num] = obj;
+ }
+ }
+ }
+ if (trailer == null)
+ throw new IOException("trailer not found.");
+ xref = new int[top * 2];
+ for (int k = 0; k < top; ++k) {
+ int obj[] = xr[k];
+ if (obj != null)
+ xref[k * 2] = obj[0];
+ }
+ }
+
+ protected PdfDictionary readDictionary() throws IOException {
+ PdfDictionary dic = new PdfDictionary();
+ while (true) {
+ tokens.nextValidToken();
+ if (tokens.getTokenType() == PRTokeniser.TK_END_DIC)
+ break;
+ if (tokens.getTokenType() != PRTokeniser.TK_NAME)
+ tokens.throwError("Dictionary key is not a name.");
+ PdfName name = new PdfName(tokens.getStringValue(), false);
+ PdfObject obj = readPRObject();
+ int type = obj.type();
+ if (-type == PRTokeniser.TK_END_DIC)
+ tokens.throwError("Unexpected '>>'");
+ if (-type == PRTokeniser.TK_END_ARRAY)
+ tokens.throwError("Unexpected ']'");
+ dic.put(name, obj);
+ }
+ return dic;
+ }
+
+ protected PdfArray readArray() throws IOException {
+ PdfArray array = new PdfArray();
+ while (true) {
+ PdfObject obj = readPRObject();
+ int type = obj.type();
+ if (-type == PRTokeniser.TK_END_ARRAY)
+ break;
+ if (-type == PRTokeniser.TK_END_DIC)
+ tokens.throwError("Unexpected '>>'");
+ array.add(obj);
+ }
+ return array;
+ }
+
+ protected PdfObject readPRObject() throws IOException {
+ tokens.nextValidToken();
+ int type = tokens.getTokenType();
+ switch (type) {
+ case PRTokeniser.TK_START_DIC: {
+ PdfDictionary dic = readDictionary();
+ int pos = tokens.getFilePointer();
+ // be careful in the trailer. May not be a "next" token.
+ if (tokens.nextToken() && tokens.getStringValue().equals("stream")) {
+ int ch = tokens.read();
+ if (ch != '\n')
+ ch = tokens.read();
+ if (ch != '\n')
+ tokens.backOnePosition(ch);
+ PRStream stream = new PRStream(this, tokens.getFilePointer());
+ stream.putAll(dic);
+ stream.setObjNum(objNum, objGen);
+ return stream;
+ }
+ else {
+ tokens.seek(pos);
+ return dic;
+ }
+ }
+ case PRTokeniser.TK_START_ARRAY:
+ return readArray();
+ case PRTokeniser.TK_NUMBER:
+ return new PdfNumber(tokens.getStringValue());
+ case PRTokeniser.TK_STRING:
+ PdfString str = new PdfString(tokens.getStringValue(), null).setHexWriting(tokens.isHexString());
+ str.setObjNum(objNum, objGen);
+ if (strings != null)
+ strings.add(str);
+ return str;
+ case PRTokeniser.TK_NAME:
+ return new PdfName(tokens.getStringValue(), false);
+ case PRTokeniser.TK_REF:
+ int num = tokens.getReference();
+ PRIndirectReference ref = new PRIndirectReference(this, num, tokens.getGeneration());
+ if (visited != null && !visited[num]) {
+ visited[num] = true;
+ newHits.put(num, 1);
+ }
+ return ref;
+ default:
+ return new PdfLiteral(-type, tokens.getStringValue());
+ }
+ }
+
+ /** Decodes a stream that has the FlateDecode filter.
+ * @param in the input data
+ * @return the decoded data
+ */
+ public static byte[] FlateDecode(byte in[]) {
+ byte b[] = FlateDecode(in, true);
+ if (b == null)
+ return FlateDecode(in, false);
+ return b;
+ }
+
+ /**
+ * @param in
+ * @param dicPar
+ * @return a byte array
+ */
+ public static byte[] decodePredictor(byte in[], PdfObject dicPar) {
+ if (dicPar == null || !dicPar.isDictionary())
+ return in;
+ PdfDictionary dic = (PdfDictionary)dicPar;
+ PdfObject obj = getPdfObject(dic.get(PdfName.PREDICTOR));
+ if (obj == null || !obj.isNumber())
+ return in;
+ int predictor = ((PdfNumber)obj).intValue();
+ if (predictor < 10)
+ return in;
+ int width = 1;
+ obj = getPdfObject(dic.get(PdfName.COLUMNS));
+ if (obj != null && obj.isNumber())
+ width = ((PdfNumber)obj).intValue();
+ int colors = 1;
+ obj = getPdfObject(dic.get(PdfName.COLORS));
+ if (obj != null && obj.isNumber())
+ colors = ((PdfNumber)obj).intValue();
+ int bpc = 8;
+ obj = getPdfObject(dic.get(PdfName.BITSPERCOMPONENT));
+ if (obj != null && obj.isNumber())
+ bpc = ((PdfNumber)obj).intValue();
+ DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(in));
+ ByteArrayOutputStream fout = new ByteArrayOutputStream(in.length);
+ int bytesPerPixel = colors * bpc / 8;
+ int bytesPerRow = (colors*width*bpc + 7)/8;
+ byte[] curr = new byte[bytesPerRow];
+ byte[] prior = new byte[bytesPerRow];
+
+ // Decode the (sub)image row-by-row
+ while (true) {
+ // Read the filter type byte and a row of data
+ int filter = 0;
+ try {
+ filter = dataStream.read();
+ if (filter < 0) {
+ return fout.toByteArray();
+ }
+ dataStream.readFully(curr, 0, bytesPerRow);
+ } catch (Exception e) {
+ return fout.toByteArray();
+ }
+
+ switch (filter) {
+ case 0: //PNG_FILTER_NONE
+ break;
+ case 1: //PNG_FILTER_SUB
+ for (int i = bytesPerPixel; i < bytesPerRow; i++) {
+ curr[i] += curr[i - bytesPerPixel];
+ }
+ break;
+ case 2: //PNG_FILTER_UP
+ for (int i = 0; i < bytesPerRow; i++) {
+ curr[i] += prior[i];
+ }
+ break;
+ case 3: //PNG_FILTER_AVERAGE
+ for (int i = 0; i < bytesPerPixel; i++) {
+ curr[i] += prior[i] / 2;
+ }
+ for (int i = bytesPerPixel; i < bytesPerRow; i++) {
+ curr[i] += ((curr[i - bytesPerPixel] & 0xff) + (prior[i] & 0xff))/2;
+ }
+ break;
+ case 4: //PNG_FILTER_PAETH
+ for (int i = 0; i < bytesPerPixel; i++) {
+ curr[i] += prior[i];
+ }
+
+ for (int i = bytesPerPixel; i < bytesPerRow; i++) {
+ int a = curr[i - bytesPerPixel] & 0xff;
+ int b = prior[i] & 0xff;
+ int c = prior[i - bytesPerPixel] & 0xff;
+
+ int p = a + b - c;
+ int pa = Math.abs(p - a);
+ int pb = Math.abs(p - b);
+ int pc = Math.abs(p - c);
+
+ int ret;
+
+ if ((pa <= pb) && (pa <= pc)) {
+ ret = a;
+ } else if (pb <= pc) {
+ ret = b;
+ } else {
+ ret = c;
+ }
+ curr[i] += (byte)(ret);
+ }
+ break;
+ default:
+ // Error -- uknown filter type
+ throw new RuntimeException("PNG filter unknown.");
+ }
+ try {
+ fout.write(curr);
+ }
+ catch (IOException ioe) {
+ // Never happens
+ }
+
+ // Swap curr and prior
+ byte[] tmp = prior;
+ prior = curr;
+ curr = tmp;
+ }
+ }
+
+ /** A helper to FlateDecode.
+ * @param in the input data
+ * @param strict true
to read a correct stream. false
+ * to try to read a corrupted stream
+ * @return the decoded data
+ */
+ public static byte[] FlateDecode(byte in[], boolean strict) {
+ ByteArrayInputStream stream = new ByteArrayInputStream(in);
+ InflaterInputStream zip = new InflaterInputStream(stream);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte b[] = new byte[strict ? 4092 : 1];
+ try {
+ int n;
+ while ((n = zip.read(b)) >= 0) {
+ out.write(b, 0, n);
+ }
+ zip.close();
+ out.close();
+ return out.toByteArray();
+ }
+ catch (Exception e) {
+ if (strict)
+ return null;
+ return out.toByteArray();
+ }
+ }
+
+ /** Decodes a stream that has the ASCIIHexDecode filter.
+ * @param in the input data
+ * @return the decoded data
+ */
+ public static byte[] ASCIIHexDecode(byte in[]) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ boolean first = true;
+ int n1 = 0;
+ for (int k = 0; k < in.length; ++k) {
+ int ch = in[k] & 0xff;
+ if (ch == '>')
+ break;
+ if (PRTokeniser.isWhitespace(ch))
+ continue;
+ int n = PRTokeniser.getHex(ch);
+ if (n == -1)
+ throw new RuntimeException("Illegal character in ASCIIHexDecode.");
+ if (first)
+ n1 = n;
+ else
+ out.write((byte)((n1 << 4) + n));
+ first = !first;
+ }
+ if (!first)
+ out.write((byte)(n1 << 4));
+ return out.toByteArray();
+ }
+
+ /** Decodes a stream that has the ASCII85Decode filter.
+ * @param in the input data
+ * @return the decoded data
+ */
+ public static byte[] ASCII85Decode(byte in[]) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int state = 0;
+ int chn[] = new int[5];
+ for (int k = 0; k < in.length; ++k) {
+ int ch = in[k] & 0xff;
+ if (ch == '~')
+ break;
+ if (PRTokeniser.isWhitespace(ch))
+ continue;
+ if (ch == 'z' && state == 0) {
+ out.write(0);
+ out.write(0);
+ out.write(0);
+ out.write(0);
+ continue;
+ }
+ if (ch < '!' || ch > 'u')
+ throw new RuntimeException("Illegal character in ASCII85Decode.");
+ chn[state] = ch - '!';
+ ++state;
+ if (state == 5) {
+ state = 0;
+ int r = 0;
+ for (int j = 0; j < 5; ++j)
+ r = r * 85 + chn[j];
+ out.write((byte)(r >> 24));
+ out.write((byte)(r >> 16));
+ out.write((byte)(r >> 8));
+ out.write((byte)r);
+ }
+ }
+ int r = 0;
+ if (state == 1)
+ throw new RuntimeException("Illegal length in ASCII85Decode.");
+ if (state == 2) {
+ r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85;
+ out.write((byte)(r >> 24));
+ }
+ else if (state == 3) {
+ r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85 + chn[2] * 85 * 85;
+ out.write((byte)(r >> 24));
+ out.write((byte)(r >> 16));
+ }
+ else if (state == 4) {
+ r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85 + chn[2] * 85 * 85 + chn[3] * 85 ;
+ out.write((byte)(r >> 24));
+ out.write((byte)(r >> 16));
+ out.write((byte)(r >> 8));
+ }
+ return out.toByteArray();
+ }
+
+ /** Decodes a stream that has the LZWDecode filter.
+ * @param in the input data
+ * @return the decoded data
+ */
+ public static byte[] LZWDecode(byte in[]) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ LZWDecoder lzw = new LZWDecoder();
+ lzw.decode(in, out);
+ return out.toByteArray();
+ }
+
+ /** Checks if the document had errors and was rebuilt.
+ * @return true if rebuilt.
+ *
+ */
+ public boolean isRebuilt() {
+ return this.rebuilt;
+ }
+
+ /** Gets the dictionary that represents a page.
+ * @param pageNum the page number. 1 is the first
+ * @return the page dictionary
+ */
+ public PdfDictionary getPageN(int pageNum) {
+ PdfDictionary dic = pageRefs.getPageN(pageNum);
+ if (dic == null)
+ return null;
+ if (appendable)
+ dic.setIndRef(pageRefs.getPageOrigRef(pageNum));
+ return dic;
+ }
+
+ /**
+ * @param pageNum
+ * @return a Dictionary object
+ */
+ public PdfDictionary getPageNRelease(int pageNum) {
+ PdfDictionary dic = getPageN(pageNum);
+ pageRefs.releasePage(pageNum);
+ return dic;
+ }
+
+ /**
+ * @param pageNum
+ */
+ public void releasePage(int pageNum) {
+ pageRefs.releasePage(pageNum);
+ }
+
+ /**
+ *
+ */
+ public void resetReleasePage() {
+ pageRefs.resetReleasePage();
+ }
+
+ /** Gets the page reference to this page.
+ * @param pageNum the page number. 1 is the first
+ * @return the page reference
+ */
+ public PRIndirectReference getPageOrigRef(int pageNum) {
+ return pageRefs.getPageOrigRef(pageNum);
+ }
+
+ /** Gets the contents of the page.
+ * @param pageNum the page number. 1 is the first
+ * @param file the location of the PDF document
+ * @throws IOException on error
+ * @return the content
+ */
+ public byte[] getPageContent(int pageNum, RandomAccessFileOrArray file) throws IOException{
+ PdfDictionary page = getPageNRelease(pageNum);
+ if (page == null)
+ return null;
+ PdfObject contents = getPdfObjectRelease(page.get(PdfName.CONTENTS));
+ if (contents == null)
+ return new byte[0];
+ ByteArrayOutputStream bout = null;
+ if (contents.isStream()) {
+ return getStreamBytes((PRStream)contents, file);
+ }
+ else if (contents.isArray()) {
+ PdfArray array = (PdfArray)contents;
+ ArrayList list = array.getArrayList();
+ bout = new ByteArrayOutputStream();
+ for (int k = 0; k < list.size(); ++k) {
+ PdfObject item = getPdfObjectRelease((PdfObject)list.get(k));
+ if (item == null || !item.isStream())
+ continue;
+ byte[] b = getStreamBytes((PRStream)item, file);
+ bout.write(b);
+ if (k != list.size() - 1)
+ bout.write('\n');
+ }
+ return bout.toByteArray();
+ }
+ else
+ return new byte[0];
+ }
+
+ /** Gets the contents of the page.
+ * @param pageNum the page number. 1 is the first
+ * @throws IOException on error
+ * @return the content
+ */
+ public byte[] getPageContent(int pageNum) throws IOException{
+ RandomAccessFileOrArray rf = getSafeFile();
+ try {
+ rf.reOpen();
+ return getPageContent(pageNum, rf);
+ }
+ finally {
+ try{rf.close();}catch(Exception e){}
+ }
+ }
+
+ protected void killXref(PdfObject obj) {
+ if (obj == null)
+ return;
+ if ((obj instanceof PdfIndirectReference) && !obj.isIndirect())
+ return;
+ switch (obj.type()) {
+ case PdfObject.INDIRECT: {
+ int xr = ((PRIndirectReference)obj).getNumber();
+ obj = (PdfObject)xrefObj.get(xr);
+ xrefObj.set(xr, null);
+ freeXref = xr;
+ killXref(obj);
+ break;
+ }
+ case PdfObject.ARRAY: {
+ ArrayList t = ((PdfArray)obj).getArrayList();
+ for (int i = 0; i < t.size(); ++i)
+ killXref((PdfObject)t.get(i));
+ break;
+ }
+ case PdfObject.STREAM:
+ case PdfObject.DICTIONARY: {
+ PdfDictionary dic = (PdfDictionary)obj;
+ for (Iterator i = dic.getKeys().iterator(); i.hasNext();){
+ killXref(dic.get((PdfName)i.next()));
+ }
+ break;
+ }
+ }
+ }
+
+ /** Sets the contents of the page.
+ * @param content the new page content
+ * @param pageNum the page number. 1 is the first
+ * @throws IOException on error
+ */
+ public void setPageContent(int pageNum, byte content[]) throws IOException{
+ PdfDictionary page = getPageN(pageNum);
+ if (page == null)
+ return;
+ PdfObject contents = page.get(PdfName.CONTENTS);
+ freeXref = -1;
+ killXref(contents);
+ if (freeXref == -1) {
+ xrefObj.add(null);
+ freeXref = xrefObj.size() - 1;
+ }
+ page.put(PdfName.CONTENTS, new PRIndirectReference(this, freeXref));
+ xrefObj.set(freeXref, new PRStream(this, content));
+ }
+
+ /** Get the content from a stream applying the required filters.
+ * @param stream the stream
+ * @param file the location where the stream is
+ * @throws IOException on error
+ * @return the stream content
+ */
+ public static byte[] getStreamBytes(PRStream stream, RandomAccessFileOrArray file) throws IOException {
+ PdfObject filter = getPdfObjectRelease(stream.get(PdfName.FILTER));
+ byte[] b = getStreamBytesRaw(stream, file);
+ ArrayList filters = new ArrayList();
+ if (filter != null) {
+ if (filter.isName())
+ filters.add(filter);
+ else if (filter.isArray())
+ filters = ((PdfArray)filter).getArrayList();
+ }
+ ArrayList dp = new ArrayList();
+ PdfObject dpo = getPdfObjectRelease(stream.get(PdfName.DECODEPARMS));
+ if (dpo == null || (!dpo.isDictionary() && !dpo.isArray()))
+ dpo = getPdfObjectRelease(stream.get(PdfName.DP));
+ if (dpo != null) {
+ if (dpo.isDictionary())
+ dp.add(dpo);
+ else if (dpo.isArray())
+ dp = ((PdfArray)dpo).getArrayList();
+ }
+ String name;
+ for (int j = 0; j < filters.size(); ++j) {
+ name = ((PdfName)PdfReader.getPdfObjectRelease((PdfObject)filters.get(j))).toString();
+ if (name.equals("/FlateDecode") || name.equals("/Fl")) {
+ b = PdfReader.FlateDecode(b);
+ PdfObject dicParam = null;
+ if (j < dp.size()) {
+ dicParam = (PdfObject)dp.get(j);
+ b = decodePredictor(b, dicParam);
+ }
+ }
+ else if (name.equals("/ASCIIHexDecode") || name.equals("/AHx"))
+ b = PdfReader.ASCIIHexDecode(b);
+ else if (name.equals("/ASCII85Decode") || name.equals("/A85"))
+ b = PdfReader.ASCII85Decode(b);
+ else if (name.equals("/LZWDecode")) {
+ b = PdfReader.LZWDecode(b);
+ PdfObject dicParam = null;
+ if (j < dp.size()) {
+ dicParam = (PdfObject)dp.get(j);
+ b = decodePredictor(b, dicParam);
+ }
+ }
+ else
+ throw new IOException("The filter " + name + " is not supported.");
+ }
+ return b;
+ }
+
+ /** Get the content from a stream applying the required filters.
+ * @param stream the stream
+ * @throws IOException on error
+ * @return the stream content
+ */
+ public static byte[] getStreamBytes(PRStream stream) throws IOException {
+ RandomAccessFileOrArray rf = stream.getReader().getSafeFile();
+ try {
+ rf.reOpen();
+ return PdfReader.getStreamBytes(stream, rf);
+ }
+ finally {
+ try{rf.close();}catch(Exception e){}
+ }
+ }
+
+ /** Get the content from a stream as it is without applying any filter.
+ * @param stream the stream
+ * @param file the location where the stream is
+ * @throws IOException on error
+ * @return the stream content
+ */
+ public static byte[] getStreamBytesRaw(PRStream stream, RandomAccessFileOrArray file) throws IOException {
+ PdfReader reader = stream.getReader();
+ byte b[];
+ if (stream.getOffset() < 0)
+ b = stream.getBytes();
+ else {
+ b = new byte[stream.getLength()];
+ file.seek(stream.getOffset());
+ file.readFully(b);
+ PdfEncryption decrypt = reader.getDecrypt();
+ if (decrypt != null) {
+ decrypt.setHashKey(stream.getObjNum(), stream.getObjGen());
+ decrypt.prepareKey();
+ decrypt.encryptRC4(b);
+ }
+ }
+ return b;
+ }
+
+ /** Get the content from a stream as it is without applying any filter.
+ * @param stream the stream
+ * @throws IOException on error
+ * @return the stream content
+ */
+ public static byte[] getStreamBytesRaw(PRStream stream) throws IOException {
+ RandomAccessFileOrArray rf = stream.getReader().getSafeFile();
+ try {
+ rf.reOpen();
+ return PdfReader.getStreamBytesRaw(stream, rf);
+ }
+ finally {
+ try{rf.close();}catch(Exception e){}
+ }
+ }
+
+ /** Eliminates shared streams if they exist. */
+ public void eliminateSharedStreams() {
+ if (!sharedStreams)
+ return;
+ sharedStreams = false;
+ if (pageRefs.size() == 1)
+ return;
+ ArrayList newRefs = new ArrayList();
+ ArrayList newStreams = new ArrayList();
+ IntHashtable visited = new IntHashtable();
+ for (int k = 1; k <= pageRefs.size(); ++k) {
+ PdfDictionary page = pageRefs.getPageN(k);
+ if (page == null)
+ continue;
+ PdfObject contents = getPdfObject(page.get(PdfName.CONTENTS));
+ if (contents == null)
+ continue;
+ if (contents.isStream()) {
+ PRIndirectReference ref = (PRIndirectReference)page.get(PdfName.CONTENTS);
+ if (visited.containsKey(ref.getNumber())) {
+ // need to duplicate
+ newRefs.add(ref);
+ newStreams.add(new PRStream((PRStream)contents, null));
+ }
+ else
+ visited.put(ref.getNumber(), 1);
+ }
+ else if (contents.isArray()) {
+ PdfArray array = (PdfArray)contents;
+ ArrayList list = array.getArrayList();
+ for (int j = 0; j < list.size(); ++j) {
+ PRIndirectReference ref = (PRIndirectReference)list.get(j);
+ if (visited.containsKey(ref.getNumber())) {
+ // need to duplicate
+ newRefs.add(ref);
+ newStreams.add(new PRStream((PRStream)getPdfObject(ref), null));
+ }
+ else
+ visited.put(ref.getNumber(), 1);
+ }
+ }
+ }
+ if (newStreams.size() == 0)
+ return;
+ for (int k = 0; k < newStreams.size(); ++k) {
+ xrefObj.add(newStreams.get(k));
+ PRIndirectReference ref = (PRIndirectReference)newRefs.get(k);
+ ref.setNumber(xrefObj.size() - 1, 0);
+ }
+ }
+
+ /** Checks if the document was changed.
+ * @return true
if the document was changed,
+ * false
otherwise
+ */
+ public boolean isTampered() {
+ return tampered;
+ }
+
+ /**
+ * Sets the tampered state. A tampered PdfReader cannot be reused in PdfStamper.
+ * @param tampered the tampered state
+ */
+ public void setTampered(boolean tampered) {
+ this.tampered = tampered;
+ }
+
+ /** Gets the XML metadata.
+ * @throws IOException on error
+ * @return the XML metadata
+ */
+ public byte[] getMetadata() throws IOException {
+ PdfObject obj = getPdfObject(catalog.get(PdfName.METADATA));
+ if (!(obj instanceof PRStream))
+ return null;
+ RandomAccessFileOrArray rf = getSafeFile();
+ byte b[] = null;
+ try {
+ rf.reOpen();
+ b = getStreamBytes((PRStream)obj, rf);
+ }
+ finally {
+ try {
+ rf.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ return b;
+ }
+
+ /**
+ * Gets the byte address of the last xref table.
+ * @return the byte address of the last xref table
+ */
+ public int getLastXref() {
+ return lastXref;
+ }
+
+ /**
+ * Gets the number of xref objects.
+ * @return the number of xref objects
+ */
+ public int getXrefSize() {
+ return xrefObj.size();
+ }
+
+ /**
+ * Gets the byte address of the %%EOF marker.
+ * @return the byte address of the %%EOF marker
+ */
+ public int getEofPos() {
+ return eofPos;
+ }
+
+ /**
+ * Gets the PDF version. Only the last version char is returned. For example
+ * version 1.4 is returned as '4'.
+ * @return the PDF version
+ */
+ public char getPdfVersion() {
+ return pdfVersion;
+ }
+
+ /**
+ * Returns true
if the PDF is encrypted.
+ * @return true
if the PDF is encrypted
+ */
+ public boolean isEncrypted() {
+ return encrypted;
+ }
+
+ /**
+ * Gets the encryption permissions. It can be used directly in
+ * PdfWriter.setEncryption()
.
+ * @return the encryption permissions
+ */
+ public int getPermissions() {
+ return pValue;
+ }
+
+ /**
+ * Returns true
if the PDF has a 128 bit key encryption.
+ * @return true
if the PDF has a 128 bit key encryption
+ */
+ public boolean is128Key() {
+ return rValue == 3;
+ }
+
+ /**
+ * Gets the trailer dictionary
+ * @return the trailer dictionary
+ */
+ public PdfDictionary getTrailer() {
+ return trailer;
+ }
+
+ PdfEncryption getDecrypt() {
+ return decrypt;
+ }
+
+ static boolean equalsn(byte a1[], byte a2[]) {
+ int length = a2.length;
+ for (int k = 0; k < length; ++k) {
+ if (a1[k] != a2[k])
+ return false;
+ }
+ return true;
+ }
+
+ static boolean existsName(PdfDictionary dic, PdfName key, PdfName value) {
+ PdfObject type = getPdfObjectRelease(dic.get(key));
+ if (type == null || !type.isName())
+ return false;
+ PdfName name = (PdfName)type;
+ return name.equals(value);
+ }
+
+ static String getFontName(PdfDictionary dic) {
+ PdfObject type = getPdfObjectRelease(dic.get(PdfName.BASEFONT));
+ if (type == null || !type.isName())
+ return null;
+ return PdfName.decodeName(type.toString());
+ }
+
+ static String getSubsetPrefix(PdfDictionary dic) {
+ String s = getFontName(dic);
+ if (s == null)
+ return null;
+ if (s.length() < 8 || s.charAt(6) != '+')
+ return null;
+ for (int k = 0; k < 6; ++k) {
+ char c = s.charAt(k);
+ if (c < 'A' || c > 'Z')
+ return null;
+ }
+ return s;
+ }
+
+ /** Finds all the font subsets and changes the prefixes to some
+ * random values.
+ * @return the number of font subsets altered
+ */
+ public int shuffleSubsetNames() {
+ int total = 0;
+ for (int k = 1; k < xrefObj.size(); ++k) {
+ PdfObject obj = getPdfObjectRelease(k);
+ if (obj == null || !obj.isDictionary())
+ continue;
+ PdfDictionary dic = (PdfDictionary)obj;
+ if (!existsName(dic, PdfName.TYPE, PdfName.FONT))
+ continue;
+ if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE1)
+ || existsName(dic, PdfName.SUBTYPE, PdfName.MMTYPE1)
+ || existsName(dic, PdfName.SUBTYPE, PdfName.TRUETYPE)) {
+ String s = getSubsetPrefix(dic);
+ if (s == null)
+ continue;
+ String ns = BaseFont.createSubsetPrefix() + s.substring(7);
+ PdfName newName = new PdfName(ns);
+ dic.put(PdfName.BASEFONT, newName);
+ setXrefPartialObject(k, dic);
+ ++total;
+ PdfDictionary fd = (PdfDictionary)getPdfObject(dic.get(PdfName.FONTDESCRIPTOR));
+ if (fd == null)
+ continue;
+ fd.put(PdfName.FONTNAME, newName);
+ }
+ else if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE0)) {
+ String s = getSubsetPrefix(dic);
+ PdfArray arr = (PdfArray)getPdfObject(dic.get(PdfName.DESCENDANTFONTS));
+ if (arr == null)
+ continue;
+ ArrayList list = arr.getArrayList();
+ if (list.size() == 0)
+ continue;
+ PdfDictionary desc = (PdfDictionary)getPdfObject((PdfObject)list.get(0));
+ String sde = getSubsetPrefix(desc);
+ if (sde == null)
+ continue;
+ String ns = BaseFont.createSubsetPrefix();
+ if (s != null)
+ dic.put(PdfName.BASEFONT, new PdfName(ns + s.substring(7)));
+ setXrefPartialObject(k, dic);
+ PdfName newName = new PdfName(ns + sde.substring(7));
+ desc.put(PdfName.BASEFONT, newName);
+ ++total;
+ PdfDictionary fd = (PdfDictionary)getPdfObject(desc.get(PdfName.FONTDESCRIPTOR));
+ if (fd == null)
+ continue;
+ fd.put(PdfName.FONTNAME, newName);
+ }
+ }
+ return total;
+ }
+
+ /** Finds all the fonts not subset but embedded and marks them as subset.
+ * @return the number of fonts altered
+ */
+ public int createFakeFontSubsets() {
+ int total = 0;
+ for (int k = 1; k < xrefObj.size(); ++k) {
+ PdfObject obj = getPdfObjectRelease(k);
+ if (obj == null || !obj.isDictionary())
+ continue;
+ PdfDictionary dic = (PdfDictionary)obj;
+ if (!existsName(dic, PdfName.TYPE, PdfName.FONT))
+ continue;
+ if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE1)
+ || existsName(dic, PdfName.SUBTYPE, PdfName.MMTYPE1)
+ || existsName(dic, PdfName.SUBTYPE, PdfName.TRUETYPE)) {
+ String s = getSubsetPrefix(dic);
+ if (s != null)
+ continue;
+ s = getFontName(dic);
+ if (s == null)
+ continue;
+ String ns = BaseFont.createSubsetPrefix() + s;
+ PdfDictionary fd = (PdfDictionary)getPdfObjectRelease(dic.get(PdfName.FONTDESCRIPTOR));
+ if (fd == null)
+ continue;
+ if (fd.get(PdfName.FONTFILE) == null && fd.get(PdfName.FONTFILE2) == null
+ && fd.get(PdfName.FONTFILE3) == null)
+ continue;
+ fd = (PdfDictionary)getPdfObject(dic.get(PdfName.FONTDESCRIPTOR));
+ PdfName newName = new PdfName(ns);
+ dic.put(PdfName.BASEFONT, newName);
+ fd.put(PdfName.FONTNAME, newName);
+ setXrefPartialObject(k, dic);
+ ++total;
+ }
+ }
+ return total;
+ }
+
+ private static PdfArray getNameArray(PdfObject obj) {
+ if (obj == null)
+ return null;
+ obj = getPdfObjectRelease(obj);
+ if (obj.isArray())
+ return (PdfArray)obj;
+ else if (obj.isDictionary()) {
+ PdfObject arr2 = getPdfObjectRelease(((PdfDictionary)obj).get(PdfName.D));
+ if (arr2 != null && arr2.isArray())
+ return (PdfArray)arr2;
+ }
+ return null;
+ }
+
+ /**
+ * Gets all the named destinations as an HashMap
. The key is the name
+ * and the value is the destinations array.
+ * @return gets all the named destinations
+ */
+ public HashMap getNamedDestination() {
+ HashMap names = getNamedDestinationFromNames();
+ names.putAll(getNamedDestinationFromStrings());
+ return names;
+ }
+
+ /**
+ * Gets the named destinations from the /Dests key in the catalog as an HashMap
. The key is the name
+ * and the value is the destinations array.
+ * @return gets the named destinations
+ */
+ public HashMap getNamedDestinationFromNames() {
+ HashMap names = new HashMap();
+ if (catalog.get(PdfName.DESTS) != null) {
+ PdfDictionary dic = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.DESTS));
+ Set keys = dic.getKeys();
+ for (Iterator it = keys.iterator(); it.hasNext();) {
+ PdfName key = (PdfName)it.next();
+ String name = PdfName.decodeName(key.toString());
+ PdfArray arr = getNameArray(dic.get(key));
+ if (arr != null)
+ names.put(name, arr);
+ }
+ }
+ return names;
+ }
+
+ /**
+ * Gets the named destinations from the /Names key in the catalog as an HashMap
. The key is the name
+ * and the value is the destinations array.
+ * @return gets the named destinations
+ */
+ public HashMap getNamedDestinationFromStrings() {
+ if (catalog.get(PdfName.NAMES) != null) {
+ PdfDictionary dic = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.NAMES));
+ dic = (PdfDictionary)getPdfObjectRelease(dic.get(PdfName.DESTS));
+ if (dic != null) {
+ HashMap names = PdfNameTree.readTree(dic);
+ for (Iterator it = names.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry)it.next();
+ PdfArray arr = getNameArray((PdfObject)entry.getValue());
+ if (arr != null)
+ entry.setValue(arr);
+ else
+ it.remove();
+ }
+ return names;
+ }
+ }
+ return new HashMap();
+ }
+
+ private boolean replaceNamedDestination(PdfObject obj, HashMap names) {
+ obj = getPdfObject(obj);
+ int objIdx = lastXrefPartial;
+ releaseLastXrefPartial();
+ if (obj != null && obj.isDictionary()) {
+ PdfObject ob2 = getPdfObjectRelease(((PdfDictionary)obj).get(PdfName.DEST));
+ String name = null;
+ if (ob2 != null) {
+ if (ob2.isName())
+ name = PdfName.decodeName(ob2.toString());
+ else if (ob2.isString())
+ name = ob2.toString();
+ PdfArray dest = (PdfArray)names.get(name);
+ if (dest != null) {
+ ((PdfDictionary)obj).put(PdfName.DEST, dest);
+ setXrefPartialObject(objIdx, obj);
+ return true;
+ }
+ }
+ else if ((ob2 = getPdfObject(((PdfDictionary)obj).get(PdfName.A))) != null) {
+ int obj2Idx = lastXrefPartial;
+ releaseLastXrefPartial();
+ PdfDictionary dic = (PdfDictionary)ob2;
+ PdfName type = (PdfName)getPdfObjectRelease(dic.get(PdfName.S));
+ if (PdfName.GOTO.equals(type)) {
+ PdfObject ob3 = getPdfObjectRelease(dic.get(PdfName.D));
+ if (ob3.isName())
+ name = PdfName.decodeName(ob3.toString());
+ else if (ob3.isString())
+ name = ob3.toString();
+ PdfArray dest = (PdfArray)names.get(name);
+ if (dest != null) {
+ dic.put(PdfName.D, dest);
+ setXrefPartialObject(obj2Idx, ob2);
+ setXrefPartialObject(objIdx, obj);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes all the fields from the document.
+ */
+ public void removeFields() {
+ pageRefs.resetReleasePage();
+ for (int k = 1; k <= pageRefs.size(); ++k) {
+ PdfDictionary page = pageRefs.getPageN(k);
+ PdfArray annots = (PdfArray)getPdfObject(page.get(PdfName.ANNOTS));
+ if (annots == null) {
+ pageRefs.releasePage(k);
+ continue;
+ }
+ ArrayList arr = annots.getArrayList();
+ for (int j = 0; j < arr.size(); ++j) {
+ PdfDictionary annot = (PdfDictionary)getPdfObjectRelease((PdfObject)arr.get(j));
+ if (PdfName.WIDGET.equals(annot.get(PdfName.SUBTYPE)))
+ arr.remove(j--);
+ }
+ if (arr.isEmpty())
+ page.remove(PdfName.ANNOTS);
+ else
+ pageRefs.releasePage(k);
+ }
+ catalog.remove(PdfName.ACROFORM);
+ pageRefs.resetReleasePage();
+ }
+
+ /**
+ * Removes all the annotations and fields from the document.
+ */
+ public void removeAnnotations() {
+ pageRefs.resetReleasePage();
+ for (int k = 1; k <= pageRefs.size(); ++k) {
+ PdfDictionary page = pageRefs.getPageN(k);
+ if (page.get(PdfName.ANNOTS) == null)
+ pageRefs.releasePage(k);
+ else
+ page.remove(PdfName.ANNOTS);
+ }
+ catalog.remove(PdfName.ACROFORM);
+ pageRefs.resetReleasePage();
+ }
+
+ private void iterateBookmarks(PdfObject outlineRef, HashMap names) {
+ while (outlineRef != null) {
+ replaceNamedDestination(outlineRef, names);
+ PdfDictionary outline = (PdfDictionary)getPdfObjectRelease(outlineRef);
+ PdfObject first = outline.get(PdfName.FIRST);
+ if (first != null) {
+ iterateBookmarks(first, names);
+ }
+ outlineRef = outline.get(PdfName.NEXT);
+ }
+ }
+
+ /** Replaces all the local named links with the actual destinations. */
+ public void consolidateNamedDestinations() {
+ if (consolidateNamedDestinations)
+ return;
+ consolidateNamedDestinations = true;
+ HashMap names = getNamedDestination();
+ if (names.size() == 0)
+ return;
+ for (int k = 1; k <= pageRefs.size(); ++k) {
+ PdfDictionary page = pageRefs.getPageN(k);
+ PdfObject annotsRef;
+ PdfArray annots = (PdfArray)getPdfObject(annotsRef = page.get(PdfName.ANNOTS));
+ int annotIdx = lastXrefPartial;
+ releaseLastXrefPartial();
+ if (annots == null) {
+ pageRefs.releasePage(k);
+ continue;
+ }
+ ArrayList list = annots.getArrayList();
+ boolean commitAnnots = false;
+ for (int an = 0; an < list.size(); ++an) {
+ PdfObject objRef = (PdfObject)list.get(an);
+ if (replaceNamedDestination(objRef, names) && !objRef.isIndirect())
+ commitAnnots = true;
+ }
+ if (commitAnnots)
+ setXrefPartialObject(annotIdx, annots);
+ if (!commitAnnots || annotsRef.isIndirect())
+ pageRefs.releasePage(k);
+ }
+ PdfDictionary outlines = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.OUTLINES));
+ if (outlines == null)
+ return;
+ iterateBookmarks(outlines.get(PdfName.FIRST), names);
+ }
+
+ protected static PdfDictionary duplicatePdfDictionary(PdfDictionary original, PdfDictionary copy, PdfReader newReader) {
+ if (copy == null)
+ copy = new PdfDictionary();
+ for (Iterator it = original.getKeys().iterator(); it.hasNext();) {
+ PdfName key = (PdfName)it.next();
+ copy.put(key, duplicatePdfObject(original.get(key), newReader));
+ }
+ return copy;
+ }
+
+ protected static PdfObject duplicatePdfObject(PdfObject original, PdfReader newReader) {
+ if (original == null)
+ return null;
+ switch (original.type()) {
+ case PdfObject.DICTIONARY: {
+ return duplicatePdfDictionary((PdfDictionary)original, null, newReader);
+ }
+ case PdfObject.STREAM: {
+ PRStream org = (PRStream)original;
+ PRStream stream = new PRStream(org, null, newReader);
+ duplicatePdfDictionary(org, stream, newReader);
+ return stream;
+ }
+ case PdfObject.ARRAY: {
+ ArrayList list = ((PdfArray)original).getArrayList();
+ PdfArray arr = new PdfArray();
+ for (Iterator it = list.iterator(); it.hasNext();) {
+ arr.add(duplicatePdfObject((PdfObject)it.next(), newReader));
+ }
+ return arr;
+ }
+ case PdfObject.INDIRECT: {
+ PRIndirectReference org = (PRIndirectReference)original;
+ return new PRIndirectReference(newReader, org.getNumber(), org.getGeneration());
+ }
+ default:
+ return original;
+ }
+ }
+
+ /**
+ * Closes the reader
+ */
+ public void close() {
+ if (!partial)
+ return;
+ try {
+ tokens.close();
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ protected void removeUnusedNode(PdfObject obj, boolean hits[]) {
+ if (obj == null)
+ return;
+ switch (obj.type()) {
+ case PdfObject.DICTIONARY:
+ case PdfObject.STREAM: {
+ PdfDictionary dic = (PdfDictionary)obj;
+ for (Iterator it = dic.getKeys().iterator(); it.hasNext();) {
+ PdfName key = (PdfName)it.next();
+ PdfObject v = dic.get(key);
+ if (v.isIndirect()) {
+ int num = ((PRIndirectReference)v).getNumber();
+ if (num >= xrefObj.size() || (!partial && xrefObj.get(num) == null)) {
+ dic.put(key, PdfNull.PDFNULL);
+ continue;
+ }
+ }
+ removeUnusedNode(v, hits);
+ }
+ break;
+ }
+ case PdfObject.ARRAY: {
+ ArrayList list = ((PdfArray)obj).getArrayList();
+ for (int k = 0; k < list.size(); ++k) {
+ PdfObject v = (PdfObject)list.get(k);
+ if (v.isIndirect()) {
+ int num = ((PRIndirectReference)v).getNumber();
+ if (num >= xrefObj.size() || (!partial && xrefObj.get(num) == null)) {
+ list.set(k, PdfNull.PDFNULL);
+ continue;
+ }
+ }
+ removeUnusedNode(v, hits);
+ }
+ break;
+ }
+ case PdfObject.INDIRECT: {
+ PRIndirectReference ref = (PRIndirectReference)obj;
+ int num = ref.getNumber();
+ if (!hits[num]) {
+ hits[num] = true;
+ removeUnusedNode(getPdfObjectRelease(ref), hits);
+ }
+ }
+ }
+ }
+
+ /** Removes all the unreachable objects.
+ * @return the number of indirect objects removed
+ */
+ public int removeUnusedObjects() {
+ boolean hits[] = new boolean[xrefObj.size()];
+ removeUnusedNode(trailer, hits);
+ int total = 0;
+ if (partial) {
+ for (int k = 1; k < hits.length; ++k) {
+ if (!hits[k]) {
+ xref[k * 2] = -1;
+ xref[k * 2 + 1] = 0;
+ xrefObj.set(k, null);
+ ++total;
+ }
+ }
+ }
+ else {
+ for (int k = 1; k < hits.length; ++k) {
+ if (!hits[k]) {
+ xrefObj.set(k, null);
+ ++total;
+ }
+ }
+ }
+ return total;
+ }
+
+ /** Gets a read-only version of AcroFields
.
+ * @return a read-only version of AcroFields
+ */
+ public AcroFields getAcroFields() {
+ return new AcroFields(this, null);
+ }
+
+ /**
+ * Gets the global document JavaScript.
+ * @param file the document file
+ * @throws IOException on error
+ * @return the global document JavaScript
+ */
+ public String getJavaScript(RandomAccessFileOrArray file) throws IOException {
+ PdfDictionary names = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.NAMES));
+ if (names == null)
+ return null;
+ PdfDictionary js = (PdfDictionary)getPdfObjectRelease(names.get(PdfName.JAVASCRIPT));
+ if (js == null)
+ return null;
+ HashMap jscript = PdfNameTree.readTree(js);
+ String sortedNames[] = new String[jscript.size()];
+ sortedNames = (String[])jscript.keySet().toArray(sortedNames);
+ Arrays.sort(sortedNames, new StringCompare());
+ StringBuffer buf = new StringBuffer();
+ for (int k = 0; k < sortedNames.length; ++k) {
+ PdfDictionary j = (PdfDictionary)getPdfObjectRelease((PdfIndirectReference)jscript.get(sortedNames[k]));
+ if (j == null)
+ continue;
+ PdfObject obj = getPdfObjectRelease(j.get(PdfName.JS));
+ if (obj.isString())
+ buf.append(((PdfString)obj).toUnicodeString()).append('\n');
+ else if (obj.isStream()) {
+ byte bytes[] = getStreamBytes((PRStream)obj, file);
+ if (bytes.length >= 2 && bytes[0] == (byte)254 && bytes[1] == (byte)255)
+ buf.append(PdfEncodings.convertToString(bytes, PdfObject.TEXT_UNICODE));
+ else
+ buf.append(PdfEncodings.convertToString(bytes, PdfObject.TEXT_PDFDOCENCODING));
+ buf.append('\n');
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Gets the global document JavaScript.
+ * @throws IOException on error
+ * @return the global document JavaScript
+ */
+ public String getJavaScript() throws IOException {
+ RandomAccessFileOrArray rf = getSafeFile();
+ try {
+ rf.reOpen();
+ return getJavaScript(rf);
+ }
+ finally {
+ try{rf.close();}catch(Exception e){}
+ }
+ }
+
+ /**
+ * Selects the pages to keep in the document. The pages are described as
+ * ranges. The page ordering can be changed but
+ * no page repetitions are allowed. Note that it may be very slow in partial mode.
+ * @param ranges the comma separated ranges as described in {@link SequenceList}
+ */
+ public void selectPages(String ranges) {
+ selectPages(SequenceList.expand(ranges, getNumberOfPages()));
+ }
+
+ /**
+ * Selects the pages to keep in the document. The pages are described as a
+ * List
of Integer
. The page ordering can be changed but
+ * no page repetitions are allowed. Note that it may be very slow in partial mode.
+ * @param pagesToKeep the pages to keep in the document
+ */
+ public void selectPages(List pagesToKeep) {
+ pageRefs.selectPages(pagesToKeep);
+ removeUnusedObjects();
+ }
+
+ /**
+ * @param preferences
+ * @param catalog
+ */
+ public static void setViewerPreferences(int preferences, PdfDictionary catalog) {
+ catalog.remove(PdfName.PAGELAYOUT);
+ catalog.remove(PdfName.PAGEMODE);
+ catalog.remove(PdfName.VIEWERPREFERENCES);
+ if ((preferences & PdfWriter.PageLayoutSinglePage) != 0)
+ catalog.put(PdfName.PAGELAYOUT, PdfName.SINGLEPAGE);
+ else if ((preferences & PdfWriter.PageLayoutOneColumn) != 0)
+ catalog.put(PdfName.PAGELAYOUT, PdfName.ONECOLUMN);
+ else if ((preferences & PdfWriter.PageLayoutTwoColumnLeft) != 0)
+ catalog.put(PdfName.PAGELAYOUT, PdfName.TWOCOLUMNLEFT);
+ else if ((preferences & PdfWriter.PageLayoutTwoColumnRight) != 0)
+ catalog.put(PdfName.PAGELAYOUT, PdfName.TWOCOLUMNRIGHT);
+ else if ((preferences & PdfWriter.PageLayoutTwoPageLeft) != 0)
+ catalog.put(PdfName.PAGELAYOUT, PdfName.TWOPAGELEFT);
+ else if ((preferences & PdfWriter.PageLayoutTwoPageRight) != 0)
+ catalog.put(PdfName.PAGELAYOUT, PdfName.TWOPAGERIGHT);
+ if ((preferences & PdfWriter.PageModeUseNone) != 0)
+ catalog.put(PdfName.PAGEMODE, PdfName.USENONE);
+ else if ((preferences & PdfWriter.PageModeUseOutlines) != 0)
+ catalog.put(PdfName.PAGEMODE, PdfName.USEOUTLINES);
+ else if ((preferences & PdfWriter.PageModeUseThumbs) != 0)
+ catalog.put(PdfName.PAGEMODE, PdfName.USETHUMBS);
+ else if ((preferences & PdfWriter.PageModeFullScreen) != 0)
+ catalog.put(PdfName.PAGEMODE, PdfName.FULLSCREEN);
+ else if ((preferences & PdfWriter.PageModeUseOC) != 0)
+ catalog.put(PdfName.PAGEMODE, PdfName.USEOC);
+ else if ((preferences & PdfWriter.PageModeUseAttachments) != 0)
+ catalog.put(PdfName.PAGEMODE, PdfName.USEATTACHMENTS);
+ if ((preferences & PdfWriter.ViewerPreferencesMask) == 0)
+ return;
+ PdfDictionary vp = new PdfDictionary();
+ if ((preferences & PdfWriter.HideToolbar) != 0)
+ vp.put(PdfName.HIDETOOLBAR, PdfBoolean.PDFTRUE);
+ if ((preferences & PdfWriter.HideMenubar) != 0)
+ vp.put(PdfName.HIDEMENUBAR, PdfBoolean.PDFTRUE);
+ if ((preferences & PdfWriter.HideWindowUI) != 0)
+ vp.put(PdfName.HIDEWINDOWUI, PdfBoolean.PDFTRUE);
+ if ((preferences & PdfWriter.FitWindow) != 0)
+ vp.put(PdfName.FITWINDOW, PdfBoolean.PDFTRUE);
+ if ((preferences & PdfWriter.CenterWindow) != 0)
+ vp.put(PdfName.CENTERWINDOW, PdfBoolean.PDFTRUE);
+ if ((preferences & PdfWriter.DisplayDocTitle) != 0)
+ vp.put(PdfName.DISPLAYDOCTITLE, PdfBoolean.PDFTRUE);
+ if ((preferences & PdfWriter.NonFullScreenPageModeUseNone) != 0)
+ vp.put(PdfName.NONFULLSCREENPAGEMODE, PdfName.USENONE);
+ else if ((preferences & PdfWriter.NonFullScreenPageModeUseOutlines) != 0)
+ vp.put(PdfName.NONFULLSCREENPAGEMODE, PdfName.USEOUTLINES);
+ else if ((preferences & PdfWriter.NonFullScreenPageModeUseThumbs) != 0)
+ vp.put(PdfName.NONFULLSCREENPAGEMODE, PdfName.USETHUMBS);
+ else if ((preferences & PdfWriter.NonFullScreenPageModeUseOC) != 0)
+ vp.put(PdfName.NONFULLSCREENPAGEMODE, PdfName.USEOC);
+ if ((preferences & PdfWriter.DirectionL2R) != 0)
+ vp.put(PdfName.DIRECTION, PdfName.L2R);
+ else if ((preferences & PdfWriter.DirectionR2L) != 0)
+ vp.put(PdfName.DIRECTION, PdfName.R2L);
+ if ((preferences & PdfWriter.PrintScalingNone) != 0)
+ vp.put(PdfName.PRINTSCALING, PdfName.NONE);
+ catalog.put(PdfName.VIEWERPREFERENCES, vp);
+ }
+
+ /**
+ * @param preferences
+ */
+ public void setViewerPreferences(int preferences) {
+ setViewerPreferences(preferences, catalog);
+ }
+
+ /**
+ * @return an int that contains the Viewer Preferences.
+ */
+ public int getViewerPreferences() {
+ int prefs = 0;
+ PdfName name = null;
+ PdfObject obj = getPdfObjectRelease(catalog.get(PdfName.PAGELAYOUT));
+ if (obj != null && obj.isName()) {
+ name = (PdfName)obj;
+ if (name.equals(PdfName.SINGLEPAGE))
+ prefs |= PdfWriter.PageLayoutSinglePage;
+ else if (name.equals(PdfName.ONECOLUMN))
+ prefs |= PdfWriter.PageLayoutOneColumn;
+ else if (name.equals(PdfName.TWOCOLUMNLEFT))
+ prefs |= PdfWriter.PageLayoutTwoColumnLeft;
+ else if (name.equals(PdfName.TWOCOLUMNRIGHT))
+ prefs |= PdfWriter.PageLayoutTwoColumnRight;
+ else if (name.equals(PdfName.TWOPAGELEFT))
+ prefs |= PdfWriter.PageLayoutTwoPageLeft;
+ else if (name.equals(PdfName.TWOPAGERIGHT))
+ prefs |= PdfWriter.PageLayoutTwoPageRight;
+ }
+ obj = getPdfObjectRelease(catalog.get(PdfName.PAGEMODE));
+ if (obj != null && obj.isName()) {
+ name = (PdfName)obj;
+ if (name.equals(PdfName.USENONE))
+ prefs |= PdfWriter.PageModeUseNone;
+ else if (name.equals(PdfName.USEOUTLINES))
+ prefs |= PdfWriter.PageModeUseOutlines;
+ else if (name.equals(PdfName.USETHUMBS))
+ prefs |= PdfWriter.PageModeUseThumbs;
+ else if (name.equals(PdfName.USEOC))
+ prefs |= PdfWriter.PageModeUseOC;
+ else if (name.equals(PdfName.USEATTACHMENTS))
+ prefs |= PdfWriter.PageModeUseAttachments;
+ }
+ obj = getPdfObjectRelease(catalog.get(PdfName.VIEWERPREFERENCES));
+ if (obj == null || !obj.isDictionary())
+ return prefs;
+ PdfDictionary vp = (PdfDictionary)obj;
+ for (int k = 0; k < vpnames.length; ++k) {
+ obj = getPdfObject(vp.get(vpnames[k]));
+ if (obj != null && "true".equals(obj.toString()))
+ prefs |= vpints[k];
+ }
+ obj = getPdfObjectRelease(vp.get(PdfName.PRINTSCALING));
+ if (PdfName.NONE.equals(obj))
+ prefs |= PdfWriter.PrintScalingNone;
+ obj = getPdfObjectRelease(vp.get(PdfName.NONFULLSCREENPAGEMODE));
+ if (obj != null && obj.isName()) {
+ name = (PdfName)obj;
+ if (name.equals(PdfName.USENONE))
+ prefs |= PdfWriter.NonFullScreenPageModeUseNone;
+ else if (name.equals(PdfName.USEOUTLINES))
+ prefs |= PdfWriter.NonFullScreenPageModeUseOutlines;
+ else if (name.equals(PdfName.USETHUMBS))
+ prefs |= PdfWriter.NonFullScreenPageModeUseThumbs;
+ else if (name.equals(PdfName.USEOC))
+ prefs |= PdfWriter.NonFullScreenPageModeUseOC;
+ }
+ obj = getPdfObjectRelease(vp.get(PdfName.DIRECTION));
+ if (obj != null && obj.isName()) {
+ name = (PdfName)obj;
+ if (name.equals(PdfName.L2R))
+ prefs |= PdfWriter.DirectionL2R;
+ else if (name.equals(PdfName.R2L))
+ prefs |= PdfWriter.DirectionR2L;
+ }
+ return prefs;
+ }
+
+ /**
+ * Getter for property appendable.
+ * @return Value of property appendable.
+ */
+ public boolean isAppendable() {
+ return this.appendable;
+ }
+
+ /**
+ * Setter for property appendable.
+ * @param appendable New value of property appendable.
+ */
+ public void setAppendable(boolean appendable) {
+ this.appendable = appendable;
+ if (appendable)
+ getPdfObject(trailer.get(PdfName.ROOT));
+ }
+
+ /**
+ * Getter for property newXrefType.
+ * @return Value of property newXrefType.
+ */
+ public boolean isNewXrefType() {
+ return newXrefType;
+ }
+
+ /**
+ * Getter for property fileLength.
+ * @return Value of property fileLength.
+ */
+ public int getFileLength() {
+ return fileLength;
+ }
+
+ /**
+ * Getter for property hybridXref.
+ * @return Value of property hybridXref.
+ */
+ public boolean isHybridXref() {
+ return hybridXref;
+ }
+
+ static class PageRefs {
+ private PdfReader reader;
+ private IntHashtable refsp;
+ private ArrayList refsn;
+ private ArrayList pageInh;
+ private int lastPageRead = -1;
+ private int sizep;
+
+ private PageRefs(PdfReader reader) throws IOException {
+ this.reader = reader;
+ if (reader.partial) {
+ refsp = new IntHashtable();
+ PdfNumber npages = (PdfNumber)PdfReader.getPdfObjectRelease(reader.rootPages.get(PdfName.COUNT));
+ sizep = npages.intValue();
+ }
+ else {
+ readPages();
+ }
+ }
+
+ PageRefs(PageRefs other, PdfReader reader) {
+ this.reader = reader;
+ this.sizep = other.sizep;
+ if (other.refsn != null) {
+ refsn = new ArrayList(other.refsn);
+ for (int k = 0; k < refsn.size(); ++k) {
+ refsn.set(k, duplicatePdfObject((PdfObject)refsn.get(k), reader));
+ }
+ }
+ else
+ this.refsp = (IntHashtable)other.refsp.clone();
+ }
+
+ int size() {
+ if (refsn != null)
+ return refsn.size();
+ else
+ return sizep;
+ }
+
+ void readPages() throws IOException {
+ if (refsn != null)
+ return;
+ refsp = null;
+ refsn = new ArrayList();
+ pageInh = new ArrayList();
+ iteratePages((PRIndirectReference)reader.catalog.get(PdfName.PAGES));
+ pageInh = null;
+ reader.rootPages.put(PdfName.COUNT, new PdfNumber(refsn.size()));
+ }
+
+ void reReadPages() throws IOException {
+ refsn = null;
+ readPages();
+ }
+
+ /** Gets the dictionary that represents a page.
+ * @param pageNum the page number. 1 is the first
+ * @return the page dictionary
+ */
+ public PdfDictionary getPageN(int pageNum) {
+ PRIndirectReference ref = getPageOrigRef(pageNum);
+ return (PdfDictionary)PdfReader.getPdfObject(ref);
+ }
+
+ /**
+ * @param pageNum
+ * @return a dictionary object
+ */
+ public PdfDictionary getPageNRelease(int pageNum) {
+ PdfDictionary page = getPageN(pageNum);
+ releasePage(pageNum);
+ return page;
+ }
+
+ /**
+ * @param pageNum
+ * @return an indirect reference
+ */
+ public PRIndirectReference getPageOrigRefRelease(int pageNum) {
+ PRIndirectReference ref = getPageOrigRef(pageNum);
+ releasePage(pageNum);
+ return ref;
+ }
+
+ /** Gets the page reference to this page.
+ * @param pageNum the page number. 1 is the first
+ * @return the page reference
+ */
+ public PRIndirectReference getPageOrigRef(int pageNum) {
+ try {
+ --pageNum;
+ if (pageNum < 0 || pageNum >= size())
+ return null;
+ if (refsn != null)
+ return (PRIndirectReference)refsn.get(pageNum);
+ else {
+ int n = refsp.get(pageNum);
+ if (n == 0) {
+ PRIndirectReference ref = getSinglePage(pageNum);
+ if (reader.lastXrefPartial == -1)
+ lastPageRead = -1;
+ else
+ lastPageRead = pageNum;
+ reader.lastXrefPartial = -1;
+ refsp.put(pageNum, ref.getNumber());
+ return ref;
+ }
+ else {
+ if (lastPageRead != pageNum)
+ lastPageRead = -1;
+ return new PRIndirectReference(reader, n);
+ }
+ }
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * @param pageNum
+ */
+ public void releasePage(int pageNum) {
+ if (refsp == null)
+ return;
+ --pageNum;
+ if (pageNum < 0 || pageNum >= size())
+ return;
+ if (pageNum != lastPageRead)
+ return;
+ lastPageRead = -1;
+ reader.lastXrefPartial = refsp.get(pageNum);
+ reader.releaseLastXrefPartial();
+ refsp.remove(pageNum);
+ }
+
+ /**
+ *
+ */
+ public void resetReleasePage() {
+ if (refsp == null)
+ return;
+ lastPageRead = -1;
+ }
+
+ void insertPage(int pageNum, PRIndirectReference ref) {
+ --pageNum;
+ if (refsn != null) {
+ if (pageNum >= refsn.size())
+ refsn.add(ref);
+ else
+ refsn.add(pageNum, ref);
+ }
+ else {
+ ++sizep;
+ lastPageRead = -1;
+ if (pageNum >= size()) {
+ refsp.put(size(), ref.getNumber());
+ }
+ else {
+ IntHashtable refs2 = new IntHashtable((refsp.size() + 1) * 2);
+ for (Iterator it = refsp.getEntryIterator(); it.hasNext();) {
+ IntHashtable.IntHashtableEntry entry = (IntHashtable.IntHashtableEntry)it.next();
+ int p = entry.getKey();
+ refs2.put(p >= pageNum ? p + 1 : p, entry.getValue());
+ }
+ refs2.put(pageNum, ref.getNumber());
+ refsp = refs2;
+ }
+ }
+ }
+
+ private void pushPageAttributes(PdfDictionary nodePages) {
+ PdfDictionary dic = new PdfDictionary();
+ if (pageInh.size() != 0) {
+ dic.putAll((PdfDictionary)pageInh.get(pageInh.size() - 1));
+ }
+ for (int k = 0; k < pageInhCandidates.length; ++k) {
+ PdfObject obj = nodePages.get(pageInhCandidates[k]);
+ if (obj != null)
+ dic.put(pageInhCandidates[k], obj);
+ }
+ pageInh.add(dic);
+ }
+
+ private void popPageAttributes() {
+ pageInh.remove(pageInh.size() - 1);
+ }
+
+ private void iteratePages(PRIndirectReference rpage) throws IOException {
+ PdfDictionary page = (PdfDictionary)getPdfObject(rpage);
+ PdfArray kidsPR = (PdfArray)getPdfObject(page.get(PdfName.KIDS));
+ if (kidsPR == null) {
+ page.put(PdfName.TYPE, PdfName.PAGE);
+ PdfDictionary dic = (PdfDictionary)pageInh.get(pageInh.size() - 1);
+ PdfName key;
+ for (Iterator i = dic.getKeys().iterator(); i.hasNext();) {
+ key = (PdfName)i.next();
+ if (page.get(key) == null)
+ page.put(key, dic.get(key));
+ }
+ if (page.get(PdfName.MEDIABOX) == null) {
+ PdfArray arr = new PdfArray(new float[]{0,0,PageSize.LETTER.right(),PageSize.LETTER.top()});
+ page.put(PdfName.MEDIABOX, arr);
+ }
+ refsn.add(rpage);
+ }
+ else {
+ page.put(PdfName.TYPE, PdfName.PAGES);
+ pushPageAttributes(page);
+ ArrayList kids = kidsPR.getArrayList();
+ for (int k = 0; k < kids.size(); ++k){
+ PdfObject obj = (PdfObject)kids.get(k);
+ if (!obj.isIndirect()) {
+ while (k < kids.size())
+ kids.remove(k);
+ break;
+ }
+ iteratePages((PRIndirectReference)obj);
+ }
+ popPageAttributes();
+ }
+ }
+
+ protected PRIndirectReference getSinglePage(int n) throws IOException {
+ PdfDictionary acc = new PdfDictionary();
+ PdfDictionary top = reader.rootPages;
+ int base = 0;
+ while (true) {
+ for (int k = 0; k < pageInhCandidates.length; ++k) {
+ PdfObject obj = top.get(pageInhCandidates[k]);
+ if (obj != null)
+ acc.put(pageInhCandidates[k], obj);
+ }
+ PdfArray kids = (PdfArray)PdfReader.getPdfObjectRelease(top.get(PdfName.KIDS));
+ for (Iterator it = kids.listIterator(); it.hasNext();) {
+ PRIndirectReference ref = (PRIndirectReference)it.next();
+ PdfDictionary dic = (PdfDictionary)getPdfObject(ref);
+ int last = reader.lastXrefPartial;
+ PdfObject count = getPdfObjectRelease(dic.get(PdfName.COUNT));
+ reader.lastXrefPartial = last;
+ int acn = 1;
+ if (count != null && count.type() == PdfObject.NUMBER)
+ acn = ((PdfNumber)count).intValue();
+ if (n < base + acn) {
+ if (count == null) {
+ dic.mergeDifferent(acc);
+ return ref;
+ }
+ reader.releaseLastXrefPartial();
+ top = dic;
+ break;
+ }
+ reader.releaseLastXrefPartial();
+ base += acn;
+ }
+ }
+ }
+
+ private void selectPages(List pagesToKeep) {
+ IntHashtable pg = new IntHashtable();
+ ArrayList finalPages = new ArrayList();
+ int psize = size();
+ for (Iterator it = pagesToKeep.iterator(); it.hasNext();) {
+ Integer pi = (Integer)it.next();
+ int p = pi.intValue();
+ if (p >= 1 && p <= psize && pg.put(p, 1) == 0)
+ finalPages.add(pi);
+ }
+ if (reader.partial) {
+ for (int k = 1; k <= psize; ++k) {
+ getPageOrigRef(k);
+ resetReleasePage();
+ }
+ }
+ PRIndirectReference parent = (PRIndirectReference)reader.catalog.get(PdfName.PAGES);
+ PdfDictionary topPages = (PdfDictionary)PdfReader.getPdfObject(parent);
+ ArrayList newPageRefs = new ArrayList(finalPages.size());
+ PdfArray kids = new PdfArray();
+ for (int k = 0; k < finalPages.size(); ++k) {
+ int p = ((Integer)finalPages.get(k)).intValue();
+ PRIndirectReference pref = getPageOrigRef(p);
+ resetReleasePage();
+ kids.add(pref);
+ newPageRefs.add(pref);
+ getPageN(p).put(PdfName.PARENT, parent);
+ }
+ AcroFields af = reader.getAcroFields();
+ boolean removeFields = (af.getFields().size() > 0);
+ for (int k = 1; k <= psize; ++k) {
+ if (!pg.containsKey(k)) {
+ if (removeFields)
+ af.removeFieldsFromPage(k);
+ PRIndirectReference pref = getPageOrigRef(k);
+ int nref = pref.getNumber();
+ reader.xrefObj.set(nref, null);
+ if (reader.partial) {
+ reader.xref[nref * 2] = -1;
+ reader.xref[nref * 2 + 1] = 0;
+ }
+ }
+ }
+ topPages.put(PdfName.COUNT, new PdfNumber(finalPages.size()));
+ topPages.put(PdfName.KIDS, kids);
+ refsp = null;
+ refsn = newPageRefs;
+ }
+ }
+
+ PdfIndirectReference getCryptoRef() {
+ if (cryptoRef == null)
+ return null;
+ return new PdfIndirectReference(0, cryptoRef.getNumber(), cryptoRef.getGeneration());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfReaderInstance.java b/src/main/java/com/lowagie/text/pdf/PdfReaderInstance.java
new file mode 100644
index 0000000..294e52d
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfReaderInstance.java
@@ -0,0 +1,182 @@
+/*
+ * $Id: PdfReaderInstance.java,v 1.52 2005/11/01 12:27:05 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.io.*;
+/**
+ * Instance of PdfReader in each output document.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+class PdfReaderInstance {
+ static final PdfLiteral IDENTITYMATRIX = new PdfLiteral("[1 0 0 1 0 0]");
+ static final PdfNumber ONE = new PdfNumber(1);
+ int myXref[];
+ PdfReader reader;
+ RandomAccessFileOrArray file;
+ HashMap importedPages = new HashMap();
+ PdfWriter writer;
+ HashMap visited = new HashMap();
+ ArrayList nextRound = new ArrayList();
+
+ PdfReaderInstance(PdfReader reader, PdfWriter writer) {
+ this.reader = reader;
+ this.writer = writer;
+ file = reader.getSafeFile();
+ myXref = new int[reader.getXrefSize()];
+ }
+
+ PdfReader getReader() {
+ return reader;
+ }
+
+ PdfImportedPage getImportedPage(int pageNumber) {
+ if (pageNumber < 1 || pageNumber > reader.getNumberOfPages())
+ throw new IllegalArgumentException("Invalid page number");
+ Integer i = new Integer(pageNumber);
+ PdfImportedPage pageT = (PdfImportedPage)importedPages.get(i);
+ if (pageT == null) {
+ pageT = new PdfImportedPage(this, writer, pageNumber);
+ importedPages.put(i, pageT);
+ }
+ return pageT;
+ }
+
+ int getNewObjectNumber(int number, int generation) {
+ if (myXref[number] == 0) {
+ myXref[number] = writer.getIndirectReferenceNumber();
+ nextRound.add(new Integer(number));
+ }
+ return myXref[number];
+ }
+
+ RandomAccessFileOrArray getReaderFile() {
+ return file;
+ }
+
+ PdfObject getResources(int pageNumber) {
+ PdfObject obj = PdfReader.getPdfObjectRelease(reader.getPageNRelease(pageNumber).get(PdfName.RESOURCES));
+ return obj;
+ }
+
+
+ PdfStream getFormXObject(int pageNumber) throws IOException {
+ PdfDictionary page = reader.getPageNRelease(pageNumber);
+ PdfObject contents = PdfReader.getPdfObjectRelease(page.get(PdfName.CONTENTS));
+ PdfDictionary dic = new PdfDictionary();
+ byte bout[] = null;
+ if (contents != null) {
+ if (contents.isStream())
+ dic.putAll((PRStream)contents);
+ else
+ bout = reader.getPageContent(pageNumber, file);
+ }
+ else
+ bout = new byte[0];
+ dic.put(PdfName.RESOURCES, PdfReader.getPdfObjectRelease(page.get(PdfName.RESOURCES)));
+ dic.put(PdfName.TYPE, PdfName.XOBJECT);
+ dic.put(PdfName.SUBTYPE, PdfName.FORM);
+ PdfImportedPage impPage = (PdfImportedPage)importedPages.get(new Integer(pageNumber));
+ dic.put(PdfName.BBOX, new PdfRectangle(impPage.getBoundingBox()));
+ PdfArray matrix = impPage.getMatrix();
+ if (matrix == null)
+ dic.put(PdfName.MATRIX, IDENTITYMATRIX);
+ else
+ dic.put(PdfName.MATRIX, matrix);
+ dic.put(PdfName.FORMTYPE, ONE);
+ PRStream stream;
+ if (bout == null) {
+ stream = new PRStream((PRStream)contents, dic);
+ }
+ else {
+ stream = new PRStream(reader, bout);
+ stream.putAll(dic);
+ }
+ return stream;
+ }
+
+ void writeAllVisited() throws IOException {
+ while (nextRound.size() > 0) {
+ ArrayList vec = nextRound;
+ nextRound = new ArrayList();
+ for (int k = 0; k < vec.size(); ++k) {
+ Integer i = (Integer)vec.get(k);
+ if (!visited.containsKey(i)) {
+ visited.put(i, null);
+ int n = i.intValue();
+ writer.addToBody(reader.getPdfObjectRelease(n), myXref[n]);
+ }
+ }
+ }
+ }
+
+ void writeAllPages() throws IOException {
+ try {
+ file.reOpen();
+ for (Iterator it = importedPages.values().iterator(); it.hasNext();) {
+ PdfImportedPage ip = (PdfImportedPage)it.next();
+ writer.addToBody(ip.getFormXObject(), ip.getIndirectReference());
+ }
+ writeAllVisited();
+ }
+ finally {
+ try {
+ reader.close();
+ file.close();
+ }
+ catch (Exception e) {
+ //Empty on purpose
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfRectangle.java b/src/main/java/com/lowagie/text/pdf/PdfRectangle.java
new file mode 100644
index 0000000..c1d5f0d
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfRectangle.java
@@ -0,0 +1,284 @@
+/*
+ * $Id: PdfRectangle.java,v 1.59 2006/01/16 13:46:20 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.Rectangle;
+
+/**
+ * PdfRectangle
is the PDF Rectangle object.
+ * array
of
+ * four numbers, specifying the lower lef x, lower left y, upper right x,
+ * and upper right y coordinates of the rectangle, in that order.
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 7.1 (page 183).
+ *
+ * @see com.lowagie.text.Rectangle
+ * @see PdfArray
+ */
+
+public class PdfRectangle extends PdfArray {
+
+ // membervariables
+
+/** lower left x */
+ private float llx = 0;
+
+/** lower left y */
+ private float lly = 0;
+
+/** upper right x */
+ private float urx = 0;
+
+/** upper right y */
+ private float ury = 0;
+
+ // constructors
+
+/**
+ * Constructs a PdfRectangle
-object.
+ *
+ * @param llx lower left x
+ * @param lly lower left y
+ * @param urx upper right x
+ * @param ury upper right y
+ *
+ * @since rugPdf0.10
+ */
+
+ public PdfRectangle(float llx, float lly, float urx, float ury, int rotation) {
+ super();
+ if (rotation == 90 || rotation == 270) {
+ this.llx = lly;
+ this.lly = llx;
+ this.urx = ury;
+ this.ury = urx;
+ }
+ else {
+ this.llx = llx;
+ this.lly = lly;
+ this.urx = urx;
+ this.ury = ury;
+ }
+ super.add(new PdfNumber(this.llx));
+ super.add(new PdfNumber(this.lly));
+ super.add(new PdfNumber(this.urx));
+ super.add(new PdfNumber(this.ury));
+ }
+
+ public PdfRectangle(float llx, float lly, float urx, float ury) {
+ this(llx, lly, urx, ury, 0);
+ }
+
+/**
+ * Constructs a PdfRectangle
-object starting from the origin (0, 0).
+ *
+ * @param urx upper right x
+ * @param ury upper right y
+ */
+
+ public PdfRectangle(float urx, float ury, int rotation) {
+ this(0, 0, urx, ury, rotation);
+ }
+
+ public PdfRectangle(float urx, float ury) {
+ this(0, 0, urx, ury, 0);
+ }
+
+/**
+ * Constructs a PdfRectangle
-object with a Rectangle
-object.
+ *
+ * @param rectangle a Rectangle
+ */
+
+ public PdfRectangle(Rectangle rectangle, int rotation) {
+ this(rectangle.left(), rectangle.bottom(), rectangle.right(), rectangle.top(), rotation);
+ }
+
+ public PdfRectangle(Rectangle rectangle) {
+ this(rectangle.left(), rectangle.bottom(), rectangle.right(), rectangle.top(), 0);
+ }
+
+ // methods
+ /**
+ * Returns the high level version of this PdfRectangle
+ * @return this PdfRectangle translated to class Rectangle
+ */
+ public Rectangle getRectangle() {
+ return new Rectangle(left(), bottom(), right(), top());
+ }
+
+/**
+ * Overrides the add
-method in PdfArray
in order to prevent the adding of extra object to the array.
+ *
+ * @param object PdfObject
to add (will not be added here)
+ * @return false
+ */
+
+ public boolean add(PdfObject object) {
+ return false;
+ }
+
+/**
+ * Returns the lower left x-coordinate.
+ *
+ * @return the lower left x-coordinaat
+ */
+
+ public float left() {
+ return llx;
+ }
+
+/**
+ * Returns the upper right x-coordinate.
+ *
+ * @return the upper right x-coordinate
+ */
+
+ public float right() {
+ return urx;
+ }
+
+/**
+ * Returns the upper right y-coordinate.
+ *
+ * @return the upper right y-coordinate
+ */
+
+ public float top() {
+ return ury;
+ }
+
+/**
+ * Returns the lower left y-coordinate.
+ *
+ * @return the lower left y-coordinate
+ */
+
+ public float bottom() {
+ return lly;
+ }
+
+/**
+ * Returns the lower left x-coordinate, considering a given margin.
+ *
+ * @param margin a margin
+ * @return the lower left x-coordinate
+ */
+
+ public float left(int margin) {
+ return llx + margin;
+ }
+
+/**
+ * Returns the upper right x-coordinate, considering a given margin.
+ *
+ * @param margin a margin
+ * @return the upper right x-coordinate
+ */
+
+ public float right(int margin) {
+ return urx - margin;
+ }
+
+/**
+ * Returns the upper right y-coordinate, considering a given margin.
+ *
+ * @param margin a margin
+ * @return the upper right y-coordinate
+ */
+
+ public float top(int margin) {
+ return ury - margin;
+ }
+
+/**
+ * Returns the lower left y-coordinate, considering a given margin.
+ *
+ * @param margin a margin
+ * @return the lower left y-coordinate
+ */
+
+ public float bottom(int margin) {
+ return lly + margin;
+ }
+
+/**
+ * Returns the width of the rectangle.
+ *
+ * @return a width
+ */
+
+ public float width() {
+ return urx - llx;
+ }
+
+/**
+ * Returns the height of the rectangle.
+ *
+ * @return a height
+ */
+
+ public float height() {
+ return ury - lly;
+ }
+
+/**
+ * Swaps the values of urx and ury and of lly and llx in order to rotate the rectangle.
+ *
+ * @return a PdfRectangle
+ */
+
+ public PdfRectangle rotate() {
+ return new PdfRectangle(lly, llx, ury, urx, 0);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfRendition.java b/src/main/java/com/lowagie/text/pdf/PdfRendition.java
new file mode 100644
index 0000000..bcbd114
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfRendition.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2003 Galo Gimenez
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+
+/**
+ * A Rendition dictionary (pdf spec 1.5)
+ */
+public class PdfRendition extends PdfDictionary {
+ PdfRendition(String file, PdfFileSpecification fs, String mimeType) throws IOException{
+ put(PdfName.S, new PdfName("MR"));
+ put(PdfName.N, new PdfString("Rendition for "+file));
+ put(PdfName.C, new PdfMediaClipData(file, fs, mimeType));
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfResources.java b/src/main/java/com/lowagie/text/pdf/PdfResources.java
new file mode 100644
index 0000000..3480a19
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfResources.java
@@ -0,0 +1,91 @@
+/*
+ * $Id: PdfResources.java,v 1.56 2005/05/04 14:31:53 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * PdfResources
is the PDF Resources-object.
+ *
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 7.5 (page 195-197).
+ *
+ * @see PdfPage
+ */
+
+class PdfResources extends PdfDictionary {
+
+ // constructor
+
+/**
+ * Constructs a PDF ResourcesDictionary.
+ */
+
+ PdfResources() {
+ super();
+ }
+
+ // methods
+
+ void add(PdfName key, PdfDictionary resource) {
+ if (resource.size() == 0)
+ return;
+ PdfDictionary dic = (PdfDictionary)PdfReader.getPdfObject(get(key));
+ if (dic == null)
+ put(key, resource);
+ else
+ dic.putAll(resource);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfShading.java b/src/main/java/com/lowagie/text/pdf/PdfShading.java
new file mode 100644
index 0000000..54e2067
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfShading.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.awt.Color;
+import java.io.IOException;
+/** Implements the shading dictionary (or stream).
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfShading {
+
+ protected PdfDictionary shading;
+
+ protected PdfWriter writer;
+
+ protected int shadingType;
+
+ protected ColorDetails colorDetails;
+
+ protected PdfName shadingName;
+
+ protected PdfIndirectReference shadingReference;
+
+ private Color cspace;
+
+ /** Holds value of property bBox. */
+ protected float[] bBox;
+
+ /** Holds value of property antiAlias. */
+ protected boolean antiAlias = false;
+
+ /** Creates new PdfShading */
+ protected PdfShading(PdfWriter writer) {
+ this.writer = writer;
+ }
+
+ protected void setColorSpace(Color color) {
+ cspace = color;
+ int type = ExtendedColor.getType(color);
+ PdfObject colorSpace = null;
+ switch (type) {
+ case ExtendedColor.TYPE_GRAY: {
+ colorSpace = PdfName.DEVICEGRAY;
+ break;
+ }
+ case ExtendedColor.TYPE_CMYK: {
+ colorSpace = PdfName.DEVICECMYK;
+ break;
+ }
+ case ExtendedColor.TYPE_SEPARATION: {
+ SpotColor spot = (SpotColor)color;
+ colorDetails = writer.addSimple(spot.getPdfSpotColor());
+ colorSpace = colorDetails.getIndirectReference();
+ break;
+ }
+ case ExtendedColor.TYPE_PATTERN:
+ case ExtendedColor.TYPE_SHADING: {
+ throwColorSpaceError();
+ }
+ default:
+ colorSpace = PdfName.DEVICERGB;
+ break;
+ }
+ shading.put(PdfName.COLORSPACE, colorSpace);
+ }
+
+ Color getColorSpace() {
+ return cspace;
+ }
+
+ public static void throwColorSpaceError() {
+ throw new IllegalArgumentException("A tiling or shading pattern cannot be used as a color space in a shading pattern");
+ }
+
+ public static void checkCompatibleColors(Color c1, Color c2) {
+ int type1 = ExtendedColor.getType(c1);
+ int type2 = ExtendedColor.getType(c2);
+ if (type1 != type2)
+ throw new IllegalArgumentException("Both colors must be of the same type.");
+ if (type1 == ExtendedColor.TYPE_SEPARATION && ((SpotColor)c1).getPdfSpotColor() != ((SpotColor)c2).getPdfSpotColor())
+ throw new IllegalArgumentException("The spot color must be the same, only the tint can vary.");
+ if (type1 == ExtendedColor.TYPE_PATTERN || type1 == ExtendedColor.TYPE_SHADING)
+ throwColorSpaceError();
+ }
+
+ public static float[] getColorArray(Color color) {
+ int type = ExtendedColor.getType(color);
+ switch (type) {
+ case ExtendedColor.TYPE_GRAY: {
+ return new float[]{((GrayColor)color).getGray()};
+ }
+ case ExtendedColor.TYPE_CMYK: {
+ CMYKColor cmyk = (CMYKColor)color;
+ return new float[]{cmyk.getCyan(), cmyk.getMagenta(), cmyk.getYellow(), cmyk.getBlack()};
+ }
+ case ExtendedColor.TYPE_SEPARATION: {
+ return new float[]{((SpotColor)color).getTint()};
+ }
+ case ExtendedColor.TYPE_RGB: {
+ return new float[]{color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f};
+ }
+ }
+ throwColorSpaceError();
+ return null;
+ }
+
+ public static PdfShading type1(PdfWriter writer, Color colorSpace, float domain[], float tMatrix[], PdfFunction function) {
+ PdfShading sp = new PdfShading(writer);
+ sp.shading = new PdfDictionary();
+ sp.shadingType = 1;
+ sp.shading.put(PdfName.SHADINGTYPE, new PdfNumber(sp.shadingType));
+ sp.setColorSpace(colorSpace);
+ if (domain != null)
+ sp.shading.put(PdfName.DOMAIN, new PdfArray(domain));
+ if (tMatrix != null)
+ sp.shading.put(PdfName.MATRIX, new PdfArray(tMatrix));
+ sp.shading.put(PdfName.FUNCTION, function.getReference());
+ return sp;
+ }
+
+ public static PdfShading type2(PdfWriter writer, Color colorSpace, float coords[], float domain[], PdfFunction function, boolean extend[]) {
+ PdfShading sp = new PdfShading(writer);
+ sp.shading = new PdfDictionary();
+ sp.shadingType = 2;
+ sp.shading.put(PdfName.SHADINGTYPE, new PdfNumber(sp.shadingType));
+ sp.setColorSpace(colorSpace);
+ sp.shading.put(PdfName.COORDS, new PdfArray(coords));
+ if (domain != null)
+ sp.shading.put(PdfName.DOMAIN, new PdfArray(domain));
+ sp.shading.put(PdfName.FUNCTION, function.getReference());
+ if (extend != null && (extend[0] || extend[1])) {
+ PdfArray array = new PdfArray(extend[0] ? PdfBoolean.PDFTRUE : PdfBoolean.PDFFALSE);
+ array.add(extend[1] ? PdfBoolean.PDFTRUE : PdfBoolean.PDFFALSE);
+ sp.shading.put(PdfName.EXTEND, array);
+ }
+ return sp;
+ }
+
+ public static PdfShading type3(PdfWriter writer, Color colorSpace, float coords[], float domain[], PdfFunction function, boolean extend[]) {
+ PdfShading sp = type2(writer, colorSpace, coords, domain, function, extend);
+ sp.shadingType = 3;
+ sp.shading.put(PdfName.SHADINGTYPE, new PdfNumber(sp.shadingType));
+ return sp;
+ }
+
+ public static PdfShading simpleAxial(PdfWriter writer, float x0, float y0, float x1, float y1, Color startColor, Color endColor, boolean extendStart, boolean extendEnd) {
+ checkCompatibleColors(startColor, endColor);
+ PdfFunction function = PdfFunction.type2(writer, new float[]{0, 1}, null, getColorArray(startColor),
+ getColorArray(endColor), 1);
+ return type2(writer, startColor, new float[]{x0, y0, x1, y1}, null, function, new boolean[]{extendStart, extendEnd});
+ }
+
+ public static PdfShading simpleAxial(PdfWriter writer, float x0, float y0, float x1, float y1, Color startColor, Color endColor) {
+ return simpleAxial(writer, x0, y0, x1, y1, startColor, endColor, true, true);
+ }
+
+ public static PdfShading simpleRadial(PdfWriter writer, float x0, float y0, float r0, float x1, float y1, float r1, Color startColor, Color endColor, boolean extendStart, boolean extendEnd) {
+ checkCompatibleColors(startColor, endColor);
+ PdfFunction function = PdfFunction.type2(writer, new float[]{0, 1}, null, getColorArray(startColor),
+ getColorArray(endColor), 1);
+ return type3(writer, startColor, new float[]{x0, y0, r0, x1, y1, r1}, null, function, new boolean[]{extendStart, extendEnd});
+ }
+
+ public static PdfShading simpleRadial(PdfWriter writer, float x0, float y0, float r0, float x1, float y1, float r1, Color startColor, Color endColor) {
+ return simpleRadial(writer, x0, y0, r0, x1, y1, r1, startColor, endColor, true, true);
+ }
+
+ PdfName getShadingName() {
+ return shadingName;
+ }
+
+ PdfIndirectReference getShadingReference() {
+ if (shadingReference == null)
+ shadingReference = writer.getPdfIndirectReference();
+ return shadingReference;
+ }
+
+ void setName(int number) {
+ shadingName = new PdfName("Sh" + number);
+ }
+
+ void addToBody() throws IOException {
+ if (bBox != null)
+ shading.put(PdfName.BBOX, new PdfArray(bBox));
+ if (antiAlias)
+ shading.put(PdfName.ANTIALIAS, PdfBoolean.PDFTRUE);
+ writer.addToBody(shading, getShadingReference());
+ }
+
+ PdfWriter getWriter() {
+ return writer;
+ }
+
+ ColorDetails getColorDetails() {
+ return colorDetails;
+ }
+
+ public float[] getBBox() {
+ return bBox;
+ }
+
+ public void setBBox(float[] bBox) {
+ if (bBox.length != 4)
+ throw new IllegalArgumentException("BBox must be a 4 element array.");
+ this.bBox = bBox;
+ }
+
+ public boolean isAntiAlias() {
+ return antiAlias;
+ }
+
+ public void setAntiAlias(boolean antiAlias) {
+ this.antiAlias = antiAlias;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfShadingPattern.java b/src/main/java/com/lowagie/text/pdf/PdfShadingPattern.java
new file mode 100644
index 0000000..c90e97c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfShadingPattern.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+/** Implements the shading pattern dictionary.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfShadingPattern extends PdfDictionary {
+
+ protected PdfShading shading;
+
+ protected PdfWriter writer;
+
+ protected float matrix[] = {1, 0, 0, 1, 0, 0};
+
+ protected PdfName patternName;
+
+ protected PdfIndirectReference patternReference;
+
+ /** Creates new PdfShadingPattern */
+ public PdfShadingPattern(PdfShading shading) {
+ writer = shading.getWriter();
+ put(PdfName.PATTERNTYPE, new PdfNumber(2));
+ this.shading = shading;
+ }
+
+ PdfName getPatternName() {
+ return patternName;
+ }
+
+ PdfName getShadingName() {
+ return shading.getShadingName();
+ }
+
+ PdfIndirectReference getPatternReference() {
+ if (patternReference == null)
+ patternReference = writer.getPdfIndirectReference();
+ return patternReference;
+ }
+
+ PdfIndirectReference getShadingReference() {
+ return shading.getShadingReference();
+ }
+
+ void setName(int number) {
+ patternName = new PdfName("P" + number);
+ }
+
+ void addToBody() throws IOException {
+ put(PdfName.SHADING, getShadingReference());
+ put(PdfName.MATRIX, new PdfArray(matrix));
+ writer.addToBody(this, getPatternReference());
+ }
+
+ public void setMatrix(float matrix[]) {
+ if (matrix.length != 6)
+ throw new RuntimeException("The matrix size must be 6.");
+ this.matrix = matrix;
+ }
+
+ public float[] getMatrix() {
+ return matrix;
+ }
+
+ PdfShading getShading() {
+ return shading;
+ }
+
+ ColorDetails getColorDetails() {
+ return shading.getColorDetails();
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfSigGenericPKCS.java b/src/main/java/com/lowagie/text/pdf/PdfSigGenericPKCS.java
new file mode 100644
index 0000000..2cbee88
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfSigGenericPKCS.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.security.cert.Certificate;
+import java.security.cert.CRL;
+import java.security.PrivateKey;
+import com.lowagie.text.ExceptionConverter;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * A signature dictionary representation for the standard filters.
+ */
+public abstract class PdfSigGenericPKCS extends PdfSignature {
+ /**
+ * The hash algorith, for example "SHA1"
+ */
+ protected String hashAlgorithm;
+ /**
+ * The crypto provider
+ */
+ protected String provider = null;
+ /**
+ * The class instance that calculates the PKCS#1 and PKCS#7
+ */
+ protected PdfPKCS7 pkcs;
+ /**
+ * The subject name in the signing certificate (the element "CN")
+ */
+ protected String name;
+
+ private byte externalDigest[];
+ private byte externalRSAdata[];
+ private String digestEncryptionAlgorithm;
+
+ /**
+ * Creates a generic standard filter.
+ * @param filter the filter name
+ * @param subFilter the sub-filter name
+ */
+ public PdfSigGenericPKCS(PdfName filter, PdfName subFilter) {
+ super(filter, subFilter);
+ }
+
+ /**
+ * Sets the crypto information to sign.
+ * @param privKey the private key
+ * @param certChain the certificate chain
+ * @param crlList the certificate revocation list. It can be null
+ */
+ public void setSignInfo(PrivateKey privKey, Certificate[] certChain, CRL[] crlList) {
+ try {
+ pkcs = new PdfPKCS7(privKey, certChain, crlList, hashAlgorithm, provider, PdfName.ADBE_PKCS7_SHA1.equals(get(PdfName.SUBFILTER)));
+ pkcs.setExternalDigest(externalDigest, externalRSAdata, digestEncryptionAlgorithm);
+ if (PdfName.ADBE_X509_RSA_SHA1.equals(get(PdfName.SUBFILTER))) {
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ for (int k = 0; k < certChain.length; ++k) {
+ bout.write(certChain[k].getEncoded());
+ }
+ bout.close();
+ setCert(bout.toByteArray());
+ setContents(pkcs.getEncodedPKCS1());
+ }
+ else
+ setContents(pkcs.getEncodedPKCS7());
+ name = PdfPKCS7.getSubjectFields(pkcs.getSigningCertificate()).getField("CN");
+ if (name != null)
+ put(PdfName.NAME, new PdfString(name, PdfObject.TEXT_UNICODE));
+ pkcs = new PdfPKCS7(privKey, certChain, crlList, hashAlgorithm, provider, PdfName.ADBE_PKCS7_SHA1.equals(get(PdfName.SUBFILTER)));
+ pkcs.setExternalDigest(externalDigest, externalRSAdata, digestEncryptionAlgorithm);
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Sets the digest/signature to an external calculated value.
+ * @param digest the digest. This is the actual signature
+ * @param RSAdata the extra data that goes into the data tag in PKCS#7
+ * @param digestEncryptionAlgorithm the encryption algorithm. It may must be null
if the digest
+ * is also null
. If the digest
is not null
+ * then it may be "RSA" or "DSA"
+ */
+ public void setExternalDigest(byte digest[], byte RSAdata[], String digestEncryptionAlgorithm) {
+ externalDigest = digest;
+ externalRSAdata = RSAdata;
+ this.digestEncryptionAlgorithm = digestEncryptionAlgorithm;
+ }
+
+ /**
+ * Gets the subject name in the signing certificate (the element "CN")
+ * @return the subject name in the signing certificate (the element "CN")
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Gets the class instance that does the actual signing.
+ * @return the class instance that does the actual signing
+ */
+ public PdfPKCS7 getSigner() {
+ return pkcs;
+ }
+
+ /**
+ * Gets the signature content. This can be a PKCS#1 or a PKCS#7. It corresponds to
+ * the /Contents key.
+ * @return the signature content
+ */
+ public byte[] getSignerContents() {
+ if (PdfName.ADBE_X509_RSA_SHA1.equals(get(PdfName.SUBFILTER)))
+ return pkcs.getEncodedPKCS1();
+ else
+ return pkcs.getEncodedPKCS7();
+ }
+
+ /**
+ * Creates a standard filter of the type VeriSign.
+ */
+ public static class VeriSign extends PdfSigGenericPKCS {
+ /**
+ * The constructor for the default provider.
+ */
+ public VeriSign() {
+ super(PdfName.VERISIGN_PPKVS, PdfName.ADBE_PKCS7_DETACHED);
+ hashAlgorithm = "MD5";
+ put(PdfName.R, new PdfNumber(65537));
+ }
+
+ /**
+ * The constructor for an explicit provider.
+ * @param provider the crypto provider
+ */
+ public VeriSign(String provider) {
+ this();
+ this.provider = provider;
+ }
+ }
+
+ /**
+ * Creates a standard filter of the type self signed.
+ */
+ public static class PPKLite extends PdfSigGenericPKCS {
+ /**
+ * The constructor for the default provider.
+ */
+ public PPKLite() {
+ super(PdfName.ADOBE_PPKLITE, PdfName.ADBE_X509_RSA_SHA1);
+ hashAlgorithm = "SHA1";
+ put(PdfName.R, new PdfNumber(65541));
+ }
+
+ /**
+ * The constructor for an explicit provider.
+ * @param provider the crypto provider
+ */
+ public PPKLite(String provider) {
+ this();
+ this.provider = provider;
+ }
+ }
+
+ /**
+ * Creates a standard filter of the type Windows Certificate.
+ */
+ public static class PPKMS extends PdfSigGenericPKCS {
+ /**
+ * The constructor for the default provider.
+ */
+ public PPKMS() {
+ super(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
+ hashAlgorithm = "SHA1";
+ }
+
+ /**
+ * The constructor for an explicit provider.
+ * @param provider the crypto provider
+ */
+ public PPKMS(String provider) {
+ this();
+ this.provider = provider;
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfSignature.java b/src/main/java/com/lowagie/text/pdf/PdfSignature.java
new file mode 100644
index 0000000..759a7ae
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfSignature.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/** Implements the signature dictionary.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfSignature extends PdfDictionary {
+
+ /** Creates new PdfSignature */
+ public PdfSignature(PdfName filter, PdfName subFilter) {
+ super(PdfName.SIG);
+ put(PdfName.FILTER, filter);
+ put(PdfName.SUBFILTER, subFilter);
+ }
+
+ public void setByteRange(int range[]) {
+ PdfArray array = new PdfArray();
+ for (int k = 0; k < range.length; ++k)
+ array.add(new PdfNumber(range[k]));
+ put(PdfName.BYTERANGE, array);
+ }
+
+ public void setContents(byte contents[]) {
+ put(PdfName.CONTENTS, new PdfString(contents).setHexWriting(true));
+ }
+
+ public void setCert(byte cert[]) {
+ put(PdfName.CERT, new PdfString(cert));
+ }
+
+ public void setName(String name) {
+ put(PdfName.NAME, new PdfString(name, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setDate(PdfDate date) {
+ put(PdfName.M, date);
+ }
+
+ public void setLocation(String name) {
+ put(PdfName.LOCATION, new PdfString(name, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setReason(String name) {
+ put(PdfName.REASON, new PdfString(name, PdfObject.TEXT_UNICODE));
+ }
+
+ public void setContact(String name) {
+ put(PdfName.CONTACTINFO, new PdfString(name, PdfObject.TEXT_UNICODE));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfSignatureAppearance.java b/src/main/java/com/lowagie/text/pdf/PdfSignatureAppearance.java
new file mode 100644
index 0000000..52b186c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfSignatureAppearance.java
@@ -0,0 +1,1349 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Font;
+import com.lowagie.text.Element;
+import com.lowagie.text.Image;
+import com.lowagie.text.DocumentException;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+import java.text.SimpleDateFormat;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.cert.CRL;
+import java.security.PrivateKey;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.io.RandomAccessFile;
+import java.io.File;
+import java.io.InputStream;
+
+/**
+ * This class takes care of the cryptographic options and appearances that form a signature.
+ */
+public class PdfSignatureAppearance {
+
+ /**
+ * The self signed filter.
+ */
+ public static final PdfName SELF_SIGNED = PdfName.ADOBE_PPKLITE;
+ /**
+ * The VeriSign filter.
+ */
+ public static final PdfName VERISIGN_SIGNED = PdfName.VERISIGN_PPKVS;
+ /**
+ * The Windows Certificate Security.
+ */
+ public static final PdfName WINCER_SIGNED = PdfName.ADOBE_PPKMS;
+
+ private static final float topSection = 0.3f;
+ private static final float margin = 2;
+ private Rectangle rect;
+ private Rectangle pageRect;
+ private PdfTemplate app[] = new PdfTemplate[5];
+ private PdfTemplate frm;
+ private PdfStamperImp writer;
+ private String layer2Text;
+ private String reason;
+ private String location;
+ private Calendar signDate;
+ private String provider;
+ private int page = 1;
+ private String fieldName;
+ private PrivateKey privKey;
+ private Certificate[] certChain;
+ private CRL[] crlList;
+ private PdfName filter;
+ private boolean newField;
+ private ByteBuffer sigout;
+ private OutputStream originalout;
+ private File tempFile;
+ private PdfDictionary cryptoDictionary;
+ private PdfStamper stamper;
+ private boolean preClosed = false;
+ private PdfSigGenericPKCS sigStandard;
+ private int range[];
+ private RandomAccessFile raf;
+ private int rangePosition = 0;
+ private byte bout[];
+ private int boutLen;
+ private byte externalDigest[];
+ private byte externalRSAdata[];
+ private String digestEncryptionAlgorithm;
+ private HashMap exclusionLocations;
+
+ PdfSignatureAppearance(PdfStamperImp writer) {
+ this.writer = writer;
+ signDate = new GregorianCalendar();
+ fieldName = getNewSigName();
+ }
+
+ /**
+ * Sets the signature text identifying the signer.
+ * @param text the signature text identifying the signer. If null
or not set
+ * a standard description will be used
+ */
+ public void setLayer2Text(String text) {
+ layer2Text = text;
+ }
+
+ /**
+ * Gets the signature text identifying the signer if set by setLayer2Text().
+ * @return the signature text identifying the signer
+ */
+ public String getLayer2Text() {
+ return layer2Text;
+ }
+
+ /**
+ * Sets the text identifying the signature status.
+ * @param text the text identifying the signature status. If null
or not set
+ * the description "Signature Not Verified" will be used
+ */
+ public void setLayer4Text(String text) {
+ layer4Text = text;
+ }
+
+ /**
+ * Gets the text identifying the signature status if set by setLayer4Text().
+ * @return the text identifying the signature status
+ */
+ public String getLayer4Text() {
+ return layer4Text;
+ }
+
+ /**
+ * Gets the rectangle representing the signature dimensions.
+ * @return the rectangle representing the signature dimensions. It may be null
+ * or have zero width or height for invisible signatures
+ */
+ public Rectangle getRect() {
+ return rect;
+ }
+
+ /**
+ * Gets the visibility status of the signature.
+ * @return the visibility status of the signature
+ */
+ public boolean isInvisible() {
+ return (rect == null || rect.width() == 0 || rect.height() == 0);
+ }
+
+ /**
+ * Sets the cryptographic parameters.
+ * @param privKey the private key
+ * @param certChain the certificate chain
+ * @param crlList the certificate revocation list. It may be null
+ * @param filter the crytographic filter type. It can be SELF_SIGNED, VERISIGN_SIGNED or WINCER_SIGNED
+ */
+ public void setCrypto(PrivateKey privKey, Certificate[] certChain, CRL[] crlList, PdfName filter) {
+ this.privKey = privKey;
+ this.certChain = certChain;
+ this.crlList = crlList;
+ this.filter = filter;
+ }
+
+ /**
+ * Sets the signature to be visible. It creates a new visible signature field.
+ * @param pageRect the position and dimension of the field in the page
+ * @param page the page to place the field. The fist page is 1
+ * @param fieldName the field name or null
to generate automatically a new field name
+ */
+ public void setVisibleSignature(Rectangle pageRect, int page, String fieldName) {
+ if (fieldName != null) {
+ if (fieldName.indexOf('.') >= 0)
+ throw new IllegalArgumentException("Field names cannot contain a dot.");
+ AcroFields af = writer.getAcroFields();
+ AcroFields.Item item = af.getFieldItem(fieldName);
+ if (item != null)
+ throw new IllegalArgumentException("The field " + fieldName + " already exists.");
+ this.fieldName = fieldName;
+ }
+ if (page < 1 || page > writer.reader.getNumberOfPages())
+ throw new IllegalArgumentException("Invalid page number: " + page);
+ this.pageRect = new Rectangle(pageRect);
+ this.pageRect.normalize();
+ rect = new Rectangle(this.pageRect.width(), this.pageRect.height());
+ this.page = page;
+ newField = true;
+ }
+
+ /**
+ * Sets the signature to be visible. An empty signature field with the same name must already exist.
+ * @param fieldName the existing empty signature field name
+ */
+ public void setVisibleSignature(String fieldName) {
+ AcroFields af = writer.getAcroFields();
+ AcroFields.Item item = af.getFieldItem(fieldName);
+ if (item == null)
+ throw new IllegalArgumentException("The field " + fieldName + " does not exist.");
+ PdfDictionary merged = (PdfDictionary)item.merged.get(0);
+ if (!PdfName.SIG.equals(PdfReader.getPdfObject(merged.get(PdfName.FT))))
+ throw new IllegalArgumentException("The field " + fieldName + " is not a signature field.");
+ this.fieldName = fieldName;
+ PdfArray r = (PdfArray)PdfReader.getPdfObject(merged.get(PdfName.RECT));
+ ArrayList ar = r.getArrayList();
+ float llx = ((PdfNumber)PdfReader.getPdfObject((PdfObject)ar.get(0))).floatValue();
+ float lly = ((PdfNumber)PdfReader.getPdfObject((PdfObject)ar.get(1))).floatValue();
+ float urx = ((PdfNumber)PdfReader.getPdfObject((PdfObject)ar.get(2))).floatValue();
+ float ury = ((PdfNumber)PdfReader.getPdfObject((PdfObject)ar.get(3))).floatValue();
+ pageRect = new Rectangle(llx, lly, urx, ury);
+ pageRect.normalize();
+ page = ((Integer)item.page.get(0)).intValue();
+ int rotation = writer.reader.getPageRotation(page);
+ Rectangle pageSize = writer.reader.getPageSizeWithRotation(page);
+ switch (rotation) {
+ case 90:
+ pageRect = new Rectangle(
+ pageRect.bottom(),
+ pageSize.top() - pageRect.left(),
+ pageRect.top(),
+ pageSize.top() - pageRect.right());
+ break;
+ case 180:
+ pageRect = new Rectangle(
+ pageSize.right() - pageRect.left(),
+ pageSize.top() - pageRect.bottom(),
+ pageSize.right() - pageRect.right(),
+ pageSize.top() - pageRect.top());
+ break;
+ case 270:
+ pageRect = new Rectangle(
+ pageSize.right() - pageRect.bottom(),
+ pageRect.left(),
+ pageSize.right() - pageRect.top(),
+ pageRect.right());
+ break;
+ }
+ if (rotation != 0)
+ pageRect.normalize();
+ rect = new Rectangle(this.pageRect.width(), this.pageRect.height());
+ }
+
+ /**
+ * Gets a template layer to create a signature appearance. The layers can go from 0 to 4.
+ * null
if the digest
+ * is also null
. If the digest
is not null
+ * then it may be "RSA" or "DSA"
+ */
+ public void setExternalDigest(byte digest[], byte RSAdata[], String digestEncryptionAlgorithm) {
+ externalDigest = digest;
+ externalRSAdata = RSAdata;
+ this.digestEncryptionAlgorithm = digestEncryptionAlgorithm;
+ }
+
+ /**
+ * Gets the signing reason.
+ * @return the signing reason
+ */
+ public String getReason() {
+ return this.reason;
+ }
+
+ /**
+ * Sets the signing reason.
+ * @param reason the signing reason
+ */
+ public void setReason(String reason) {
+ this.reason = reason;
+ }
+
+ /**
+ * Gets the signing location.
+ * @return the signing location
+ */
+ public String getLocation() {
+ return this.location;
+ }
+
+ /**
+ * Sets the signing location.
+ * @param location the signing location
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Returns the Cryptographic Service Provider that will sign the document.
+ * @return provider the name of the provider, for example "SUN",
+ * or null
to use the default provider.
+ */
+ public String getProvider() {
+ return this.provider;
+ }
+
+ /**
+ * Sets the Cryptographic Service Provider that will sign the document.
+ *
+ * @param provider the name of the provider, for example "SUN", or
+ * null
to use the default provider.
+ */
+ public void setProvider(String provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * Gets the private key.
+ * @return the private key
+ */
+ public java.security.PrivateKey getPrivKey() {
+ return privKey;
+ }
+
+ /**
+ * Gets the certificate chain.
+ * @return the certificate chain
+ */
+ public java.security.cert.Certificate[] getCertChain() {
+ return this.certChain;
+ }
+
+ /**
+ * Gets the certificate revocation list.
+ * @return the certificate revocation list
+ */
+ public java.security.cert.CRL[] getCrlList() {
+ return this.crlList;
+ }
+
+ /**
+ * Gets the filter used to sign the document.
+ * @return the filter used to sign the document
+ */
+ public com.lowagie.text.pdf.PdfName getFilter() {
+ return filter;
+ }
+
+ /**
+ * Checks if a new field was created.
+ * @return true
if a new field was created, false
if signing
+ * an existing field or if the signature is invisible
+ */
+ public boolean isNewField() {
+ return this.newField;
+ }
+
+ /**
+ * Gets the page number of the field.
+ * @return the page number of the field
+ */
+ public int getPage() {
+ return page;
+ }
+
+ /**
+ * Gets the field name.
+ * @return the field name
+ */
+ public java.lang.String getFieldName() {
+ return fieldName;
+ }
+
+ /**
+ * Gets the rectangle that represent the position and dimension of the signature in the page.
+ * @return the rectangle that represent the position and dimension of the signature in the page
+ */
+ public com.lowagie.text.Rectangle getPageRect() {
+ return pageRect;
+ }
+
+ /**
+ * Gets the signature date.
+ * @return the signature date
+ */
+ public java.util.Calendar getSignDate() {
+ return signDate;
+ }
+
+ /**
+ * Sets the signature date.
+ * @param signDate the signature date
+ */
+ public void setSignDate(java.util.Calendar signDate) {
+ this.signDate = signDate;
+ }
+
+ com.lowagie.text.pdf.ByteBuffer getSigout() {
+ return sigout;
+ }
+
+ void setSigout(com.lowagie.text.pdf.ByteBuffer sigout) {
+ this.sigout = sigout;
+ }
+
+ java.io.OutputStream getOriginalout() {
+ return originalout;
+ }
+
+ void setOriginalout(java.io.OutputStream originalout) {
+ this.originalout = originalout;
+ }
+
+ /**
+ * Gets the temporary file.
+ * @return the temporary file or null
is the document is created in memory
+ */
+ public java.io.File getTempFile() {
+ return tempFile;
+ }
+
+ void setTempFile(java.io.File tempFile) {
+ this.tempFile = tempFile;
+ }
+
+ /**
+ * Gets a new signature fied name that doesn't clash with any existing name.
+ * @return a new signature fied name
+ */
+ public String getNewSigName() {
+ AcroFields af = writer.getAcroFields();
+ String name = "Signature";
+ int step = 0;
+ boolean found = false;
+ while (!found) {
+ ++step;
+ String n1 = name + step;
+ if (af.getFieldItem(n1) != null)
+ continue;
+ n1 += ".";
+ found = true;
+ for (Iterator it = af.getFields().keySet().iterator(); it.hasNext();) {
+ String fn = (String)it.next();
+ if (fn.startsWith(n1)) {
+ found = false;
+ break;
+ }
+ }
+ }
+ name += step;
+ return name;
+ }
+
+ /**
+ * This is the first method to be called when using external signatures. The general sequence is:
+ * preClose(), getDocumentBytes() and close().
+ * exclusionSizes
must contain at least
+ * the PdfName.CONTENTS
key with the size that it will take in the
+ * document. Note that due to the hex string coding this size should be
+ * byte_size*2+2.
+ * @param exclusionSizes a HashMap
with names and sizes to be excluded in the signature
+ * calculation. The key is a PdfName
and the value an
+ * Integer
. At least the PdfName.CONTENTS
must be present
+ * @throws IOException on error
+ * @throws DocumentException on error
+ */
+ public void preClose(HashMap exclusionSizes) throws IOException, DocumentException {
+ if (preClosed)
+ throw new DocumentException("Document already pre closed.");
+ preClosed = true;
+ AcroFields af = writer.getAcroFields();
+ String name = getFieldName();
+ boolean fieldExists = !(isInvisible() || isNewField());
+ int flags = 132;
+ PdfIndirectReference refSig = writer.getPdfIndirectReference();
+ if (fieldExists && name.indexOf('.') >= 0) {
+ ArrayList widgets = af.getFieldItem(name).widgets;
+ PdfDictionary widget = (PdfDictionary)widgets.get(0);
+ writer.markUsed(widget);
+ widget.put(PdfName.P, writer.getPageReference(getPage()));
+ widget.put(PdfName.V, refSig);
+ PdfDictionary ap = new PdfDictionary();
+ ap.put(PdfName.N, getAppearance().getIndirectReference());
+ widget.put(PdfName.AP, ap);
+ }
+ else {
+ if (fieldExists) {
+ flags = 0;
+ ArrayList merged = af.getFieldItem(name).merged;
+ PdfObject obj = PdfReader.getPdfObjectRelease(((PdfDictionary)merged.get(0)).get(PdfName.F));
+ if (obj != null && obj.isNumber())
+ flags = ((PdfNumber)obj).intValue();
+ af.removeField(name);
+ }
+ writer.setSigFlags(3);
+ PdfFormField sigField = PdfFormField.createSignature(writer);
+ sigField.setFieldName(name);
+ sigField.put(PdfName.V, refSig);
+ sigField.setFlags(flags);
+
+ int pagen = getPage();
+ if (!isInvisible()) {
+ sigField.setWidget(getPageRect(), null);
+ sigField.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, getAppearance());
+ }
+ else
+ sigField.setWidget(new Rectangle(0, 0), null);
+ sigField.setPage(pagen);
+ writer.addAnnotation(sigField, pagen);
+ }
+
+ exclusionLocations = new HashMap();
+ if (cryptoDictionary == null) {
+ if (PdfName.ADOBE_PPKLITE.equals(getFilter()))
+ sigStandard = new PdfSigGenericPKCS.PPKLite(getProvider());
+ else if (PdfName.ADOBE_PPKMS.equals(getFilter()))
+ sigStandard = new PdfSigGenericPKCS.PPKMS(getProvider());
+ else if (PdfName.VERISIGN_PPKVS.equals(getFilter()))
+ sigStandard = new PdfSigGenericPKCS.VeriSign(getProvider());
+ else
+ throw new IllegalArgumentException("Unknown filter: " + getFilter());
+ sigStandard.setExternalDigest(externalDigest, externalRSAdata, digestEncryptionAlgorithm);
+ if (getReason() != null)
+ sigStandard.setReason(getReason());
+ if (getLocation() != null)
+ sigStandard.setLocation(getLocation());
+ if (getContact() != null)
+ sigStandard.setContact(getContact());
+ sigStandard.put(PdfName.M, new PdfDate(getSignDate()));
+ sigStandard.setSignInfo(getPrivKey(), getCertChain(), getCrlList());
+ PdfString contents = (PdfString)sigStandard.get(PdfName.CONTENTS);
+ PdfLiteral lit = new PdfLiteral((contents.toString().length() + (PdfName.ADOBE_PPKLITE.equals(getFilter())?0:64)) * 2 + 2);
+ exclusionLocations.put(PdfName.CONTENTS, lit);
+ sigStandard.put(PdfName.CONTENTS, lit);
+ lit = new PdfLiteral(80);
+ exclusionLocations.put(PdfName.BYTERANGE, lit);
+ sigStandard.put(PdfName.BYTERANGE, lit);
+ if (certified)
+ addDocMDP(sigStandard);
+ if (signatureEvent != null)
+ signatureEvent.getSignatureDictionary(sigStandard);
+ writer.addToBody(sigStandard, refSig, false);
+ }
+ else {
+ PdfLiteral lit = new PdfLiteral(80);
+ exclusionLocations.put(PdfName.BYTERANGE, lit);
+ cryptoDictionary.put(PdfName.BYTERANGE, lit);
+ for (Iterator it = exclusionSizes.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry)it.next();
+ PdfName key = (PdfName)entry.getKey();
+ Integer v = (Integer)entry.getValue();
+ lit = new PdfLiteral(v.intValue());
+ exclusionLocations.put(key, lit);
+ cryptoDictionary.put(key, lit);
+ }
+ if (certified)
+ addDocMDP(cryptoDictionary);
+ if (signatureEvent != null)
+ signatureEvent.getSignatureDictionary(cryptoDictionary);
+ writer.addToBody(cryptoDictionary, refSig, false);
+ }
+ if (certified) {
+ // add DocMDP entry to root
+ PdfDictionary docmdp = new PdfDictionary();
+ docmdp.put(new PdfName("DocMDP"), refSig);
+ writer.reader.getCatalog().put(new PdfName("Perms"), docmdp);
+ }
+ writer.close(stamper.getMoreInfo());
+
+ range = new int[exclusionLocations.size() * 2];
+ int byteRangePosition = ((PdfLiteral)exclusionLocations.get(PdfName.BYTERANGE)).getPosition();
+ exclusionLocations.remove(PdfName.BYTERANGE);
+ int idx = 1;
+ for (Iterator it = exclusionLocations.values().iterator(); it.hasNext();) {
+ PdfLiteral lit = (PdfLiteral)it.next();
+ int n = lit.getPosition();
+ range[idx++] = n;
+ range[idx++] = lit.getPosLength() + n;
+ }
+ Arrays.sort(range, 1, range.length - 1);
+ for (int k = 3; k < range.length - 2; k += 2)
+ range[k] -= range[k - 1];
+
+ if (tempFile == null) {
+ bout = sigout.getBuffer();
+ boutLen = sigout.size();
+ range[range.length - 1] = boutLen - range[range.length - 2];
+ ByteBuffer bf = new ByteBuffer();
+ bf.append('[');
+ for (int k = 0; k < range.length; ++k)
+ bf.append(range[k]).append(' ');
+ bf.append(']');
+ System.arraycopy(bf.getBuffer(), 0, bout, byteRangePosition, bf.size());
+ }
+ else {
+ try {
+ raf = new RandomAccessFile(tempFile, "rw");
+ int boutLen = (int)raf.length();
+ range[range.length - 1] = boutLen - range[range.length - 2];
+ ByteBuffer bf = new ByteBuffer();
+ bf.append('[');
+ for (int k = 0; k < range.length; ++k)
+ bf.append(range[k]).append(' ');
+ bf.append(']');
+ raf.seek(byteRangePosition);
+ raf.write(bf.getBuffer(), 0, bf.size());
+ }
+ catch (IOException e) {
+ try{raf.close();}catch(Exception ee){}
+ try{tempFile.delete();}catch(Exception ee){}
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * This is the last method to be called when using external signatures. The general sequence is:
+ * preClose(), getDocumentBytes() and close().
+ * update
is a PdfDictionary
that must have exactly the
+ * same keys as the ones provided in {@link #preClose(HashMap)}.
+ * @param update a PdfDictionary
with the key/value that will fill the holes defined
+ * in {@link #preClose(HashMap)}
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public void close(PdfDictionary update) throws IOException, DocumentException {
+ try {
+ if (!preClosed)
+ throw new DocumentException("preClose() must be called first.");
+ ByteBuffer bf = new ByteBuffer();
+ for (Iterator it = update.getKeys().iterator(); it.hasNext();) {
+ PdfName key = (PdfName)it.next();
+ PdfObject obj = update.get(key);
+ PdfLiteral lit = (PdfLiteral)exclusionLocations.get(key);
+ if (lit == null)
+ throw new IllegalArgumentException("The key " + key.toString() + " didn't reserve space in preClose().");
+ bf.reset();
+ obj.toPdf(null, bf);
+ if (bf.size() > lit.getPosLength())
+ throw new IllegalArgumentException("The key " + key.toString() + " is too big. Is " + bf.size() + ", reserved " + lit.getPosLength());
+ if (tempFile == null)
+ System.arraycopy(bf.getBuffer(), 0, bout, lit.getPosition(), bf.size());
+ else {
+ raf.seek(lit.getPosition());
+ raf.write(bf.getBuffer(), 0, bf.size());
+ }
+ }
+ if (update.size() != exclusionLocations.size())
+ throw new IllegalArgumentException("The update dictionary has less keys than required.");
+ if (tempFile == null) {
+ originalout.write(bout, 0, boutLen);
+ }
+ else {
+ if (originalout != null) {
+ raf.seek(0);
+ int length = (int)raf.length();
+ byte buf[] = new byte[8192];
+ while (length > 0) {
+ int r = raf.read(buf, 0, Math.min(buf.length, length));
+ if (r < 0)
+ throw new EOFException("Unexpected EOF");
+ originalout.write(buf, 0, r);
+ length -= r;
+ }
+ }
+ }
+ }
+ finally {
+ if (tempFile != null) {
+ try{raf.close();}catch(Exception ee){}
+ if (originalout != null)
+ try{tempFile.delete();}catch(Exception ee){}
+ }
+ if (originalout != null)
+ try{originalout.close();}catch(Exception e){}
+ }
+ }
+
+ private void addDocMDP(PdfDictionary crypto) {
+ PdfDictionary reference = new PdfDictionary();
+ PdfDictionary transformParams = new PdfDictionary();
+ transformParams.put(PdfName.P, new PdfNumber(1));
+ transformParams.put(PdfName.V, new PdfName("1.2"));
+ transformParams.put(PdfName.TYPE, new PdfName("TransformParams"));
+ reference.put(new PdfName("TransformMethod"), new PdfName("DocMDP"));
+ reference.put(PdfName.TYPE, new PdfName("SigRef"));
+ reference.put(new PdfName("TransformParams"), transformParams);
+ PdfArray types = new PdfArray();
+ types.add(reference);
+ crypto.put(new PdfName("Reference"), types);
+ }
+
+ private static int indexArray(byte bout[], int position, String search) {
+ byte ss[] = PdfEncodings.convertToBytes(search, null);
+ while (true) {
+ int k;
+ for (k = 0; k < ss.length; ++k) {
+ if (ss[k] != bout[position + k])
+ break;
+ }
+ if (k == ss.length)
+ return position;
+ ++position;
+ }
+ }
+
+ private static int indexFile(RandomAccessFile raf, int position, String search) throws IOException {
+ byte ss[] = PdfEncodings.convertToBytes(search, null);
+ while (true) {
+ raf.seek(position);
+ int k;
+ for (k = 0; k < ss.length; ++k) {
+ int b = raf.read();
+ if (b < 0)
+ throw new EOFException("Unexpected EOF");
+ if (ss[k] != (byte)b)
+ break;
+ }
+ if (k == ss.length)
+ return position;
+ ++position;
+ }
+ }
+
+ /**
+ * Gets the document bytes that are hashable when using external signatures. The general sequence is:
+ * preClose(), getRangeStream() and close().
+ * PdfStamper
associated with this instance.
+ * @return the PdfStamper
associated with this instance
+ */
+ public com.lowagie.text.pdf.PdfStamper getStamper() {
+ return stamper;
+ }
+
+ void setStamper(com.lowagie.text.pdf.PdfStamper stamper) {
+ this.stamper = stamper;
+ }
+
+ /**
+ * Checks if the document is in the process of closing.
+ * @return true
if the document is in the process of closing,
+ * false
otherwise
+ */
+ public boolean isPreClosed() {
+ return preClosed;
+ }
+
+ /**
+ * Gets the instance of the standard signature dictionary. This instance
+ * is only available after pre close.
+ * true
only the layers n2 and n4 will be present
+ */
+ public void setAcro6Layers(boolean acro6Layers) {
+ this.acro6Layers = acro6Layers;
+ }
+
+ /** Sets the run direction in the n2 and n4 layer.
+ * @param runDirection the run direction
+ */
+ public void setRunDirection(int runDirection) {
+ if (runDirection < PdfWriter.RUN_DIRECTION_DEFAULT || runDirection > PdfWriter.RUN_DIRECTION_RTL)
+ throw new RuntimeException("Invalid run direction: " + runDirection);
+ this.runDirection = runDirection;
+ }
+
+ /** Gets the run direction.
+ * @return the run direction
+ */
+ public int getRunDirection() {
+ return runDirection;
+ }
+
+ /**
+ * Getter for property signatureEvent.
+ * @return Value of property signatureEvent.
+ */
+ public SignatureEvent getSignatureEvent() {
+ return this.signatureEvent;
+ }
+
+ /**
+ * Sets the signature event to allow modification of the signature dictionary.
+ * @param signatureEvent the signature event
+ */
+ public void setSignatureEvent(SignatureEvent signatureEvent) {
+ this.signatureEvent = signatureEvent;
+ }
+
+ /**
+ * Gets the background image for the layer 2.
+ * @return the background image for the layer 2
+ */
+ public Image getImage() {
+ return this.image;
+ }
+
+ /**
+ * Sets the background image for the layer 2.
+ * @param image the background image for the layer 2
+ */
+ public void setImage(Image image) {
+ this.image = image;
+ }
+
+ /**
+ * Gets the scaling to be applied to the background image.
+ * @return the scaling to be applied to the background image
+ */
+ public float getImageScale() {
+ return this.imageScale;
+ }
+
+ /**
+ * Sets the scaling to be applied to the background image. If it's zero the image
+ * will fully fill the rectangle. If it's less than zero the image will fill the rectangle but
+ * will keep the proportions. If it's greater than zero that scaling will be applied.
+ * In any of the cases the image will always be centered. It's zero by default.
+ * @param imageScale the scaling to be applied to the background image
+ */
+ public void setImageScale(float imageScale) {
+ this.imageScale = imageScale;
+ }
+
+ /**
+ * Commands to draw a yellow question mark in a stream content
+ */
+ public static final String questionMark =
+ "% DSUnknown\n" +
+ "q\n" +
+ "1 G\n" +
+ "1 g\n" +
+ "0.1 0 0 0.1 9 0 cm\n" +
+ "0 J 0 j 4 M []0 d\n" +
+ "1 i \n" +
+ "0 g\n" +
+ "313 292 m\n" +
+ "313 404 325 453 432 529 c\n" +
+ "478 561 504 597 504 645 c\n" +
+ "504 736 440 760 391 760 c\n" +
+ "286 760 271 681 265 626 c\n" +
+ "265 625 l\n" +
+ "100 625 l\n" +
+ "100 828 253 898 381 898 c\n" +
+ "451 898 679 878 679 650 c\n" +
+ "679 555 628 499 538 435 c\n" +
+ "488 399 467 376 467 292 c\n" +
+ "313 292 l\n" +
+ "h\n" +
+ "308 214 170 -164 re\n" +
+ "f\n" +
+ "0.44 G\n" +
+ "1.2 w\n" +
+ "1 1 0.4 rg\n" +
+ "287 318 m\n" +
+ "287 430 299 479 406 555 c\n" +
+ "451 587 478 623 478 671 c\n" +
+ "478 762 414 786 365 786 c\n" +
+ "260 786 245 707 239 652 c\n" +
+ "239 651 l\n" +
+ "74 651 l\n" +
+ "74 854 227 924 355 924 c\n" +
+ "425 924 653 904 653 676 c\n" +
+ "653 581 602 525 512 461 c\n" +
+ "462 425 441 402 441 318 c\n" +
+ "287 318 l\n" +
+ "h\n" +
+ "282 240 170 -164 re\n" +
+ "B\n" +
+ "Q\n";
+
+ /**
+ * Holds value of property contact.
+ */
+ private String contact;
+
+ /**
+ * Holds value of property layer2Font.
+ */
+ private Font layer2Font;
+
+ /**
+ * Holds value of property layer4Text.
+ */
+ private String layer4Text;
+
+ /**
+ * Holds value of property acro6Layers.
+ */
+ private boolean acro6Layers;
+
+ /**
+ * Holds value of property runDirection.
+ */
+ private int runDirection = PdfWriter.RUN_DIRECTION_NO_BIDI;
+
+ /**
+ * Holds value of property signatureEvent.
+ */
+ private SignatureEvent signatureEvent;
+
+ /**
+ * Holds value of property image.
+ */
+ private Image image;
+
+ /**
+ * Holds value of property imageScale.
+ */
+ private float imageScale;
+
+ /**
+ *
+ */
+ private static class RangeStream extends InputStream {
+ private byte b[] = new byte[1];
+ private RandomAccessFile raf;
+ private byte bout[];
+ private int range[];
+ private int rangePosition = 0;
+
+ private RangeStream(RandomAccessFile raf, byte bout[], int range[]) {
+ this.raf = raf;
+ this.bout = bout;
+ this.range = range;
+ }
+
+ /**
+ * @see java.io.InputStream#read()
+ */
+ public int read() throws IOException {
+ int n = read(b);
+ if (n != 1)
+ return -1;
+ return b[0] & 0xff;
+ }
+
+ /**
+ * @see java.io.InputStream#read(byte[], int, int)
+ */
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+ } else if ((off < 0) || (off > b.length) || (len < 0) ||
+ ((off + len) > b.length) || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+ if (rangePosition >= range[range.length - 2] + range[range.length - 1]) {
+ return -1;
+ }
+ for (int k = 0; k < range.length; k += 2) {
+ int start = range[k];
+ int end = start + range[k + 1];
+ if (rangePosition < start)
+ rangePosition = start;
+ if (rangePosition >= start && rangePosition < end) {
+ int lenf = Math.min(len, end - rangePosition);
+ if (raf == null)
+ System.arraycopy(bout, rangePosition, b, off, lenf);
+ else {
+ raf.seek(rangePosition);
+ raf.readFully(b, off, lenf);
+ }
+ rangePosition += lenf;
+ return lenf;
+ }
+ }
+ return -1;
+ }
+ }
+
+ /**
+ * An interface to retrieve the signature dictionary for modification.
+ */
+ public interface SignatureEvent {
+ /**
+ * Allows modification of the signature dictionary.
+ * @param sig the signature dictionary
+ */
+ public void getSignatureDictionary(PdfDictionary sig);
+ }
+
+ /**
+ * Holds value of property certified.
+ */
+ private boolean certified;
+
+ /**
+ * Gets the certified status of this document.
+ * @return the certified status
+ */
+ public boolean isCertified() {
+ return this.certified;
+ }
+
+ /**
+ * Sets the document type to certified instead of simply signed. The certified document doesn't allow any changes.
+ * @param certified true
to certify the document, false
to just apply a simple signature
+ */
+ public void setCertified(boolean certified) {
+ this.certified = certified;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfSpotColor.java b/src/main/java/com/lowagie/text/pdf/PdfSpotColor.java
new file mode 100644
index 0000000..4bb2de6
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfSpotColor.java
@@ -0,0 +1,132 @@
+/*
+ * $Id: PdfSpotColor.java,v 1.46 2005/06/08 11:09:28 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.awt.Color;
+import java.io.IOException;
+/**
+ * A PdfSpotColor
defines a ColorSpace
+ *
+ * @see PdfDictionary
+ */
+
+public class PdfSpotColor{
+
+/* The tint value */
+ protected float tint;
+
+/** The color name */
+ public PdfName name;
+
+/** The alternative color space */
+ public Color altcs;
+ // constructors
+
+ /**
+ * Constructs a new PdfSpotColor
.
+ *
+ * @param name a String value
+ * @param tint a tint value between 0 and 1
+ * @param altcs a altnative colorspace value
+ */
+
+ public PdfSpotColor(String name, float tint, Color altcs) {
+ this.name = new PdfName(name);
+ this.tint = tint;
+ this.altcs = altcs;
+ }
+
+ /**
+ * Gets the tint of the SpotColor.
+ * @return a float
+ */
+ public float getTint() {
+ return tint;
+ }
+
+ /**
+ * Gets the alternative ColorSpace.
+ * @return a Colot
+ */
+ public Color getAlternativeCS() {
+ return altcs;
+ }
+
+ protected PdfObject getSpotObject(PdfWriter writer) throws IOException {
+ PdfArray array = new PdfArray(PdfName.SEPARATION);
+ array.add(name);
+ PdfFunction func = null;
+ if (altcs instanceof ExtendedColor) {
+ int type = ((ExtendedColor)altcs).type;
+ switch (type) {
+ case ExtendedColor.TYPE_GRAY:
+ array.add(PdfName.DEVICEGRAY);
+ func = PdfFunction.type2(writer, new float[]{0, 1}, null, new float[]{0}, new float[]{((GrayColor)altcs).getGray()}, 1);
+ break;
+ case ExtendedColor.TYPE_CMYK:
+ array.add(PdfName.DEVICECMYK);
+ CMYKColor cmyk = (CMYKColor)altcs;
+ func = PdfFunction.type2(writer, new float[]{0, 1}, null, new float[]{0, 0, 0, 0},
+ new float[]{cmyk.getCyan(), cmyk.getMagenta(), cmyk.getYellow(), cmyk.getBlack()}, 1);
+ break;
+ default:
+ throw new RuntimeException("Only RGB, Gray and CMYK are supported as alternative color spaces.");
+ }
+ }
+ else {
+ array.add(PdfName.DEVICERGB);
+ func = PdfFunction.type2(writer, new float[]{0, 1}, null, new float[]{1, 1, 1},
+ new float[]{(float)altcs.getRed() / 255, (float)altcs.getGreen() / 255, (float)altcs.getBlue() / 255}, 1);
+ }
+ array.add(func.getReference());
+ return array;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfStamper.java b/src/main/java/com/lowagie/text/pdf/PdfStamper.java
new file mode 100644
index 0000000..f0d6eea
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfStamper.java
@@ -0,0 +1,667 @@
+/*
+ * Copyright 2003, 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.security.SignatureException;
+import java.io.OutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.io.RandomAccessFile;
+import java.io.File;
+import java.io.InputStream;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.DocWriter;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Image;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Iterator;
+
+/** Applies extra content to the pages of a PDF document.
+ * This extra content can be all the objects allowed in PdfContentByte
+ * including pages from other Pdfs. The original PDF will keep
+ * all the interactive elements including bookmarks, links and form fields.
+ * true
appends the document changes as a new revision. This is
+ * only useful for multiple signatures as nothing is gained in speed or memory
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public PdfStamper(PdfReader reader, OutputStream os, char pdfVersion, boolean append) throws DocumentException, IOException {
+ stamper = new PdfStamperImp(reader, os, pdfVersion, append);
+ }
+
+ /** Gets the optional String
map to add or change values in
+ * the info dictionary.
+ * @return the map or null
+ *
+ */
+ public HashMap getMoreInfo() {
+ return this.moreInfo;
+ }
+
+ /** An optional String
map to add or change values in
+ * the info dictionary. Entries with null
+ * values delete the key in the original info dictionary
+ * @param moreInfo additional entries to the info dictionary
+ *
+ */
+ public void setMoreInfo(HashMap moreInfo) {
+ this.moreInfo = moreInfo;
+ }
+
+ /**
+ * Inserts a blank page. All the pages above and including pageNumber
will
+ * be shifted up. If pageNumber
is bigger than the total number of pages
+ * the new page will be the last one.
+ * @param pageNumber the page number position where the new page will be inserted
+ * @param mediabox the size of the new page
+ */
+ public void insertPage(int pageNumber, Rectangle mediabox) {
+ stamper.insertPage(pageNumber, mediabox);
+ }
+
+ /**
+ * Gets the signing instance. The appearances and other parameters can the be set.
+ * @return the signing instance
+ */
+ public PdfSignatureAppearance getSignatureAppearance() {
+ return sigApp;
+ }
+
+ private String getNewSigName() {
+ AcroFields af = getAcroFields();
+ String name = "Signature";
+ int step = 0;
+ boolean found = false;
+ while (!found) {
+ ++step;
+ String n1 = name + step;
+ if (af.getFieldItem(n1) != null)
+ continue;
+ n1 += ".";
+ found = true;
+ for (Iterator it = af.getFields().keySet().iterator(); it.hasNext();) {
+ String fn = (String)it.next();
+ if (fn.startsWith(n1)) {
+ found = false;
+ break;
+ }
+ }
+ }
+ name += step;
+ return name;
+ }
+ /**
+ * Closes the document. No more content can be written after the
+ * document is closed.
+ * PdfSignatureAppearance
instance.
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public void close() throws DocumentException, IOException {
+ if (!hasSignature) {
+ stamper.close(moreInfo);
+ return;
+ }
+ sigApp.preClose();
+ PdfSigGenericPKCS sig = sigApp.getSigStandard();
+ PdfLiteral lit = (PdfLiteral)sig.get(PdfName.CONTENTS);
+ int totalBuf = (lit.getPosLength() - 2) / 2;
+ byte buf[] = new byte[8192];
+ int n;
+ InputStream inp = sigApp.getRangeStream();
+ try {
+ while ((n = inp.read(buf)) > 0) {
+ sig.getSigner().update(buf, 0, n);
+ }
+ }
+ catch (SignatureException se) {
+ throw new ExceptionConverter(se);
+ }
+ buf = new byte[totalBuf];
+ byte[] bsig = sig.getSignerContents();
+ System.arraycopy(bsig, 0, buf, 0, bsig.length);
+ PdfString str = new PdfString(buf);
+ str.setHexWriting(true);
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.CONTENTS, str);
+ sigApp.close(dic);
+ stamper.reader.close();
+ }
+
+ private static int indexArray(byte bout[], int position, String search) {
+ byte ss[] = PdfEncodings.convertToBytes(search, null);
+ while (true) {
+ int k;
+ for (k = 0; k < ss.length; ++k) {
+ if (ss[k] != bout[position + k])
+ break;
+ }
+ if (k == ss.length)
+ return position;
+ ++position;
+ }
+ }
+
+ private static int indexFile(RandomAccessFile raf, int position, String search) throws IOException {
+ byte ss[] = PdfEncodings.convertToBytes(search, null);
+ while (true) {
+ raf.seek(position);
+ int k;
+ for (k = 0; k < ss.length; ++k) {
+ int b = raf.read();
+ if (b < 0)
+ throw new EOFException("Unexpected EOF");
+ if (ss[k] != (byte)b)
+ break;
+ }
+ if (k == ss.length)
+ return position;
+ ++position;
+ }
+ }
+
+ /** Gets a PdfContentByte
to write under the page of
+ * the original document.
+ * @param pageNum the page number where the extra content is written
+ * @return a PdfContentByte
to write under the page of
+ * the original document
+ */
+ public PdfContentByte getUnderContent(int pageNum) {
+ return stamper.getUnderContent(pageNum);
+ }
+
+ /** Gets a PdfContentByte
to write over the page of
+ * the original document.
+ * @param pageNum the page number where the extra content is written
+ * @return a PdfContentByte
to write over the page of
+ * the original document
+ */
+ public PdfContentByte getOverContent(int pageNum) {
+ return stamper.getOverContent(pageNum);
+ }
+
+ /** Checks if the content is automatically adjusted to compensate
+ * the original page rotation.
+ * @return the auto-rotation status
+ */
+ public boolean isRotateContents() {
+ return stamper.isRotateContents();
+ }
+
+ /** Flags the content to be automatically adjusted to compensate
+ * the original page rotation. The default is true
.
+ * @param rotateContents true
to set auto-rotation, false
+ * otherwise
+ */
+ public void setRotateContents(boolean rotateContents) {
+ stamper.setRotateContents(rotateContents);
+ }
+
+ /** Sets the encryption options for this document. The userPassword and the
+ * ownerPassword can be null or have zero length. In this case the ownerPassword
+ * is replaced by a random string. The open permissions for the document can be
+ * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
+ * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
+ * The permissions can be combined by ORing them.
+ * @param userPassword the user password. Can be null or empty
+ * @param ownerPassword the owner password. Can be null or empty
+ * @param permissions the user permissions
+ * @param strength128Bits true
for 128 bit key length, false
for 40 bit key length
+ * @throws DocumentException if anything was already written to the output
+ */
+ public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, boolean strength128Bits) throws DocumentException {
+ if (stamper.isAppend())
+ throw new DocumentException("Append mode does not support changing the encryption status.");
+ if (stamper.isContentWritten())
+ throw new DocumentException("Content was already written to the output.");
+ stamper.setEncryption(userPassword, ownerPassword, permissions, strength128Bits);
+ }
+
+ /**
+ * Sets the encryption options for this document. The userPassword and the
+ * ownerPassword can be null or have zero length. In this case the ownerPassword
+ * is replaced by a random string. The open permissions for the document can be
+ * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
+ * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
+ * The permissions can be combined by ORing them.
+ * @param strength true
for 128 bit key length, false
for 40 bit key length
+ * @param userPassword the user password. Can be null or empty
+ * @param ownerPassword the owner password. Can be null or empty
+ * @param permissions the user permissions
+ * @throws DocumentException if anything was already written to the output
+ */
+ public void setEncryption(boolean strength, String userPassword, String ownerPassword, int permissions) throws DocumentException {
+ setEncryption(DocWriter.getISOBytes(userPassword), DocWriter.getISOBytes(ownerPassword), permissions, strength);
+ }
+
+ /** Gets a page from other PDF document. Note that calling this method more than
+ * once with the same parameters will retrieve the same object.
+ * @param reader the PDF document where the page is
+ * @param pageNumber the page number. The first page is 1
+ * @return the template representing the imported page
+ */
+ public PdfImportedPage getImportedPage(PdfReader reader, int pageNumber) {
+ return stamper.getImportedPage(reader, pageNumber);
+ }
+
+ /** Gets the underlying PdfWriter.
+ * @return the underlying PdfWriter
+ */
+ public PdfWriter getWriter() {
+ return stamper;
+ }
+
+ /** Gets the underlying PdfReader.
+ * @return the underlying PdfReader
+ */
+ public PdfReader getReader() {
+ return stamper.reader;
+ }
+
+ /** Gets the AcroFields
object that allows to get and set field values
+ * and to merge FDF forms.
+ * @return the AcroFields
object
+ */
+ public AcroFields getAcroFields() {
+ return stamper.getAcroFields();
+ }
+
+ /** Determines if the fields are flattened on close. The fields added with
+ * {@link #addAnnotation(PdfAnnotation,int)} will never be flattened.
+ * @param flat true
to flatten the fields, false
+ * to keep the fields
+ */
+ public void setFormFlattening(boolean flat) {
+ stamper.setFormFlattening(flat);
+ }
+
+ /** Determines if the FreeText annotations are flattened on close.
+ * @param flat true
to flatten the FreeText annotations, false
+ * (the default) to keep the FreeText annotations as active content.
+ */
+ public void setFreeTextFlattening(boolean flat) {
+ stamper.setFreeTextFlattening(flat);
+ }
+
+ /**
+ * Adds an annotation of form field in a specific page. This page number
+ * can be overridden with {@link PdfAnnotation#setPlaceInPage(int)}.
+ * @param annot the annotation
+ * @param page the page
+ */
+ public void addAnnotation(PdfAnnotation annot, int page) {
+ stamper.addAnnotation(annot, page);
+ }
+
+ /**
+ * Adds the comments present in an FDF file.
+ * @param fdf the FDF file
+ * @throws IOException on error
+ */
+ public void addComments(FdfReader fdf) throws IOException {
+ stamper.addComments(fdf);
+ }
+
+ /**
+ * Sets the bookmarks. The list structure is defined in
+ * {@link SimpleBookmark}.
+ * @param outlines the bookmarks or null
to remove any
+ * @throws IOException on error
+ */
+ public void setOutlines(List outlines) throws IOException {
+ stamper.setOutlines(outlines);
+ }
+
+ /**
+ * Sets the thumbnail image for a page.
+ * @param image the image
+ * @param page the page
+ * @throws PdfException on error
+ * @throws DocumentException on error
+ */
+ public void setThumbnail(Image image, int page) throws PdfException, DocumentException {
+ stamper.setThumbnail(image, page);
+ }
+
+ /**
+ * Adds name
to the list of fields that will be flattened on close,
+ * all the other fields will remain. If this method is never called or is called
+ * with invalid field names, all the fields will be flattened.
+ * setFormFlattening(true)
is needed to have any kind of
+ * flattening.
+ * @param name the field name
+ * @return true
if the field exists, false
otherwise
+ */
+ public boolean partialFormFlattening(String name) {
+ return stamper.partialFormFlattening(name);
+ }
+
+ /** Adds a JavaScript action at the document level. When the document
+ * opens all this JavaScript runs. The existing JavaScript will be replaced.
+ * @param js the JavaScript code
+ */
+ public void addJavaScript(String js) {
+ stamper.addJavaScript(js, !PdfEncodings.isPdfDocEncoding(js));
+ }
+
+ /** Adds a file attachment at the document level. Existing attachments will be kept.
+ * @param description the file description
+ * @param fileStore an array with the file. If it's null
+ * the file will be read from the disk
+ * @param file the path to the file. It will only be used if
+ * fileStore
is not null
+ * @param fileDisplay the actual file name stored in the pdf
+ * @throws IOException on error
+ */
+ public void addFileAttachment(String description, byte fileStore[], String file, String fileDisplay) throws IOException {
+ addFileAttachment(description, PdfFileSpecification.fileEmbedded(stamper, file, fileDisplay, fileStore));
+ }
+
+ /** Adds a file attachment at the document level. Existing attachments will be kept.
+ * @param description the file description
+ * @param fs the file specification
+ */
+ public void addFileAttachment(String description, PdfFileSpecification fs) throws IOException {
+ stamper.addFileAttachment(description, fs);
+ }
+
+ /**
+ * Sets the viewer preferences.
+ * @param preferences the viewer preferences
+ * @see PdfWriter#setViewerPreferences(int)
+ */
+ public void setViewerPreferences(int preferences) {
+ stamper.setViewerPreferences(preferences);
+ }
+
+ /**
+ * Sets the XMP metadata.
+ * @param xmp
+ * @see PdfWriter#setXmpMetadata(byte[])
+ */
+ public void setXmpMetadata(byte[] xmp) {
+ stamper.setXmpMetadata(xmp);
+ }
+
+ /**
+ * Gets the 1.5 compression status.
+ * @return true
if the 1.5 compression is on
+ */
+ public boolean isFullCompression() {
+ return stamper.isFullCompression();
+ }
+
+ /**
+ * Sets the document's compression to the new 1.5 mode with object streams and xref
+ * streams. It can be set at any time but once set it can't be unset.
+ */
+ public void setFullCompression() {
+ if (stamper.isAppend())
+ return;
+ stamper.setFullCompression();
+ }
+
+ /**
+ * Sets the open and close page additional action.
+ * @param actionType the action type. It can be PdfWriter.PAGE_OPEN
+ * or PdfWriter.PAGE_CLOSE
+ * @param action the action to perform
+ * @param page the page where the action will be applied. The first page is 1
+ * @throws PdfException if the action type is invalid
+ */
+ public void setPageAction(PdfName actionType, PdfAction action, int page) throws PdfException {
+ stamper.setPageAction(actionType, action, page);
+ }
+
+ /**
+ * Sets the display duration for the page (for presentations)
+ * @param seconds the number of seconds to display the page. A negative value removes the entry
+ * @param page the page where the duration will be applied. The first page is 1
+ */
+ public void setDuration(int seconds, int page) {
+ stamper.setDuration(seconds, page);
+ }
+
+ /**
+ * Sets the transition for the page
+ * @param transition the transition object. A null
removes the transition
+ * @param page the page where the transition will be applied. The first page is 1
+ */
+ public void setTransition(PdfTransition transition, int page) {
+ stamper.setTransition(transition, page);
+ }
+
+ /**
+ * Applies a digital signature to a document, possibly as a new revision, making
+ * possible multiple signatures. The returned PdfStamper
+ * can be used normally as the signature is only applied when closing.
+ *
+ * KeyStore ks = KeyStore.getInstance("pkcs12");
+ * ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
+ * String alias = (String)ks.aliases().nextElement();
+ * PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
+ * Certificate[] chain = ks.getCertificateChain(alias);
+ * PdfReader reader = new PdfReader("original.pdf");
+ * FileOutputStream fout = new FileOutputStream("signed.pdf");
+ * PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0', new
+ * File("/temp"), true);
+ * PdfSignatureAppearance sap = stp.getSignatureAppearance();
+ * sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
+ * sap.setReason("I'm the author");
+ * sap.setLocation("Lisbon");
+ * // comment next line to have an invisible signature
+ * sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
+ * stp.close();
+ *
+ * @param reader the original document
+ * @param os the output stream or null
to keep the document in the temporary file
+ * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
+ * document
+ * @param tempFile location of the temporary file. If it's a directory a temporary file will be created there.
+ * If it's a file it will be used directly. The file will be deleted on exit unless os
is null.
+ * In that case the document can be retrieved directly from the temporary file. If it's null
+ * no temporary file will be created and memory will be used
+ * @param append if true
the signature and all the other content will be added as a
+ * new revision thus not invalidating existing signatures
+ * @return a PdfStamper
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public static PdfStamper createSignature(PdfReader reader, OutputStream os, char pdfVersion, File tempFile, boolean append) throws DocumentException, IOException {
+ PdfStamper stp;
+ if (tempFile == null) {
+ ByteBuffer bout = new ByteBuffer();
+ stp = new PdfStamper(reader, bout, pdfVersion, append);
+ stp.sigApp = new PdfSignatureAppearance(stp.stamper);
+ stp.sigApp.setSigout(bout);
+ }
+ else {
+ if (tempFile.isDirectory())
+ tempFile = File.createTempFile("pdf", null, tempFile);
+ FileOutputStream fout = new FileOutputStream(tempFile);
+ stp = new PdfStamper(reader, fout, pdfVersion, append);
+ stp.sigApp = new PdfSignatureAppearance(stp.stamper);
+ stp.sigApp.setTempFile(tempFile);
+ }
+ stp.sigApp.setOriginalout(os);
+ stp.sigApp.setStamper(stp);
+ stp.hasSignature = true;
+ return stp;
+ }
+
+ /**
+ * Applies a digital signature to a document. The returned PdfStamper
+ * can be used normally as the signature is only applied when closing.
+ *
+ * KeyStore ks = KeyStore.getInstance("pkcs12");
+ * ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
+ * String alias = (String)ks.aliases().nextElement();
+ * PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
+ * Certificate[] chain = ks.getCertificateChain(alias);
+ * PdfReader reader = new PdfReader("original.pdf");
+ * FileOutputStream fout = new FileOutputStream("signed.pdf");
+ * PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0');
+ * PdfSignatureAppearance sap = stp.getSignatureAppearance();
+ * sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
+ * sap.setReason("I'm the author");
+ * sap.setLocation("Lisbon");
+ * // comment next line to have an invisible signature
+ * sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
+ * stp.close();
+ *
+ * @param reader the original document
+ * @param os the output stream
+ * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
+ * document
+ * @throws DocumentException on error
+ * @throws IOException on error
+ * @return a PdfStamper
+ */
+ public static PdfStamper createSignature(PdfReader reader, OutputStream os, char pdfVersion) throws DocumentException, IOException {
+ return createSignature(reader, os, pdfVersion, null, false);
+ }
+
+ /**
+ * Applies a digital signature to a document. The returned PdfStamper
+ * can be used normally as the signature is only applied when closing.
+ *
+ * KeyStore ks = KeyStore.getInstance("pkcs12");
+ * ks.load(new FileInputStream("my_private_key.pfx"), "my_password".toCharArray());
+ * String alias = (String)ks.aliases().nextElement();
+ * PrivateKey key = (PrivateKey)ks.getKey(alias, "my_password".toCharArray());
+ * Certificate[] chain = ks.getCertificateChain(alias);
+ * PdfReader reader = new PdfReader("original.pdf");
+ * FileOutputStream fout = new FileOutputStream("signed.pdf");
+ * PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0', new File("/temp"));
+ * PdfSignatureAppearance sap = stp.getSignatureAppearance();
+ * sap.setCrypto(key, chain, null, PdfSignatureAppearance.WINCER_SIGNED);
+ * sap.setReason("I'm the author");
+ * sap.setLocation("Lisbon");
+ * // comment next line to have an invisible signature
+ * sap.setVisibleSignature(new Rectangle(100, 100, 200, 200), 1, null);
+ * stp.close();
+ *
+ * @param reader the original document
+ * @param os the output stream or null
to keep the document in the temporary file
+ * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
+ * document
+ * @param tempFile location of the temporary file. If it's a directory a temporary file will be created there.
+ * If it's a file it will be used directly. The file will be deleted on exit unless os
is null.
+ * In that case the document can be retrieved directly from the temporary file. If it's null
+ * no temporary file will be created and memory will be used
+ * @return a PdfStamper
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ public static PdfStamper createSignature(PdfReader reader, OutputStream os, char pdfVersion, File tempFile) throws DocumentException, IOException
+ {
+ return createSignature(reader, os, pdfVersion, tempFile, false);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfStamperImp.java b/src/main/java/com/lowagie/text/pdf/PdfStamperImp.java
new file mode 100644
index 0000000..7a36cf6
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfStamperImp.java
@@ -0,0 +1,1461 @@
+/*
+ * Copyright 2003 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Image;
+import com.lowagie.text.ExceptionConverter;
+
+// wprinz: had to change this to public, as we want to explicitely mark something as USED.
+public class PdfStamperImp extends PdfWriter {
+ HashMap readers2intrefs = new HashMap();
+ HashMap readers2file = new HashMap();
+ RandomAccessFileOrArray file;
+ PdfReader reader;
+ IntHashtable myXref = new IntHashtable();
+ /** Integer(page number) -> PageStamp */
+ HashMap pagesToContent = new HashMap();
+ boolean closed = false;
+ /** Holds value of property rotateContents. */
+ private boolean rotateContents = true;
+ protected AcroFields acroFields;
+ protected boolean flat = false;
+ protected boolean flatFreeText = false;
+ protected int namePtr[] = {0};
+ protected boolean namedAsNames;
+ protected List newBookmarks;
+ protected HashSet partialFlattening = new HashSet();
+ protected boolean useVp = false;
+ protected int vp = 0;
+ protected HashMap fieldTemplates = new HashMap();
+ protected boolean fieldsAdded = false;
+ protected int sigFlags = 0;
+ protected boolean append;
+ protected IntHashtable marked;
+ protected int initialXrefSize;
+ protected PdfAction openAction;
+
+ // egiz code:
+ protected PdfName egiz_dict_name = null;
+ protected PdfIndirectReference egiz_dict_ir = null;
+ /**
+ * Sets the key and the contents of the entry to be added to the trailer if an egiz dict is present.
+ * @param name The name of the egiz dict in the trailer.
+ * @param ir The indirect reference of the egiz dict.
+ */
+ public void setEgizDictTrailerInfo(PdfName name, PdfIndirectReference ir)
+ {
+ this.egiz_dict_name = name;
+ this.egiz_dict_ir = ir;
+ }
+ // end egiz code;
+
+
+ /** Creates new PdfStamperImp.
+ * @param reader the read PDF
+ * @param os the output destination
+ * @param pdfVersion the new pdf version or '\0' to keep the same version as the original
+ * document
+ * @param append
+ * @throws DocumentException on error
+ * @throws IOException
+ */
+ PdfStamperImp(PdfReader reader, OutputStream os, char pdfVersion, boolean append) throws DocumentException, IOException {
+ super(new PdfDocument(), os);
+ if (reader.isTampered())
+ throw new DocumentException("The original document was reused. Read it again from file.");
+ reader.setTampered(true);
+ this.reader = reader;
+ file = reader.getSafeFile();
+ this.append = append;
+ if (append) {
+ if (reader.isRebuilt())
+ throw new DocumentException("Append mode requires a document without errors even if recovery was possible.");
+ if (reader.isEncrypted())
+ crypto = new PdfEncryption(reader.getDecrypt());
+ HEADER = getISOBytes("\n");
+ file.reOpen();
+ byte buf[] = new byte[8192];
+ int n;
+ while ((n = file.read(buf)) > 0)
+ this.os.write(buf, 0, n);
+ file.close();
+ prevxref = reader.getLastXref();
+ reader.setAppendable(true);
+ }
+ else {
+ if (pdfVersion == 0)
+ super.setPdfVersion(reader.getPdfVersion());
+ else
+ super.setPdfVersion(pdfVersion);
+ }
+ super.open();
+ pdf.addWriter(this);
+ if (append) {
+ body.setRefnum(reader.getXrefSize());
+ marked = new IntHashtable();
+ if (reader.isNewXrefType())
+ fullCompression = true;
+ if (reader.isHybridXref())
+ fullCompression = false;
+ }
+ initialXrefSize = reader.getXrefSize();
+ }
+
+ void close(HashMap moreInfo) throws DocumentException, IOException {
+ if (closed)
+ return;
+ if (useVp) {
+ reader.setViewerPreferences(vp);
+ markUsed(reader.getTrailer().get(PdfName.ROOT));
+ }
+ if (flat)
+ flatFields();
+ if (flatFreeText)
+ flatFreeTextFields();
+ addFieldResources();
+ if (sigFlags != 0) {
+ PdfDictionary acroForm = (PdfDictionary)PdfReader.getPdfObject(reader.getCatalog().get(PdfName.ACROFORM), reader.getCatalog());
+ if (acroForm != null) {
+ acroForm.put(PdfName.SIGFLAGS, new PdfNumber(sigFlags));
+ markUsed(acroForm);
+ }
+ }
+ closed = true;
+ addSharedObjectsToBody();
+ setOutlines();
+ setJavaScript();
+ addFileAttachments();
+ if (openAction != null) {
+ reader.getCatalog().put(PdfName.OPENACTION, openAction);
+ }
+ // if there is XMP data to add: add it
+ if (xmpMetadata != null) {
+ PdfDictionary catalog = reader.getCatalog();
+ PdfStream xmp = new PdfStream(xmpMetadata);
+ xmp.put(PdfName.TYPE, PdfName.METADATA);
+ xmp.put(PdfName.SUBTYPE, PdfName.XML);
+ catalog.put(PdfName.METADATA, body.add(xmp).getIndirectReference());
+ markUsed(catalog);
+ }
+ PRIndirectReference iInfo = null;
+ try {
+ file.reOpen();
+ alterContents();
+ iInfo = (PRIndirectReference)reader.trailer.get(PdfName.INFO);
+ int skip = -1;
+ if (iInfo != null)
+ skip = iInfo.getNumber();
+ int rootN = ((PRIndirectReference)reader.trailer.get(PdfName.ROOT)).getNumber();
+ if (append) {
+ int keys[] = marked.getKeys();
+ for (int k = 0; k < keys.length; ++k) {
+ int j = keys[k];
+ PdfObject obj = reader.getPdfObjectRelease(j);
+ if (obj != null && skip != j && j < initialXrefSize) {
+ addToBody(obj, j, j != rootN);
+ }
+ }
+ for (int k = initialXrefSize; k < reader.getXrefSize(); ++k) {
+ PdfObject obj = reader.getPdfObject(k);
+ if (obj != null) {
+ addToBody(obj, getNewObjectNumber(reader, k, 0));
+ }
+ }
+ }
+ else {
+ for (int k = 1; k < reader.getXrefSize(); ++k) {
+ PdfObject obj = reader.getPdfObjectRelease(k);
+ if (obj != null && skip != k) {
+ addToBody(obj, getNewObjectNumber(reader, k, 0), k != rootN);
+ }
+ }
+ }
+ }
+ finally {
+ try {
+ file.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ PdfIndirectReference encryption = null;
+ PdfObject fileID = null;
+ if (crypto != null) {
+ if (append) {
+ encryption = reader.getCryptoRef();
+ }
+ else {
+ PdfIndirectObject encryptionObject = addToBody(crypto.getEncryptionDictionary(), false);
+ encryption = encryptionObject.getIndirectReference();
+ }
+ fileID = crypto.getFileID();
+ }
+ PRIndirectReference iRoot = (PRIndirectReference)reader.trailer.get(PdfName.ROOT);
+ PdfIndirectReference root = new PdfIndirectReference(0, getNewObjectNumber(reader, iRoot.getNumber(), 0));
+ PdfIndirectReference info = null;
+ PdfDictionary oldInfo = (PdfDictionary)PdfReader.getPdfObject(iInfo);
+ PdfDictionary newInfo = new PdfDictionary();
+ if (oldInfo != null) {
+ for (Iterator i = oldInfo.getKeys().iterator(); i.hasNext();) {
+ PdfName key = (PdfName)i.next();
+ PdfObject value = PdfReader.getPdfObject(oldInfo.get(key));
+ newInfo.put(key, value);
+ }
+ }
+ if (moreInfo != null) {
+ for (Iterator i = moreInfo.keySet().iterator(); i.hasNext();) {
+ String key = (String)i.next();
+ PdfName keyName = new PdfName(key);
+ String value = (String)moreInfo.get(key);
+ if (value == null)
+ newInfo.remove(keyName);
+ else
+ newInfo.put(keyName, new PdfString(value, PdfObject.TEXT_UNICODE));
+ }
+ }
+ if (append) {
+ if (iInfo == null)
+ info = addToBody(newInfo, false).getIndirectReference();
+ else
+ info = addToBody(newInfo, iInfo.getNumber(), false).getIndirectReference();
+ }
+ else {
+ if (!newInfo.getKeys().isEmpty())
+ info = addToBody(newInfo, false).getIndirectReference();
+ }
+ // write the cross-reference table of the body
+ body.writeCrossReferenceTable(os, root, info, encryption, fileID, prevxref);
+ if (fullCompression) {
+ os.write(getISOBytes("startxref\n"));
+ os.write(getISOBytes(String.valueOf(body.offset())));
+ os.write(getISOBytes("\n%%EOF\n"));
+ }
+ else {
+ PdfTrailer trailer = new PdfTrailer(body.size(),
+ body.offset(),
+ root,
+ info,
+ encryption,
+ fileID, prevxref);
+ // EGIZ code - add EGIZ dict to trailer)
+ if (this.egiz_dict_name != null)
+ {
+ trailer.put(this.egiz_dict_name, this.egiz_dict_ir);
+ }
+ // endo of egiz code.
+ trailer.toPdf(this, os);
+ }
+ os.flush();
+ if (isCloseStream())
+ os.close();
+ reader.close();
+ }
+
+ void applyRotation(PdfDictionary pageN, ByteBuffer out) {
+ if (!rotateContents)
+ return;
+ Rectangle page = reader.getPageSizeWithRotation(pageN);
+ int rotation = page.getRotation();
+ switch (rotation) {
+ case 90:
+ out.append(PdfContents.ROTATE90);
+ out.append(page.top());
+ out.append(' ').append('0').append(PdfContents.ROTATEFINAL);
+ break;
+ case 180:
+ out.append(PdfContents.ROTATE180);
+ out.append(page.right());
+ out.append(' ');
+ out.append(page.top());
+ out.append(PdfContents.ROTATEFINAL);
+ break;
+ case 270:
+ out.append(PdfContents.ROTATE270);
+ out.append('0').append(' ');
+ out.append(page.right());
+ out.append(PdfContents.ROTATEFINAL);
+ break;
+ }
+ }
+
+ void alterContents() throws IOException {
+ for (Iterator i = pagesToContent.values().iterator(); i.hasNext();) {
+ PageStamp ps = (PageStamp)i.next();
+ PdfDictionary pageN = ps.pageN;
+ markUsed(pageN);
+ PdfArray ar = null;
+ PdfObject content = PdfReader.getPdfObject(pageN.get(PdfName.CONTENTS), pageN);
+ if (content == null) {
+ ar = new PdfArray();
+ pageN.put(PdfName.CONTENTS, ar);
+ }
+ else if (content.isArray()) {
+ ar = (PdfArray)content;
+ markUsed(ar);
+ }
+ else if (content.isStream()) {
+ ar = new PdfArray();
+ ar.add(pageN.get(PdfName.CONTENTS));
+ pageN.put(PdfName.CONTENTS, ar);
+ }
+ else {
+ ar = new PdfArray();
+ pageN.put(PdfName.CONTENTS, ar);
+ }
+ ByteBuffer out = new ByteBuffer();
+ if (ps.under != null) {
+ out.append(PdfContents.SAVESTATE);
+ applyRotation(pageN, out);
+ out.append(ps.under.getInternalBuffer());
+ out.append(PdfContents.RESTORESTATE);
+ }
+ if (ps.over != null)
+ out.append(PdfContents.SAVESTATE);
+ PdfStream stream = new PdfStream(out.toByteArray());
+ try{stream.flateCompress();}catch(Exception e){throw new ExceptionConverter(e);}
+ ar.addFirst(addToBody(stream).getIndirectReference());
+ out.reset();
+ if (ps.over != null) {
+ out.append(' ');
+ out.append(PdfContents.RESTORESTATE);
+ out.append(PdfContents.SAVESTATE);
+ applyRotation(pageN, out);
+ out.append(ps.over.getInternalBuffer());
+ out.append(PdfContents.RESTORESTATE);
+ stream = new PdfStream(out.toByteArray());
+ try{stream.flateCompress();}catch(Exception e){throw new ExceptionConverter(e);}
+ ar.add(addToBody(stream).getIndirectReference());
+ }
+ alterResources(ps);
+ }
+ }
+
+ void alterResources(PageStamp ps) {
+ ps.pageN.put(PdfName.RESOURCES, ps.pageResources.getResources());
+ }
+
+ protected int getNewObjectNumber(PdfReader reader, int number, int generation) {
+ IntHashtable ref = (IntHashtable)readers2intrefs.get(reader);
+ if (ref != null) {
+ int n = ref.get(number);
+ if (n == 0) {
+ n = getIndirectReferenceNumber();
+ ref.put(number, n);
+ }
+ return n;
+ }
+ if (currentPdfReaderInstance == null) {
+ if (append && number < initialXrefSize)
+ return number;
+ int n = myXref.get(number);
+ if (n == 0) {
+ n = getIndirectReferenceNumber();
+ myXref.put(number, n);
+ }
+ return n;
+ }
+ else
+ return currentPdfReaderInstance.getNewObjectNumber(number, generation);
+ }
+
+ RandomAccessFileOrArray getReaderFile(PdfReader reader) {
+ if (readers2intrefs.containsKey(reader)) {
+ RandomAccessFileOrArray raf = (RandomAccessFileOrArray)readers2file.get(reader);
+ if (raf != null)
+ return raf;
+ return reader.getSafeFile();
+ }
+ if (currentPdfReaderInstance == null)
+ return file;
+ else
+ return currentPdfReaderInstance.getReaderFile();
+ }
+
+ /**
+ * @param reader
+ * @param openFile
+ * @throws IOException
+ */
+ public void registerReader(PdfReader reader, boolean openFile) throws IOException {
+ if (readers2intrefs.containsKey(reader))
+ return;
+ readers2intrefs.put(reader, new IntHashtable());
+ if (openFile) {
+ RandomAccessFileOrArray raf = reader.getSafeFile();
+ readers2file.put(reader, raf);
+ raf.reOpen();
+ }
+ }
+
+ /**
+ * @param reader
+ */
+ public void unRegisterReader(PdfReader reader) {
+ if (!readers2intrefs.containsKey(reader))
+ return;
+ readers2intrefs.remove(reader);
+ RandomAccessFileOrArray raf = (RandomAccessFileOrArray)readers2file.get(reader);
+ if (raf == null)
+ return;
+ readers2file.remove(reader);
+ try{raf.close();}catch(Exception e){}
+ }
+
+ static void findAllObjects(PdfReader reader, PdfObject obj, IntHashtable hits) {
+ if (obj == null)
+ return;
+ switch (obj.type()) {
+ case PdfObject.INDIRECT:
+ PRIndirectReference iref = (PRIndirectReference)obj;
+ if (reader != iref.getReader())
+ return;
+ if (hits.containsKey(iref.getNumber()))
+ return;
+ hits.put(iref.getNumber(), 1);
+ findAllObjects(reader, PdfReader.getPdfObject(obj), hits);
+ return;
+ case PdfObject.ARRAY:
+ ArrayList lst = ((PdfArray)obj).getArrayList();
+ for (int k = 0; k < lst.size(); ++k) {
+ findAllObjects(reader, (PdfObject)lst.get(k), hits);
+ }
+ return;
+ case PdfObject.DICTIONARY:
+ case PdfObject.STREAM:
+ PdfDictionary dic = (PdfDictionary)obj;
+ for (Iterator it = dic.getKeys().iterator(); it.hasNext();) {
+ PdfName name = (PdfName)it.next();
+ findAllObjects(reader, dic.get(name), hits);
+ }
+ return;
+ }
+ }
+
+ /**
+ * @param fdf
+ * @throws IOException
+ */
+ public void addComments(FdfReader fdf) throws IOException{
+ if (readers2intrefs.containsKey(fdf))
+ return;
+ PdfDictionary catalog = fdf.getCatalog();
+ catalog = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.FDF));
+ if (catalog == null)
+ return;
+ PdfArray annots = (PdfArray)PdfReader.getPdfObject(catalog.get(PdfName.ANNOTS));
+ if (annots == null || annots.size() == 0)
+ return;
+ registerReader(fdf, false);
+ IntHashtable hits = new IntHashtable();
+ HashMap irt = new HashMap();
+ ArrayList an = new ArrayList();
+ ArrayList ar = annots.getArrayList();
+ for (int k = 0; k < ar.size(); ++k) {
+ PdfObject obj = (PdfObject)ar.get(k);
+ PdfDictionary annot = (PdfDictionary)PdfReader.getPdfObject(obj);
+ PdfNumber page = (PdfNumber)PdfReader.getPdfObject(annot.get(PdfName.PAGE));
+ if (page == null || page.intValue() >= reader.getNumberOfPages())
+ continue;
+ findAllObjects(fdf, obj, hits);
+ an.add(obj);
+ if (obj.type() == PdfObject.INDIRECT) {
+ PdfObject nm = PdfReader.getPdfObject(annot.get(PdfName.NM));
+ if (nm != null && nm.type() == PdfObject.STRING)
+ irt.put(nm.toString(), obj);
+ }
+ }
+ int arhits[] = hits.getKeys();
+ for (int k = 0; k < arhits.length; ++k) {
+ int n = arhits[k];
+ PdfObject obj = fdf.getPdfObject(n);
+ if (obj.type() == PdfObject.DICTIONARY) {
+ PdfObject str = PdfReader.getPdfObject(((PdfDictionary)obj).get(PdfName.IRT));
+ if (str != null && str.type() == PdfObject.STRING) {
+ PdfObject i = (PdfObject)irt.get(str.toString());
+ if (i != null) {
+ PdfDictionary dic2 = new PdfDictionary();
+ dic2.merge((PdfDictionary)obj);
+ dic2.put(PdfName.IRT, i);
+ obj = dic2;
+ }
+ }
+ }
+ addToBody(obj, getNewObjectNumber(fdf, n, 0));
+ }
+ for (int k = 0; k < an.size(); ++k) {
+ PdfObject obj = (PdfObject)an.get(k);
+ PdfDictionary annot = (PdfDictionary)PdfReader.getPdfObject(obj);
+ PdfNumber page = (PdfNumber)PdfReader.getPdfObject(annot.get(PdfName.PAGE));
+ PdfDictionary dic = reader.getPageN(page.intValue() + 1);
+ PdfArray annotsp = (PdfArray)PdfReader.getPdfObject(dic.get(PdfName.ANNOTS), dic);
+ if (annotsp == null) {
+ annotsp = new PdfArray();
+ dic.put(PdfName.ANNOTS, annotsp);
+ markUsed(dic);
+ }
+ markUsed(annotsp);
+ annotsp.add(obj);
+ }
+ }
+
+ PageStamp getPageStamp(int pageNum) {
+ PdfDictionary pageN = reader.getPageN(pageNum);
+ PageStamp ps = (PageStamp)pagesToContent.get(pageN);
+ if (ps == null) {
+ ps = new PageStamp(this, reader, pageN);
+ pagesToContent.put(pageN, ps);
+ }
+ return ps;
+ }
+
+ PdfContentByte getUnderContent(int pageNum) {
+ if (pageNum < 1 || pageNum > reader.getNumberOfPages())
+ return null;
+ PageStamp ps = getPageStamp(pageNum);
+ if (ps.under == null)
+ ps.under = new StampContent(this, ps);
+ return ps.under;
+ }
+
+ PdfContentByte getOverContent(int pageNum) {
+ if (pageNum < 1 || pageNum > reader.getNumberOfPages())
+ return null;
+ PageStamp ps = getPageStamp(pageNum);
+ if (ps.over == null)
+ ps.over = new StampContent(this, ps);
+ return ps.over;
+ }
+
+ void correctAcroFieldPages(int page) {
+ if (acroFields == null)
+ return;
+ if (page > reader.getNumberOfPages())
+ return;
+ HashMap fields = acroFields.getFields();
+ for (Iterator it = fields.values().iterator(); it.hasNext();) {
+ AcroFields.Item item = (AcroFields.Item)it.next();
+ ArrayList pages = item.page;
+ for (int k = 0; k < pages.size(); ++k) {
+ int p = ((Integer)pages.get(k)).intValue();
+ if (p >= page)
+ pages.set(k, new Integer(p + 1));
+ }
+ }
+ }
+
+ void insertPage(int pageNumber, Rectangle mediabox) {
+ Rectangle media = new Rectangle(mediabox);
+ int rotation = media.getRotation() % 360;
+ PdfDictionary page = new PdfDictionary(PdfName.PAGE);
+ PdfDictionary resources = new PdfDictionary();
+ PdfArray procset = new PdfArray();
+ procset.add(PdfName.PDF);
+ procset.add(PdfName.TEXT);
+ procset.add(PdfName.IMAGEB);
+ procset.add(PdfName.IMAGEC);
+ procset.add(PdfName.IMAGEI);
+ resources.put(PdfName.PROCSET, procset);
+ page.put(PdfName.RESOURCES, resources);
+ page.put(PdfName.ROTATE, new PdfNumber(rotation));
+ page.put(PdfName.MEDIABOX, new PdfRectangle(media, rotation));
+ PRIndirectReference pref = reader.addPdfObject(page);
+ PdfDictionary parent;
+ PRIndirectReference parentRef;
+ if (pageNumber > reader.getNumberOfPages()) {
+ PdfDictionary lastPage = reader.getPageNRelease(reader.getNumberOfPages());
+ parentRef = (PRIndirectReference)lastPage.get(PdfName.PARENT);
+ parentRef = new PRIndirectReference(reader, parentRef.getNumber());
+ parent = (PdfDictionary)PdfReader.getPdfObject(parentRef);
+ PdfArray kids = (PdfArray)PdfReader.getPdfObject(parent.get(PdfName.KIDS), parent);
+ kids.add(pref);
+ markUsed(kids);
+ reader.pageRefs.insertPage(pageNumber, pref);
+ }
+ else {
+ if (pageNumber < 1)
+ pageNumber = 1;
+ PdfDictionary firstPage = reader.getPageN(pageNumber);
+ PRIndirectReference firstPageRef = reader.getPageOrigRef(pageNumber);
+ reader.releasePage(pageNumber);
+ parentRef = (PRIndirectReference)firstPage.get(PdfName.PARENT);
+ parentRef = new PRIndirectReference(reader, parentRef.getNumber());
+ parent = (PdfDictionary)PdfReader.getPdfObject(parentRef);
+ PdfArray kids = (PdfArray)PdfReader.getPdfObject(parent.get(PdfName.KIDS), parent);
+ ArrayList ar = kids.getArrayList();
+ int len = ar.size();
+ int num = firstPageRef.getNumber();
+ for (int k = 0; k < len; ++k) {
+ PRIndirectReference cur = (PRIndirectReference)ar.get(k);
+ if (num == cur.getNumber()) {
+ ar.add(k, pref);
+ break;
+ }
+ }
+ if (len == ar.size())
+ throw new RuntimeException("Internal inconsistence.");
+ markUsed(kids);
+ reader.pageRefs.insertPage(pageNumber, pref);
+ correctAcroFieldPages(pageNumber);
+ }
+ page.put(PdfName.PARENT, parentRef);
+ while (parent != null) {
+ markUsed(parent);
+ PdfNumber count = (PdfNumber)PdfReader.getPdfObjectRelease(parent.get(PdfName.COUNT));
+ parent.put(PdfName.COUNT, new PdfNumber(count.intValue() + 1));
+ parent = (PdfDictionary)PdfReader.getPdfObject(parent.get(PdfName.PARENT));
+ }
+ }
+
+ /** Getter for property rotateContents.
+ * @return Value of property rotateContents.
+ *
+ */
+ boolean isRotateContents() {
+ return this.rotateContents;
+ }
+
+ /** Setter for property rotateContents.
+ * @param rotateContents New value of property rotateContents.
+ *
+ */
+ void setRotateContents(boolean rotateContents) {
+ this.rotateContents = rotateContents;
+ }
+
+ boolean isContentWritten() {
+ return body.size() > 1;
+ }
+
+ AcroFields getAcroFields() {
+ if (acroFields == null) {
+ acroFields = new AcroFields(reader, this);
+ }
+ return acroFields;
+ }
+
+ void setFormFlattening(boolean flat) {
+ this.flat = flat;
+ }
+
+ void setFreeTextFlattening(boolean flat) {
+ this.flatFreeText = flat;
+ }
+
+ boolean partialFormFlattening(String name) {
+ getAcroFields();
+ if (!acroFields.getFields().containsKey(name))
+ return false;
+ partialFlattening.add(name);
+ return true;
+ }
+
+ void flatFields() {
+ if (append)
+ throw new IllegalArgumentException("Field flattening is not supported in append mode.");
+ getAcroFields();
+ HashMap fields = acroFields.getFields();
+ if (fieldsAdded && partialFlattening.isEmpty()) {
+ for (Iterator i = fields.keySet().iterator(); i.hasNext();) {
+ partialFlattening.add(i.next());
+ }
+ }
+ PdfDictionary acroForm = (PdfDictionary)PdfReader.getPdfObject(reader.getCatalog().get(PdfName.ACROFORM));
+ ArrayList acroFds = null;
+ if (acroForm != null) {
+ PdfArray array = (PdfArray)PdfReader.getPdfObject(acroForm.get(PdfName.FIELDS), acroForm);
+ if (array != null)
+ acroFds = array.getArrayList();
+ }
+ for (Iterator i = fields.keySet().iterator(); i.hasNext();) {
+ String name = (String)i.next();
+ if (!partialFlattening.isEmpty() && !partialFlattening.contains(name))
+ continue;
+ AcroFields.Item item = (AcroFields.Item)fields.get(name);
+ for (int k = 0; k < item.merged.size(); ++k) {
+ PdfDictionary merged = (PdfDictionary)item.merged.get(k);
+ PdfNumber ff = (PdfNumber)PdfReader.getPdfObject(merged.get(PdfName.F));
+ int flags = 0;
+ if (ff != null)
+ flags = ff.intValue();
+ int page = ((Integer)item.page.get(k)).intValue();
+ PdfDictionary appDic = (PdfDictionary)PdfReader.getPdfObject(merged.get(PdfName.AP));
+ if (appDic != null && (flags & PdfFormField.FLAGS_PRINT) != 0 && (flags & PdfFormField.FLAGS_HIDDEN) == 0) {
+ PdfObject obj = appDic.get(PdfName.N);
+ PdfAppearance app = null;
+ if (obj != null) {
+ PdfObject objReal = PdfReader.getPdfObject(obj);
+ if (obj instanceof PdfIndirectReference && !obj.isIndirect())
+ app = new PdfAppearance((PdfIndirectReference)obj);
+ else if (objReal instanceof PdfStream) {
+ ((PdfDictionary)objReal).put(PdfName.SUBTYPE, PdfName.FORM);
+ app = new PdfAppearance((PdfIndirectReference)obj);
+ }
+ else {
+ if (objReal.isDictionary()) {
+ PdfName as = (PdfName)PdfReader.getPdfObject(merged.get(PdfName.AS));
+ if (as != null) {
+ PdfIndirectReference iref = (PdfIndirectReference)((PdfDictionary)objReal).get(as);
+ if (iref != null) {
+ app = new PdfAppearance(iref);
+ if (iref.isIndirect()) {
+ objReal = PdfReader.getPdfObject(iref);
+ ((PdfDictionary)objReal).put(PdfName.SUBTYPE, PdfName.FORM);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (app != null) {
+ Rectangle box = PdfReader.getNormalizedRectangle((PdfArray)PdfReader.getPdfObject(merged.get(PdfName.RECT)));
+ PdfContentByte cb = getOverContent(page);
+ cb.setLiteral("Q ");
+ cb.addTemplate(app, box.left(), box.bottom());
+ cb.setLiteral("q ");
+ }
+ }
+ if (partialFlattening.isEmpty())
+ continue;
+ PdfDictionary pageDic = reader.getPageN(page);
+ PdfArray annots = (PdfArray)PdfReader.getPdfObject(pageDic.get(PdfName.ANNOTS));
+ if (annots == null)
+ continue;
+ ArrayList ar = annots.getArrayList();
+ for (int idx = 0; idx < ar.size(); ++idx) {
+ PdfObject ran = (PdfObject)ar.get(idx);
+ if (!ran.isIndirect())
+ continue;
+ PdfObject ran2 = (PdfObject)item.widget_refs.get(k);
+ if (!ran2.isIndirect())
+ continue;
+ if (((PRIndirectReference)ran).getNumber() == ((PRIndirectReference)ran2).getNumber()) {
+ ar.remove(idx--);
+ PRIndirectReference wdref = (PRIndirectReference)ran2;
+ while (true) {
+ PdfDictionary wd = (PdfDictionary)PdfReader.getPdfObject(wdref);
+ PRIndirectReference parentRef = (PRIndirectReference)wd.get(PdfName.PARENT);
+ PdfReader.killIndirect(wdref);
+ if (parentRef == null) { // reached AcroForm
+ for (int fr = 0; fr < acroFds.size(); ++fr) {
+ PdfObject h = (PdfObject)acroFds.get(fr);
+ if (h.isIndirect() && ((PRIndirectReference)h).getNumber() == wdref.getNumber()) {
+ acroFds.remove(fr);
+ --fr;
+ }
+ }
+ break;
+ }
+ PdfDictionary parent = (PdfDictionary)PdfReader.getPdfObject(parentRef);
+ PdfArray kids = (PdfArray)PdfReader.getPdfObject(parent.get(PdfName.KIDS));
+ ArrayList kar = kids.getArrayList();
+ for (int fr = 0; fr < kar.size(); ++fr) {
+ PdfObject h = (PdfObject)kar.get(fr);
+ if (h.isIndirect() && ((PRIndirectReference)h).getNumber() == wdref.getNumber()) {
+ kar.remove(fr);
+ --fr;
+ }
+ }
+ if (!kar.isEmpty())
+ break;
+ wdref = parentRef;
+ }
+ }
+ }
+ if (ar.size() == 0) {
+ PdfReader.killIndirect(pageDic.get(PdfName.ANNOTS));
+ pageDic.remove(PdfName.ANNOTS);
+ }
+ }
+ }
+ if (!fieldsAdded && partialFlattening.isEmpty()) {
+ for (int page = 1; page <= reader.getNumberOfPages(); ++page) {
+ PdfDictionary pageDic = reader.getPageN(page);
+ PdfArray annots = (PdfArray)PdfReader.getPdfObject(pageDic.get(PdfName.ANNOTS));
+ if (annots == null)
+ continue;
+ ArrayList ar = annots.getArrayList();
+ for (int idx = 0; idx < ar.size(); ++idx) {
+ PdfObject annoto = PdfReader.getPdfObject((PdfObject)ar.get(idx));
+ if ((annoto instanceof PdfIndirectReference) && !annoto.isIndirect())
+ continue;
+ PdfDictionary annot = (PdfDictionary)annoto;
+ if (PdfName.WIDGET.equals(annot.get(PdfName.SUBTYPE))) {
+ ar.remove(idx);
+ --idx;
+ }
+ }
+ if (ar.size() == 0) {
+ PdfReader.killIndirect(pageDic.get(PdfName.ANNOTS));
+ pageDic.remove(PdfName.ANNOTS);
+ }
+ }
+ eliminateAcroformObjects();
+ }
+ }
+
+ void eliminateAcroformObjects() {
+ PdfObject acro = reader.getCatalog().get(PdfName.ACROFORM);
+ if (acro == null)
+ return;
+ PdfDictionary acrodic = (PdfDictionary)PdfReader.getPdfObject(acro);
+ PdfObject iFields = acrodic.get(PdfName.FIELDS);
+ if (iFields != null) {
+ PdfDictionary kids = new PdfDictionary();
+ kids.put(PdfName.KIDS, iFields);
+ sweepKids(kids);
+ PdfReader.killIndirect(iFields);
+ acrodic.put(PdfName.FIELDS, new PdfArray());
+ }
+// PdfReader.killIndirect(acro);
+// reader.getCatalog().remove(PdfName.ACROFORM);
+ }
+
+ void sweepKids(PdfObject obj) {
+ PdfObject oo = PdfReader.killIndirect(obj);
+ if (oo == null || !oo.isDictionary())
+ return;
+ PdfDictionary dic = (PdfDictionary)oo;
+ PdfArray kids = (PdfArray)PdfReader.killIndirect(dic.get(PdfName.KIDS));
+ if (kids == null)
+ return;
+ ArrayList ar = kids.getArrayList();
+ for (int k = 0; k < ar.size(); ++k) {
+ sweepKids((PdfObject)ar.get(k));
+ }
+ }
+
+ private void flatFreeTextFields()
+ {
+ if (append)
+ throw new IllegalArgumentException("FreeText flattening is not supported in append mode.");
+
+ for (int page = 1; page <= reader.getNumberOfPages(); ++page)
+ {
+ PdfDictionary pageDic = reader.getPageN(page);
+ PdfArray annots = (PdfArray)PdfReader.getPdfObject(pageDic.get(PdfName.ANNOTS));
+ if (annots == null)
+ continue;
+ ArrayList ar = annots.getArrayList();
+ for (int idx = 0; idx < ar.size(); ++idx)
+ {
+ PdfObject annoto = PdfReader.getPdfObject((PdfObject)ar.get(idx));
+ if ((annoto instanceof PdfIndirectReference) && !annoto.isIndirect())
+ continue;
+
+ PdfDictionary annDic = (PdfDictionary)annoto;
+ if (!((PdfName)annDic.get(PdfName.SUBTYPE)).equals(PdfName.FREETEXT))
+ continue;
+ PdfNumber ff = (PdfNumber)PdfReader.getPdfObject(annDic.get(PdfName.F));
+ int flags = (ff != null) ? ff.intValue() : 0;
+
+ if ( (flags & PdfFormField.FLAGS_PRINT) != 0 && (flags & PdfFormField.FLAGS_HIDDEN) == 0)
+ {
+ PdfObject obj1 = annDic.get(PdfName.AP);
+ if (obj1 == null)
+ continue;
+ PdfDictionary appDic = (obj1 instanceof PdfIndirectReference) ?
+ (PdfDictionary) PdfReader.getPdfObject(obj1) : (PdfDictionary) obj1;
+ PdfObject obj = appDic.get(PdfName.N);
+ PdfAppearance app = null;
+ PdfObject objReal = PdfReader.getPdfObject(obj);
+
+ if (obj instanceof PdfIndirectReference && !obj.isIndirect())
+ app = new PdfAppearance((PdfIndirectReference)obj);
+ else if (objReal instanceof PdfStream)
+ {
+ ((PdfDictionary)objReal).put(PdfName.SUBTYPE, PdfName.FORM);
+ app = new PdfAppearance((PdfIndirectReference)obj);
+ }
+ else
+ {
+ if (objReal.isDictionary())
+ {
+ PdfName as_p = (PdfName)PdfReader.getPdfObject(appDic.get(PdfName.AS));
+ if (as_p != null)
+ {
+ PdfIndirectReference iref = (PdfIndirectReference)((PdfDictionary)objReal).get(as_p);
+ if (iref != null)
+ {
+ app = new PdfAppearance(iref);
+ if (iref.isIndirect())
+ {
+ objReal = PdfReader.getPdfObject(iref);
+ ((PdfDictionary)objReal).put(PdfName.SUBTYPE, PdfName.FORM);
+ }
+ }
+ }
+ }
+ }
+ if (app != null)
+ {
+ Rectangle box = PdfReader.getNormalizedRectangle((PdfArray)PdfReader.getPdfObject(annDic.get(PdfName.RECT)));
+ PdfContentByte cb = getOverContent(page);
+ cb.setLiteral("Q ");
+ cb.addTemplate(app, box.left(), box.bottom());
+ cb.setLiteral("q ");
+ }
+ }
+ if (partialFlattening.size() == 0)
+ continue;
+ }
+ for (int idx = 0; idx < ar.size(); ++idx)
+ {
+ PdfObject annoto = PdfReader.getPdfObject((PdfObject)ar.get(idx));
+ if ((annoto instanceof PdfIndirectReference) && annoto.isIndirect())
+ {
+ PdfDictionary annot = (PdfDictionary)annoto;
+ if (PdfName.FREETEXT.equals(annot.get(PdfName.SUBTYPE)))
+ {
+ ar.remove(idx);
+ --idx;
+ }
+ }
+ }
+ if (ar.size() == 0)
+ {
+ PdfReader.killIndirect(pageDic.get(PdfName.ANNOTS));
+ pageDic.remove(PdfName.ANNOTS);
+ }
+ }
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfWriter#getPageReference(int)
+ */
+ public PdfIndirectReference getPageReference(int page) {
+ PdfIndirectReference ref = reader.getPageOrigRef(page);
+ if (ref == null)
+ throw new IllegalArgumentException("Invalid page number " + page);
+ return ref;
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfWriter#addAnnotation(com.lowagie.text.pdf.PdfAnnotation)
+ */
+ public void addAnnotation(PdfAnnotation annot) {
+ throw new RuntimeException("Unsupported in this context. Use PdfStamper.addAnnotation()");
+ }
+
+ void addDocumentField(PdfIndirectReference ref) {
+ PdfDictionary catalog = reader.getCatalog();
+ PdfDictionary acroForm = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.ACROFORM), catalog);
+ if (acroForm == null) {
+ acroForm = new PdfDictionary();
+ catalog.put(PdfName.ACROFORM, acroForm);
+ markUsed(catalog);
+ }
+ PdfArray fields = (PdfArray)PdfReader.getPdfObject(acroForm.get(PdfName.FIELDS), acroForm);
+ if (fields == null) {
+ fields = new PdfArray();
+ acroForm.put(PdfName.FIELDS, fields);
+ markUsed(acroForm);
+ }
+ fields.add(ref);
+ markUsed(fields);
+ }
+
+ void addFieldResources() {
+ if (fieldTemplates.size() == 0)
+ return;
+ PdfDictionary catalog = reader.getCatalog();
+ PdfDictionary acroForm = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.ACROFORM), catalog);
+ if (acroForm == null) {
+ acroForm = new PdfDictionary();
+ catalog.put(PdfName.ACROFORM, acroForm);
+ markUsed(catalog);
+ }
+ PdfDictionary dr = (PdfDictionary)PdfReader.getPdfObject(acroForm.get(PdfName.DR), acroForm);
+ if (dr == null) {
+ dr = new PdfDictionary();
+ acroForm.put(PdfName.DR, dr);
+ markUsed(acroForm);
+ }
+ markUsed(dr);
+ for (Iterator it = fieldTemplates.keySet().iterator(); it.hasNext();) {
+ PdfTemplate template = (PdfTemplate)it.next();
+ PdfFormField.mergeResources(dr, (PdfDictionary)template.getResources(), this);
+ }
+ PdfDictionary fonts = (PdfDictionary)PdfReader.getPdfObject(dr.get(PdfName.FONT));
+ if (fonts != null && acroForm.get(PdfName.DA) == null) {
+ acroForm.put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g "));
+ markUsed(acroForm);
+ }
+ }
+
+ void expandFields(PdfFormField field, ArrayList allAnnots) {
+ allAnnots.add(field);
+ ArrayList kids = field.getKids();
+ if (kids != null) {
+ for (int k = 0; k < kids.size(); ++k)
+ expandFields((PdfFormField)kids.get(k), allAnnots);
+ }
+ }
+
+ void addAnnotation(PdfAnnotation annot, PdfDictionary pageN) {
+ try {
+ ArrayList allAnnots = new ArrayList();
+ if (annot.isForm()) {
+ fieldsAdded = true;
+ getAcroFields();
+ PdfFormField field = (PdfFormField)annot;
+ if (field.getParent() != null)
+ return;
+ expandFields(field, allAnnots);
+ }
+ else
+ allAnnots.add(annot);
+ for (int k = 0; k < allAnnots.size(); ++k) {
+ annot = (PdfAnnotation)allAnnots.get(k);
+ if (annot.getPlaceInPage() > 0)
+ pageN = reader.getPageN(annot.getPlaceInPage());
+ if (annot.isForm()) {
+ if (!annot.isUsed()) {
+ HashMap templates = annot.getTemplates();
+ if (templates != null)
+ fieldTemplates.putAll(templates);
+ }
+ PdfFormField field = (PdfFormField)annot;
+ if (field.getParent() == null)
+ addDocumentField(field.getIndirectReference());
+ }
+ if (annot.isAnnotation()) {
+ PdfArray annots = (PdfArray)PdfReader.getPdfObject(pageN.get(PdfName.ANNOTS), pageN);
+ if (annots == null) {
+ annots = new PdfArray();
+ pageN.put(PdfName.ANNOTS, annots);
+ markUsed(pageN);
+ }
+ annots.add(annot.getIndirectReference());
+ markUsed(annots);
+ if (!annot.isUsed()) {
+ PdfRectangle rect = (PdfRectangle)annot.get(PdfName.RECT);
+ if (rect != null && (rect.left() != 0 || rect.right() != 0 || rect.top() != 0 || rect.bottom() != 0)) {
+ int rotation = reader.getPageRotation(pageN);
+ Rectangle pageSize = reader.getPageSizeWithRotation(pageN);
+ switch (rotation) {
+ case 90:
+ annot.put(PdfName.RECT, new PdfRectangle(
+ pageSize.top() - rect.bottom(),
+ rect.left(),
+ pageSize.top() - rect.top(),
+ rect.right()));
+ break;
+ case 180:
+ annot.put(PdfName.RECT, new PdfRectangle(
+ pageSize.right() - rect.left(),
+ pageSize.top() - rect.bottom(),
+ pageSize.right() - rect.right(),
+ pageSize.top() - rect.top()));
+ break;
+ case 270:
+ annot.put(PdfName.RECT, new PdfRectangle(
+ rect.bottom(),
+ pageSize.right() - rect.left(),
+ rect.top(),
+ pageSize.right() - rect.right()));
+ break;
+ }
+ }
+ }
+ }
+ if (!annot.isUsed()) {
+ annot.setUsed();
+ addToBody(annot, annot.getIndirectReference());
+ }
+ }
+ }
+ catch (IOException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ void addAnnotation(PdfAnnotation annot, int page) {
+ addAnnotation(annot, reader.getPageN(page));
+ }
+
+ private void outlineTravel(PRIndirectReference outline) {
+ while (outline != null) {
+ PdfDictionary outlineR = (PdfDictionary)PdfReader.getPdfObjectRelease(outline);
+ PRIndirectReference first = (PRIndirectReference)outlineR.get(PdfName.FIRST);
+ if (first != null) {
+ outlineTravel(first);
+ }
+ PdfReader.killIndirect(outlineR.get(PdfName.DEST));
+ PdfReader.killIndirect(outlineR.get(PdfName.A));
+ PdfReader.killIndirect(outline);
+ outline = (PRIndirectReference)outlineR.get(PdfName.NEXT);
+ }
+ }
+
+ void deleteOutlines() {
+ PdfDictionary catalog = reader.getCatalog();
+ PRIndirectReference outlines = (PRIndirectReference)catalog.get(PdfName.OUTLINES);
+ if (outlines == null)
+ return;
+ outlineTravel(outlines);
+ PdfReader.killIndirect(outlines);
+ catalog.remove(PdfName.OUTLINES);
+ markUsed(catalog);
+ }
+
+ void setJavaScript() throws IOException {
+ ArrayList djs = pdf.getDocumentJavaScript();
+ if (djs.size() == 0)
+ return;
+ PdfDictionary catalog = reader.getCatalog();
+ PdfDictionary names = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.NAMES), catalog);
+ if (names == null) {
+ names = new PdfDictionary();
+ catalog.put(PdfName.NAMES, names);
+ markUsed(catalog);
+ }
+ markUsed(names);
+ String s = String.valueOf(djs.size() - 1);
+ int n = s.length();
+ String pad = "000000000000000";
+ HashMap maptree = new HashMap();
+ for (int k = 0; k < djs.size(); ++k) {
+ s = String.valueOf(k);
+ s = pad.substring(0, n - s.length()) + s;
+ maptree.put(s, djs.get(k));
+ }
+ PdfDictionary tree = PdfNameTree.writeTree(maptree, this);
+ names.put(PdfName.JAVASCRIPT, addToBody(tree).getIndirectReference());
+ }
+
+ void addFileAttachments() throws IOException {
+ HashMap fs = pdf.getDocumentFileAttachment();
+ if (fs.size() == 0)
+ return;
+ PdfDictionary catalog = reader.getCatalog();
+ PdfDictionary names = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.NAMES), catalog);
+ if (names == null) {
+ names = new PdfDictionary();
+ catalog.put(PdfName.NAMES, names);
+ markUsed(catalog);
+ }
+ markUsed(names);
+ HashMap old = PdfNameTree.readTree((PdfDictionary)PdfReader.getPdfObjectRelease(names.get(PdfName.EMBEDDEDFILES)));
+ for (Iterator it = fs.keySet().iterator(); it.hasNext();) {
+ String name = (String)it.next();
+ int k = 0;
+ String nn = name;
+ while (old.containsKey(nn)) {
+ ++k;
+ nn += " " + k;
+ }
+ old.put(nn, fs.get(name));
+ }
+ PdfDictionary tree = PdfNameTree.writeTree(old, this);
+ names.put(PdfName.EMBEDDEDFILES, addToBody(tree).getIndirectReference());
+ }
+
+ void setOutlines() throws IOException {
+ if (newBookmarks == null)
+ return;
+ deleteOutlines();
+ if (newBookmarks.size() == 0)
+ return;
+ namedAsNames = (reader.getCatalog().get(PdfName.DESTS) != null);
+ PdfDictionary top = new PdfDictionary();
+ PdfIndirectReference topRef = getPdfIndirectReference();
+ Object kids[] = SimpleBookmark.iterateOutlines(this, topRef, newBookmarks, namedAsNames);
+ top.put(PdfName.FIRST, (PdfIndirectReference)kids[0]);
+ top.put(PdfName.LAST, (PdfIndirectReference)kids[1]);
+ top.put(PdfName.COUNT, new PdfNumber(((Integer)kids[2]).intValue()));
+ addToBody(top, topRef);
+ reader.getCatalog().put(PdfName.OUTLINES, topRef);
+ markUsed(reader.getCatalog());
+ }
+
+ void setOutlines(List outlines) {
+ newBookmarks = outlines;
+ }
+
+ /**
+ * Sets the viewer preferences.
+ * @param preferences the viewer preferences
+ * @see PdfWriter#setViewerPreferences(int)
+ */
+ public void setViewerPreferences(int preferences) {
+ useVp = true;
+ vp |= preferences;
+ }
+
+ /**
+ * Set the signature flags.
+ * @param f the flags. This flags are ORed with current ones
+ */
+ public void setSigFlags(int f) {
+ sigFlags |= f;
+ }
+
+ /** Always throws an UnsupportedOperationException
.
+ * @param actionType ignore
+ * @param action ignore
+ * @throws PdfException ignore
+ * @see PdfStamper#setPageAction(PdfName, PdfAction, int)
+ */
+ public void setPageAction(PdfName actionType, PdfAction action) throws PdfException {
+ throw new UnsupportedOperationException("Use setPageAction(PdfName actionType, PdfAction action, int page)");
+ }
+
+ /**
+ * Sets the open and close page additional action.
+ * @param actionType the action type. It can be PdfWriter.PAGE_OPEN
+ * or PdfWriter.PAGE_CLOSE
+ * @param action the action to perform
+ * @param page the page where the action will be applied. The first page is 1
+ * @throws PdfException if the action type is invalid
+ */
+ void setPageAction(PdfName actionType, PdfAction action, int page) throws PdfException {
+ if (!actionType.equals(PAGE_OPEN) && !actionType.equals(PAGE_CLOSE))
+ throw new PdfException("Invalid page additional action type: " + actionType.toString());
+ PdfDictionary pg = reader.getPageN(page);
+ PdfDictionary aa = (PdfDictionary)PdfReader.getPdfObject(pg.get(PdfName.AA), pg);
+ if (aa == null) {
+ aa = new PdfDictionary();
+ pg.put(PdfName.AA, aa);
+ markUsed(pg);
+ }
+ aa.put(actionType, action);
+ markUsed(aa);
+ }
+
+ /**
+ * Always throws an UnsupportedOperationException
.
+ * @param seconds ignore
+ */
+ public void setDuration(int seconds) {
+ throw new UnsupportedOperationException("Use setPageAction(PdfName actionType, PdfAction action, int page)");
+ }
+
+ /**
+ * Always throws an UnsupportedOperationException
.
+ * @param transition ignore
+ */
+ public void setTransition(PdfTransition transition) {
+ throw new UnsupportedOperationException("Use setPageAction(PdfName actionType, PdfAction action, int page)");
+ }
+
+ /**
+ * Sets the display duration for the page (for presentations)
+ * @param seconds the number of seconds to display the page. A negative value removes the entry
+ * @param page the page where the duration will be applied. The first page is 1
+ */
+ void setDuration(int seconds, int page) {
+ PdfDictionary pg = reader.getPageN(page);
+ if (seconds < 0)
+ pg.remove(PdfName.DUR);
+ else
+ pg.put(PdfName.DUR, new PdfNumber(seconds));
+ markUsed(pg);
+ }
+
+ /**
+ * Sets the transition for the page
+ * @param transition the transition object. A null
removes the transition
+ * @param page the page where the transition will be applied. The first page is 1
+ */
+ void setTransition(PdfTransition transition, int page) {
+ PdfDictionary pg = reader.getPageN(page);
+ if (transition == null)
+ pg.remove(PdfName.TRANS);
+ else
+ pg.put(PdfName.TRANS, transition.getTransitionDictionary());
+ markUsed(pg);
+ }
+
+ // wprinz: had to make this public as we want to explicitely mark something as USED.
+ public void markUsed(PdfObject obj) {
+ if (append && obj != null) {
+ PRIndirectReference ref = null;
+ if (obj.type() == PdfObject.INDIRECT)
+ ref = (PRIndirectReference)obj;
+ else
+ ref = obj.getIndRef();
+ if (ref != null)
+ marked.put(ref.getNumber(), 1);
+ }
+ }
+
+ protected void markUsed(int num) {
+ if (append)
+ marked.put(num, 1);
+ }
+
+ /**
+ * Getter for property append.
+ * @return Value of property append.
+ */
+ boolean isAppend() {
+ return append;
+ }
+
+ /** Additional-actions defining the actions to be taken in
+ * response to various trigger events affecting the document
+ * as a whole. The actions types allowed are: DOCUMENT_CLOSE
,
+ * WILL_SAVE
, DID_SAVE
, WILL_PRINT
+ * and DID_PRINT
.
+ *
+ * @param actionType the action type
+ * @param action the action to execute in response to the trigger
+ * @throws PdfException on invalid action type
+ */
+ public void setAdditionalAction(PdfName actionType, PdfAction action) throws PdfException {
+ if (!(actionType.equals(DOCUMENT_CLOSE) ||
+ actionType.equals(WILL_SAVE) ||
+ actionType.equals(DID_SAVE) ||
+ actionType.equals(WILL_PRINT) ||
+ actionType.equals(DID_PRINT))) {
+ throw new PdfException("Invalid additional action type: " + actionType.toString());
+ }
+ PdfDictionary aa = (PdfDictionary)PdfReader.getPdfObject(reader.getCatalog().get(PdfName.AA));
+ if (aa == null) {
+ if (action == null)
+ return;
+ aa = new PdfDictionary();
+ reader.getCatalog().put(PdfName.AA, aa);
+ }
+ markUsed(aa);
+ if (action == null)
+ aa.remove(actionType);
+ else
+ aa.put(actionType, action);
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfWriter#setOpenAction(com.lowagie.text.pdf.PdfAction)
+ */
+ public void setOpenAction(PdfAction action) {
+ openAction = action;
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfWriter#setOpenAction(java.lang.String)
+ */
+ public void setOpenAction(String name) {
+ throw new UnsupportedOperationException("Open actions by name are not supported.");
+ }
+
+ /**
+ * @see com.lowagie.text.pdf.PdfWriter#setThumbnail(com.lowagie.text.Image)
+ */
+ public void setThumbnail(com.lowagie.text.Image image) {
+ throw new UnsupportedOperationException("Use PdfStamper.setThumbnail().");
+ }
+
+ void setThumbnail(Image image, int page) throws PdfException, DocumentException {
+ PdfIndirectReference thumb = getImageReference(addDirectImageSimple(image));
+ reader.resetReleasePage();
+ PdfDictionary dic = reader.getPageN(page);
+ dic.put(PdfName.THUMB, thumb);
+ reader.resetReleasePage();
+ }
+
+ public PdfContentByte getDirectContentUnder() {
+ throw new UnsupportedOperationException("Use PdfStamper.getUnderContent() or PdfStamper.getOverContent()");
+ }
+
+ public PdfContentByte getDirectContent() {
+ throw new UnsupportedOperationException("Use PdfStamper.getUnderContent() or PdfStamper.getOverContent()");
+ }
+
+ static class PageStamp {
+
+ PdfDictionary pageN;
+ StampContent under;
+ StampContent over;
+ PageResources pageResources;
+
+ PageStamp(PdfStamperImp stamper, PdfReader reader, PdfDictionary pageN) {
+ this.pageN = pageN;
+ pageResources = new PageResources();
+ PdfDictionary resources = (PdfDictionary)PdfReader.getPdfObject(pageN.get(PdfName.RESOURCES));
+
+
+ // wprinz: flatten out the /Resource dictionary so that, when incrementally written, all new Resources are written correctly.
+ // alternatively, indirect resource dictionary for /Font and /XObject could be marked as used
+ // when they are modified. - e.g. in PageResources - but that would be far more effort than flattening the dict.
+ // (actually the objects that were flattened out could be deleted in the IncrementalUpdate xref)
+ //System.out.println("pageN = " + pageN.getIndRef());
+ //System.out.println("pageN.get(/Resources) = " + pageN.get(PdfName.RESOURCES));
+ //System.out.println("new resources = " + resources);
+ //System.out.println("flattening out new resources:");
+ for (Iterator it = resources.getKeys().iterator(); it.hasNext(); )
+ {
+ PdfName key = (PdfName)it.next();
+ PdfObject value = resources.get(key);
+ //System.out.println(" " + key + " = " + value);
+
+ if (value.isIndirect() && (key.compareTo(PdfName.FONT) == 0 || key.compareTo(PdfName.XOBJECT) == 0))
+ {
+ PRIndirectReference ref = (PRIndirectReference)value;
+ PdfObject direct_object = PdfReader.getPdfObject(ref);
+
+ resources.put(key, direct_object);
+ //System.out.println(" flattended " + key + " to " + direct_object);
+ }
+ }
+ // wprinz: end
+
+
+ pageResources.setOriginalResources(resources, stamper.namePtr);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfStream.java b/src/main/java/com/lowagie/text/pdf/PdfStream.java
new file mode 100644
index 0000000..3d3b9dd
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfStream.java
@@ -0,0 +1,313 @@
+/*
+ * $Id: PdfStream.java,v 1.57 2005/11/01 12:27:05 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.Deflater;
+import com.lowagie.text.Document;
+import com.lowagie.text.DocWriter;
+import com.lowagie.text.ExceptionConverter;
+
+/**
+ * PdfStream
is the Pdf stream object.
+ *
+ * A stream consists of a dictionary that describes a sequence of characters, followed by
+ * the keyword stream, followed by zero or more lines of characters, followed by
+ * the keyword endstream.
+ * All streams must be PdfIndirectObject
s. The stream dictionary must be a direct
+ * object. The keyword stream that follows the stream dictionary should be followed by
+ * a carriage return and linefeed or just a linefeed.
+ * Remark: In this version only the FLATEDECODE-filter is supported.
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 4.8 (page 41-53).
+ *
+ * @see PdfObject
+ * @see PdfDictionary
+ */
+
+public class PdfStream extends PdfDictionary {
+
+ // membervariables
+
+/** is the stream compressed? */
+ protected boolean compressed = false;
+
+ protected ByteArrayOutputStream streamBytes = null;
+ protected InputStream inputStream;
+ protected PdfIndirectReference ref;
+ protected int inputStreamLength = -1;
+ protected PdfWriter writer;
+ protected int rawLength;
+
+ static final byte STARTSTREAM[] = DocWriter.getISOBytes("stream\n");
+ static final byte ENDSTREAM[] = DocWriter.getISOBytes("\nendstream");
+ static final int SIZESTREAM = STARTSTREAM.length + ENDSTREAM.length;
+
+ // constructors
+
+/**
+ * Constructs a PdfStream
-object.
+ *
+ * @param bytes content of the new PdfObject
as an array of byte
.
+ */
+
+ public PdfStream(byte[] bytes) {
+ super();
+ type = STREAM;
+ this.bytes = bytes;
+ rawLength = bytes.length;
+ put(PdfName.LENGTH, new PdfNumber(bytes.length));
+ }
+
+ /**
+ * Creates an efficient stream. No temporary array is ever created. The InputStream
+ * is totally consumed but is not closed. The general usage is:
+ *
+ * InputStream in = ...;
+ * PdfStream stream = new PdfStream(in, writer);
+ * stream.flateCompress();
+ * writer.addToBody(stream);
+ * stream.writeLength();
+ * in.close();
+ *
+ * @param inputStream the data to write to this stream
+ * @param writer the PdfWriter
for this stream
+ */
+ public PdfStream(InputStream inputStream, PdfWriter writer) {
+ super();
+ type = STREAM;
+ this.inputStream = inputStream;
+ this.writer = writer;
+ ref = writer.getPdfIndirectReference();
+ put(PdfName.LENGTH, ref);
+ }
+
+/**
+ * Constructs a PdfStream
-object.
+ */
+
+ protected PdfStream() {
+ super();
+ type = STREAM;
+ }
+
+ /**
+ * Writes the stream length to the PdfWriter
.
+ * OutputStream
.
+ * @param os the destination to write to
+ * @throws IOException on error
+ */
+ public void writeContent(OutputStream os) throws IOException {
+ if (streamBytes != null)
+ streamBytes.writeTo(os);
+ else if (bytes != null)
+ os.write(bytes);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfString.java b/src/main/java/com/lowagie/text/pdf/PdfString.java
new file mode 100644
index 0000000..dac4035
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfString.java
@@ -0,0 +1,236 @@
+/*
+ * $Id: PdfString.java,v 1.58 2005/05/04 14:32:24 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * A PdfString
-class is the PDF-equivalent of a JAVA-String
-object.
+ *
+ * This object is described in the 'Portable Document Format Reference Manual version 1.3'
+ * section 4.4 (page 37-39).
+ *
+ * @see PdfObject
+ * @see BadPdfFormatException
+ */
+
+public class PdfString extends PdfObject {
+
+ // membervariables
+
+ /** The value of this object. */
+ protected String value = NOTHING;
+ protected String originalValue = null;
+
+ /** The encoding. */
+ protected String encoding = TEXT_PDFDOCENCODING;
+ protected int objNum = 0;
+ protected int objGen = 0;
+ protected boolean hexWriting = false;
+
+ // constructors
+
+ /**
+ * Constructs an empty PdfString
-object.
+ */
+
+ public PdfString() {
+ super(STRING);
+ }
+
+ /**
+ * Constructs a PdfString
-object.
+ *
+ * @param value the content of the string
+ */
+
+ public PdfString(String value) {
+ super(STRING);
+ this.value = value;
+ }
+
+ /**
+ * Constructs a PdfString
-object.
+ *
+ * @param value the content of the string
+ * @param encoding an encoding
+ */
+
+ public PdfString(String value, String encoding) {
+ super(STRING);
+ this.value = value;
+ this.encoding = encoding;
+ }
+
+ /**
+ * Constructs a PdfString
-object.
+ *
+ * @param bytes an array of byte
+ */
+
+ public PdfString(byte[] bytes) {
+ super(STRING);
+ value = PdfEncodings.convertToString(bytes, null);
+ encoding = NOTHING;
+ }
+
+ // methods overriding some methods in PdfObject
+
+ /**
+ * Returns the PDF representation of this PdfString
.
+ *
+ * @return an array of byte
s
+ */
+
+ public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
+ byte b[] = getBytes();
+ PdfEncryption crypto = null;
+ if (writer != null)
+ crypto = writer.getEncryption();
+ if (crypto != null) {
+ b = (byte[])bytes.clone();
+ crypto.prepareKey();
+ crypto.encryptRC4(b);
+ }
+ if (hexWriting) {
+ ByteBuffer buf = new ByteBuffer();
+ buf.append('<');
+ int len = b.length;
+ for (int k = 0; k < len; ++k)
+ buf.appendHex(b[k]);
+ buf.append('>');
+ os.write(buf.toByteArray());
+ }
+ else
+ os.write(PdfContentByte.escapeString(b));
+ }
+
+ /**
+ * Returns the String
value of the PdfString
-object.
+ *
+ * @return a String
+ */
+
+ public String toString() {
+ return value;
+ }
+
+ // other methods
+
+ /**
+ * Gets the encoding of this string.
+ *
+ * @return a String
+ */
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public String toUnicodeString() {
+ if (encoding != null && encoding.length() != 0)
+ return value;
+ getBytes();
+ if (bytes.length >= 2 && bytes[0] == (byte)254 && bytes[1] == (byte)255)
+ return PdfEncodings.convertToString(bytes, PdfObject.TEXT_UNICODE);
+ else
+ return PdfEncodings.convertToString(bytes, PdfObject.TEXT_PDFDOCENCODING);
+ }
+
+ void setObjNum(int objNum, int objGen) {
+ this.objNum = objNum;
+ this.objGen = objGen;
+ }
+
+ void decrypt(PdfReader reader) {
+ PdfEncryption decrypt = reader.getDecrypt();
+ if (decrypt != null) {
+ originalValue = value;
+ decrypt.setHashKey(objNum, objGen);
+ decrypt.prepareKey();
+ bytes = PdfEncodings.convertToBytes(value, null);
+ decrypt.encryptRC4(bytes);
+ value = PdfEncodings.convertToString(bytes, null);
+ }
+ }
+
+ public byte[] getBytes() {
+ if (bytes == null) {
+ if (encoding != null && encoding.equals(TEXT_UNICODE) && PdfEncodings.isPdfDocEncoding(value))
+ bytes = PdfEncodings.convertToBytes(value, TEXT_PDFDOCENCODING);
+ else
+ bytes = PdfEncodings.convertToBytes(value, encoding);
+ }
+ return bytes;
+ }
+
+ public byte[] getOriginalBytes() {
+ if (originalValue == null)
+ return getBytes();
+ return PdfEncodings.convertToBytes(originalValue, null);
+ }
+
+ public PdfString setHexWriting(boolean hexWriting) {
+ this.hexWriting = hexWriting;
+ return this;
+ }
+
+ public boolean isHexWriting() {
+ return hexWriting;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfStructureElement.java b/src/main/java/com/lowagie/text/pdf/PdfStructureElement.java
new file mode 100644
index 0000000..57e8654
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfStructureElement.java
@@ -0,0 +1,130 @@
+/*
+ * $Id: PdfStructureElement.java,v 1.3 2005/11/01 12:27:05 psoares33 Exp $
+ *
+ * Copyright 2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ * This is a node in a document logical structure. It may contain a mark point or it may contain
+ * other nodes.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfStructureElement extends PdfDictionary {
+
+ /**
+ * Holds value of property kids.
+ */
+ private PdfStructureElement parent;
+ private PdfStructureTreeRoot top;
+
+ /**
+ * Holds value of property reference.
+ */
+ private PdfIndirectReference reference;
+
+ /**
+ * Creates a new instance of PdfStructureElement.
+ * @param parent the parent of this node
+ * @param structureType the type of structure. It may be a standard type or a user type mapped by the role map
+ */
+ public PdfStructureElement(PdfStructureElement parent, PdfName structureType) {
+ top = parent.top;
+ init(parent, structureType);
+ this.parent = parent;
+ put(PdfName.P, parent.reference);
+ }
+
+ /**
+ * Creates a new instance of PdfStructureElement.
+ * @param parent the parent of this node
+ * @param structureType the type of structure. It may be a standard type or a user type mapped by the role map
+ */
+ public PdfStructureElement(PdfStructureTreeRoot parent, PdfName structureType) {
+ top = parent;
+ init(parent, structureType);
+ put(PdfName.P, parent.getReference());
+ }
+
+ private void init(PdfDictionary parent, PdfName structureType) {
+ PdfObject kido = parent.get(PdfName.K);
+ PdfArray kids = null;
+ if (kido != null && !kido.isArray())
+ throw new IllegalArgumentException("The parent has already another function.");
+ if (kido == null) {
+ kids = new PdfArray();
+ parent.put(PdfName.K, kids);
+ }
+ else
+ kids = (PdfArray)kido;
+ kids.add(this);
+ put(PdfName.S, structureType);
+ reference = top.getWriter().getPdfIndirectReference();
+ }
+
+ /**
+ * Gets the parent of this node.
+ * @return the parent of this node
+ */
+ public PdfDictionary getParent() {
+ return parent;
+ }
+
+ void setPageMark(int page, int mark) {
+ if (mark >= 0)
+ put(PdfName.K, new PdfNumber(mark));
+ top.setPageMark(page, reference);
+ }
+
+ /**
+ * Gets the reference this object will be written to.
+ * @return the reference this object will be written to
+ */
+ public PdfIndirectReference getReference() {
+ return this.reference;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfStructureTreeRoot.java b/src/main/java/com/lowagie/text/pdf/PdfStructureTreeRoot.java
new file mode 100644
index 0000000..e6e8c2c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfStructureTreeRoot.java
@@ -0,0 +1,146 @@
+/*
+ * $Id: PdfStructureTreeRoot.java,v 1.2 2005/10/18 15:30:42 psoares33 Exp $
+ *
+ * Copyright 2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.io.IOException;
+
+/**
+ * The structure tree root corresponds to the highest hierarchy level in a tagged PDF.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfStructureTreeRoot extends PdfDictionary {
+
+ private HashMap parentTree = new HashMap();
+ private PdfIndirectReference reference;
+
+ /**
+ * Holds value of property writer.
+ */
+ private PdfWriter writer;
+
+ /** Creates a new instance of PdfStructureTreeRoot */
+ PdfStructureTreeRoot(PdfWriter writer) {
+ super(PdfName.STRUCTTREEROOT);
+ this.writer = writer;
+ reference = writer.getPdfIndirectReference();
+ }
+
+ /**
+ * Maps the user tags to the standard tags. The mapping will allow a standard application to make some sense of the tagged
+ * document whatever the user tags may be.
+ * @param used the user tag
+ * @param standard the standard tag
+ */
+ public void mapRole(PdfName used, PdfName standard) {
+ PdfDictionary rm = (PdfDictionary)get(PdfName.ROLEMAP);
+ if (rm == null) {
+ rm = new PdfDictionary();
+ put(PdfName.ROLEMAP, rm);
+ }
+ rm.put(used, standard);
+ }
+
+ /**
+ * Gets the writer.
+ * @return the writer
+ */
+ public PdfWriter getWriter() {
+ return this.writer;
+ }
+
+ /**
+ * Gets the reference this object will be written to.
+ * @return the reference this object will be written to
+ */
+ public PdfIndirectReference getReference() {
+ return this.reference;
+ }
+
+ void setPageMark(int page, PdfIndirectReference struc) {
+ Integer i = new Integer(page);
+ PdfArray ar = (PdfArray)parentTree.get(i);
+ if (ar == null) {
+ ar = new PdfArray();
+ parentTree.put(i, ar);
+ }
+ ar.add(struc);
+ }
+
+ private void nodeProcess(PdfDictionary struc, PdfIndirectReference reference) throws IOException {
+ PdfObject obj = struc.get(PdfName.K);
+ if (obj != null && obj.isArray() && !((PdfObject)((PdfArray)obj).getArrayList().get(0)).isNumber()) {
+ PdfArray ar = (PdfArray)obj;
+ ArrayList a = ar.getArrayList();
+ for (int k = 0; k < a.size(); ++k) {
+ PdfStructureElement e = (PdfStructureElement)a.get(k);
+ a.set(k, e.getReference());
+ nodeProcess(e, e.getReference());
+ }
+ }
+ if (reference != null)
+ writer.addToBody(struc, reference);
+ }
+
+ void buildTree() throws IOException {
+ HashMap numTree = new HashMap();
+ for (Iterator it = parentTree.keySet().iterator(); it.hasNext();) {
+ Integer i = (Integer)it.next();
+ PdfArray ar = (PdfArray)parentTree.get(i);
+ numTree.put(i, writer.addToBody(ar).getIndirectReference());
+ }
+ PdfDictionary dicTree = PdfNumberTree.writeTree(numTree, writer);
+ if (dicTree != null)
+ put(PdfName.PARENTTREE, writer.addToBody(dicTree).getIndirectReference());
+
+ nodeProcess(this, reference);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfTable.java b/src/main/java/com/lowagie/text/pdf/PdfTable.java
new file mode 100644
index 0000000..0fa6009
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfTable.java
@@ -0,0 +1,316 @@
+/*
+ * $Id: PdfTable.java,v 1.68 2005/05/04 14:31:53 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import com.lowagie.text.Element;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Cell;
+import com.lowagie.text.Row;
+import com.lowagie.text.Table;
+
+/**
+ * PdfTable
is an object that contains the graphics and text of a table.
+ *
+ * @see com.lowagie.text.Table
+ * @see com.lowagie.text.Row
+ * @see com.lowagie.text.Cell
+ * @see PdfCell
+ */
+
+public class PdfTable extends Rectangle {
+
+ // membervariables
+
+ /** this is the number of columns in the table. */
+ private int columns;
+
+ /** this is the ArrayList with all the cell of the table header. */
+ private ArrayList headercells;
+
+ /** this is the ArrayList with all the cells in the table. */
+ private ArrayList cells;
+
+ /** Original table used to build this object*/
+ protected Table table;
+
+ /** Cached column widths. */
+ protected float[] positions;
+
+ // constructors
+
+ /**
+ * Constructs a PdfTable
-object.
+ *
+ * @param table a Table
+ * @param left the left border on the page
+ * @param right the right border on the page
+ * @param top the start position of the top of the table
+ * @param supportUpdateRowAdditions
+ * if true, table rows will be deleted after building the PdfTable table,
+ * in order to preserve memory and detect future row additions
+ */
+
+ PdfTable(Table table, float left, float right, float top, boolean supportUpdateRowAdditions) {
+ // constructs a Rectangle (the bottomvalue will be changed afterwards)
+ super(left, top, right, top);
+ this.table = table;
+ table.complete();
+
+ // copying the attributes from class Table
+ cloneNonPositionParameters(table);
+
+ this.columns = table.columns();
+ positions = table.getWidths(left, right - left);
+
+ // initialisation of some parameters
+ setLeft(positions[0]);
+ setRight(positions[positions.length - 1]);
+
+ headercells = new ArrayList();
+ cells = new ArrayList();
+
+ updateRowAdditionsInternal();
+ if (supportUpdateRowAdditions) {
+ table.deleteAllRows();
+ }
+ }
+
+ // methods
+
+ /**
+ * Updates the table row additions in the underlying table object and deletes all table rows,
+ * in order to preserve memory and detect future row additions.
+ * supportUpdateRowAdditions
equals to true.
+ */
+
+ void updateRowAdditions() {
+ table.complete();
+ updateRowAdditionsInternal();
+ table.deleteAllRows();
+ }
+
+ /**
+ * Updates the table row additions in the underlying table object
+ */
+
+ private void updateRowAdditionsInternal() {
+ // correct table : fill empty cells/ parse table in table
+ Row row;
+ int prevRows = rows();
+ int rowNumber = 0;
+ int groupNumber = 0;
+ boolean groupChange;
+ int firstDataRow = table.firstDataRow();
+ Cell cell;
+ PdfCell currentCell;
+ ArrayList newCells = new ArrayList();
+ int rows = table.size() + 1;
+ float[] offsets = new float[rows];
+ for (int i = 0; i < rows; i++) {
+ offsets[i] = bottom();
+ }
+
+ // loop over all the rows
+ for (Iterator rowIterator = table.iterator(); rowIterator.hasNext(); ) {
+ groupChange = false;
+ row = (Row) rowIterator.next();
+ if (row.isEmpty()) {
+ if (rowNumber < rows - 1 && offsets[rowNumber + 1] > offsets[rowNumber]) offsets[rowNumber + 1] = offsets[rowNumber];
+ }
+ else {
+ for(int i = 0; i < row.columns(); i++) {
+ cell = (Cell) row.getCell(i);
+ if (cell != null) {
+ currentCell = new PdfCell(cell, rowNumber+prevRows, positions[i], positions[i + cell.colspan()], offsets[rowNumber], cellspacing(), cellpadding());
+ try {
+ if (offsets[rowNumber] - currentCell.height() - cellpadding() < offsets[rowNumber + currentCell.rowspan()]) {
+ offsets[rowNumber + currentCell.rowspan()] = offsets[rowNumber] - currentCell.height() - cellpadding();
+ }
+ }
+ catch(ArrayIndexOutOfBoundsException aioobe) {
+ if (offsets[rowNumber] - currentCell.height() < offsets[rows - 1]) {
+ offsets[rows - 1] = offsets[rowNumber] - currentCell.height();
+ }
+ }
+ if (rowNumber < firstDataRow) {
+ currentCell.setHeader();
+ headercells.add(currentCell);
+ }
+ currentCell.setGroupNumber(groupNumber);
+ groupChange |= cell.getGroupChange();
+ newCells.add(currentCell);
+ }
+ }
+ }
+ rowNumber++;
+ if( groupChange ) groupNumber++;
+ }
+
+ // loop over all the cells
+ int n = newCells.size();
+ for (int i = 0; i < n; i++) {
+ currentCell = (PdfCell) newCells.get(i);
+ try {
+ currentCell.setBottom(offsets[currentCell.rownumber()-prevRows + currentCell.rowspan()]);
+ }
+ catch(ArrayIndexOutOfBoundsException aioobe) {
+ currentCell.setBottom(offsets[rows - 1]);
+ }
+ }
+ cells.addAll(newCells);
+ setBottom(offsets[rows - 1]);
+ }
+
+ /**
+ * Get the number of rows
+ */
+
+ int rows() {
+ return cells.size() == 0 ? 0 : ((PdfCell)cells.get(cells.size()-1)).rownumber()+1;
+ }
+
+ /** @see com.lowagie.text.Element#type() */
+ public int type() {
+ return Element.TABLE;
+ }
+
+ /**
+ * Returns the arraylist with the cells of the table header.
+ *
+ * @return an ArrayList
+ */
+
+ ArrayList getHeaderCells() {
+ return headercells;
+ }
+
+ /**
+ * Checks if there is a table header.
+ *
+ * @return an ArrayList
+ */
+
+ boolean hasHeader() {
+ return headercells.size() > 0;
+ }
+
+ /**
+ * Returns the arraylist with the cells of the table.
+ *
+ * @return an ArrayList
+ */
+
+ ArrayList getCells() {
+ return cells;
+ }
+
+ /**
+ * Returns the number of columns of the table.
+ *
+ * @return the number of columns
+ */
+
+ int columns() {
+ return columns;
+ }
+
+ /**
+ * Returns the cellpadding of the table.
+ *
+ * @return the cellpadding
+ */
+
+ final float cellpadding() {
+ return table.cellpadding();
+ }
+
+ /**
+ * Returns the cellspacing of the table.
+ *
+ * @return the cellspacing
+ */
+
+ final float cellspacing() {
+ return table.cellspacing();
+ }
+
+ /**
+ * Checks if this Table
has to fit a page.
+ *
+ * @return true if the table may not be split
+ */
+
+ public final boolean hasToFitPageTable() {
+ return table.hasToFitPageTable();
+ }
+
+ /**
+ * Checks if the cells of this Table
have to fit a page.
+ *
+ * @return true if the cells may not be split
+ */
+
+ public final boolean hasToFitPageCells() {
+ return table.hasToFitPageCells();
+ }
+
+ /**
+ * Gets the offset of this table.
+ *
+ * @return the space between this table and the previous element.
+ */
+ public float getOffset() {
+ return table.getOffset();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfTemplate.java b/src/main/java/com/lowagie/text/pdf/PdfTemplate.java
new file mode 100644
index 0000000..e7ba7ba
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfTemplate.java
@@ -0,0 +1,267 @@
+/*
+ * $Id: PdfTemplate.java,v 1.61 2006/03/02 17:56:30 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import java.io.IOException;
+
+import com.lowagie.text.Rectangle;
+
+/**
+ * Implements the form XObject.
+ */
+
+public class PdfTemplate extends PdfContentByte {
+ public static final int TYPE_TEMPLATE = 1;
+ public static final int TYPE_IMPORTED = 2;
+ public static final int TYPE_PATTERN = 3;
+ protected int type;
+ /** The indirect reference to this template */
+ protected PdfIndirectReference thisReference;
+
+ /** The resources used by this template */
+ protected PageResources pageResources;
+
+
+ /** The bounding box of this template */
+ protected Rectangle bBox = new Rectangle(0, 0);
+
+ protected PdfArray matrix;
+
+ protected PdfTransparencyGroup group;
+
+ protected PdfOCG layer;
+
+ /**
+ *Creates a PdfTemplate
.
+ */
+
+ protected PdfTemplate() {
+ super(null);
+ type = TYPE_TEMPLATE;
+ }
+
+ /**
+ * Creates new PdfTemplate
+ *
+ * @param wr the PdfWriter
+ */
+
+ PdfTemplate(PdfWriter wr) {
+ super(wr);
+ type = TYPE_TEMPLATE;
+ pageResources = new PageResources();
+ pageResources.addDefaultColor(wr.getDefaultColorspace());
+ thisReference = writer.getPdfIndirectReference();
+ }
+
+ /**
+ * Sets the bounding width of this template.
+ *
+ * @param width the bounding width
+ */
+
+ public void setWidth(float width) {
+ bBox.setLeft(0);
+ bBox.setRight(width);
+ }
+
+ /**
+ * Sets the bounding heigth of this template.
+ *
+ * @param height the bounding height
+ */
+
+ public void setHeight(float height) {
+ bBox.setBottom(0);
+ bBox.setTop(height);
+ }
+
+ /**
+ * Gets the bounding width of this template.
+ *
+ * @return width the bounding width
+ */
+ public float getWidth() {
+ return bBox.width();
+ }
+
+ /**
+ * Gets the bounding heigth of this template.
+ *
+ * @return heigth the bounding height
+ */
+
+ public float getHeight() {
+ return bBox.height();
+ }
+
+ public Rectangle getBoundingBox() {
+ return bBox;
+ }
+
+ public void setBoundingBox(Rectangle bBox) {
+ this.bBox = bBox;
+ }
+
+ /**
+ * Sets the layer this template belongs to.
+ * @param layer the layer this template belongs to
+ */
+ public void setLayer(PdfOCG layer) {
+ this.layer = layer;
+ }
+
+ /**
+ * Gets the layer this template belongs to.
+ * @return the layer this template belongs to or null
for no layer defined
+ */
+ public PdfOCG getLayer() {
+ return layer;
+ }
+
+ public void setMatrix(float a, float b, float c, float d, float e, float f) {
+ matrix = new PdfArray();
+ matrix.add(new PdfNumber(a));
+ matrix.add(new PdfNumber(b));
+ matrix.add(new PdfNumber(c));
+ matrix.add(new PdfNumber(d));
+ matrix.add(new PdfNumber(e));
+ matrix.add(new PdfNumber(f));
+ }
+
+ PdfArray getMatrix() {
+ return matrix;
+ }
+
+ /**
+ * Gets the indirect reference to this template.
+ *
+ * @return the indirect reference to this template
+ */
+
+ public PdfIndirectReference getIndirectReference() {
+ return thisReference;
+ }
+
+ public void beginVariableText() {
+ content.append("/Tx BMC ");
+ }
+
+ public void endVariableText() {
+ content.append("EMC ");
+ }
+
+ /**
+ * Constructs the resources used by this template.
+ *
+ * @return the resources used by this template
+ */
+
+ PdfObject getResources() {
+ return getPageResources().getResources();
+ }
+
+ /**
+ * Gets the stream representing this template.
+ *
+ * @return the stream representing this template
+ */
+
+ PdfStream getFormXObject() throws IOException {
+ return new PdfFormXObject(this);
+ }
+
+ /**
+ * Gets a duplicate of this PdfTemplate
. All
+ * the members are copied by reference but the buffer stays different.
+ * @return a copy of this PdfTemplate
+ */
+
+ public PdfContentByte getDuplicate() {
+ PdfTemplate tpl = new PdfTemplate();
+ tpl.writer = writer;
+ tpl.pdf = pdf;
+ tpl.thisReference = thisReference;
+ tpl.pageResources = pageResources;
+ tpl.bBox = new Rectangle(bBox);
+ tpl.group = group;
+ tpl.layer = layer;
+ if (matrix != null) {
+ tpl.matrix = new PdfArray(matrix);
+ }
+ tpl.separator = separator;
+ return tpl;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ PageResources getPageResources() {
+ return pageResources;
+ }
+
+ /** Getter for property group.
+ * @return Value of property group.
+ *
+ */
+ public PdfTransparencyGroup getGroup() {
+ return this.group;
+ }
+
+ /** Setter for property group.
+ * @param group New value of property group.
+ *
+ */
+ public void setGroup(PdfTransparencyGroup group) {
+ this.group = group;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfTextArray.java b/src/main/java/com/lowagie/text/pdf/PdfTextArray.java
new file mode 100644
index 0000000..16901bb
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfTextArray.java
@@ -0,0 +1,99 @@
+/*
+ * $Id: PdfTextArray.java,v 1.58 2005/05/04 14:32:21 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.ArrayList;
+
+/**
+ * PdfTextArray
defines an array with displacements and PdfString
-objects.
+ * TextArray
is used with the operator TJ in PdfText
.
+ * The first object in this array has to be a PdfString
;
+ * see reference manual version 1.3 section 8.7.5, pages 346-347.
+ */
+
+public class PdfTextArray{
+ ArrayList arrayList = new ArrayList();
+ // constructors
+
+
+ public PdfTextArray(String str) {
+ arrayList.add(str);
+ }
+
+ public PdfTextArray() {
+ }
+
+/**
+ * Adds a PdfNumber
to the PdfArray
.
+ *
+ * @param number displacement of the string
+ */
+
+ public void add(PdfNumber number)
+ {
+ arrayList.add(new Float(number.doubleValue()));
+ }
+
+ public void add(float number)
+ {
+ arrayList.add(new Float(number));
+ }
+
+ public void add(String str)
+ {
+ arrayList.add(str);
+ }
+
+ ArrayList getArrayList() {
+ return arrayList;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfTransition.java b/src/main/java/com/lowagie/text/pdf/PdfTransition.java
new file mode 100644
index 0000000..038d4fb
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfTransition.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2002 by Josselin PUJO.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+public class PdfTransition {
+ /**
+ * Out Vertical Split
+ */
+ public static final int SPLITVOUT = 1;
+ /**
+ * Out Horizontal Split
+ */
+ public static final int SPLITHOUT = 2;
+ /**
+ * In Vertical Split
+ */
+ public static final int SPLITVIN = 3;
+ /**
+ * IN Horizontal Split
+ */
+ public static final int SPLITHIN = 4;
+ /**
+ * Vertical Blinds
+ */
+ public static final int BLINDV = 5;
+ /**
+ * Vertical Blinds
+ */
+ public static final int BLINDH = 6;
+ /**
+ * Inward Box
+ */
+ public static final int INBOX = 7;
+ /**
+ * Outward Box
+ */
+ public static final int OUTBOX = 8;
+ /**
+ * Left-Right Wipe
+ */
+ public static final int LRWIPE = 9;
+ /**
+ * Right-Left Wipe
+ */
+ public static final int RLWIPE = 10;
+ /**
+ * Bottom-Top Wipe
+ */
+ public static final int BTWIPE = 11;
+ /**
+ * Top-Bottom Wipe
+ */
+ public static final int TBWIPE = 12;
+ /**
+ * Dissolve
+ */
+ public static final int DISSOLVE = 13;
+ /**
+ * Left-Right Glitter
+ */
+ public static final int LRGLITTER = 14;
+ /**
+ * Top-Bottom Glitter
+ */
+ public static final int TBGLITTER = 15;
+ /**
+ * Diagonal Glitter
+ */
+ public static final int DGLITTER = 16;
+
+ /**
+ * duration of the transition effect
+ */
+ protected int duration;
+ /**
+ * type of the transition effect
+ */
+ protected int type;
+
+ /**
+ * Constructs a Transition
.
+ *
+ */
+ public PdfTransition() {
+ this(BLINDH);
+ }
+
+ /**
+ * Constructs a Transition
.
+ *
+ *@param type type of the transition effect
+ */
+ public PdfTransition(int type) {
+ this(type,1);
+ }
+
+ /**
+ * Constructs a Transition
.
+ *
+ *@param type type of the transition effect
+ *@param duration duration of the transition effect
+ */
+ public PdfTransition(int type, int duration) {
+ this.duration = duration;
+ this.type = type;
+ }
+
+
+ public int getDuration() {
+ return duration;
+ }
+
+
+ public int getType() {
+ return type;
+ }
+
+ public PdfDictionary getTransitionDictionary() {
+ PdfDictionary trans = new PdfDictionary(PdfName.TRANS);
+ switch (type) {
+ case SPLITVOUT:
+ trans.put(PdfName.S,PdfName.SPLIT);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DM,PdfName.V);
+ trans.put(PdfName.M,PdfName.O);
+ break;
+ case SPLITHOUT:
+ trans.put(PdfName.S,PdfName.SPLIT);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DM,PdfName.H);
+ trans.put(PdfName.M,PdfName.O);
+ break;
+ case SPLITVIN:
+ trans.put(PdfName.S,PdfName.SPLIT);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DM,PdfName.V);
+ trans.put(PdfName.M,PdfName.I);
+ break;
+ case SPLITHIN:
+ trans.put(PdfName.S,PdfName.SPLIT);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DM,PdfName.H);
+ trans.put(PdfName.M,PdfName.I);
+ break;
+ case BLINDV:
+ trans.put(PdfName.S,PdfName.BLINDS);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DM,PdfName.V);
+ break;
+ case BLINDH:
+ trans.put(PdfName.S,PdfName.BLINDS);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DM,PdfName.H);
+ break;
+ case INBOX:
+ trans.put(PdfName.S,PdfName.BOX);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.M,PdfName.I);
+ break;
+ case OUTBOX:
+ trans.put(PdfName.S,PdfName.BOX);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.M,PdfName.O);
+ break;
+ case LRWIPE:
+ trans.put(PdfName.S,PdfName.WIPE);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DI,new PdfNumber(0));
+ break;
+ case RLWIPE:
+ trans.put(PdfName.S,PdfName.WIPE);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DI,new PdfNumber(180));
+ break;
+ case BTWIPE:
+ trans.put(PdfName.S,PdfName.WIPE);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DI,new PdfNumber(90));
+ break;
+ case TBWIPE:
+ trans.put(PdfName.S,PdfName.WIPE);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DI,new PdfNumber(270));
+ break;
+ case DISSOLVE:
+ trans.put(PdfName.S,PdfName.DISSOLVE);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ break;
+ case LRGLITTER:
+ trans.put(PdfName.S,PdfName.GLITTER);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DI,new PdfNumber(0));
+ break;
+ case TBGLITTER:
+ trans.put(PdfName.S,PdfName.GLITTER);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DI,new PdfNumber(270));
+ break;
+ case DGLITTER:
+ trans.put(PdfName.S,PdfName.GLITTER);
+ trans.put(PdfName.D,new PdfNumber(duration));
+ trans.put(PdfName.DI,new PdfNumber(315));
+ break;
+ }
+ return trans;
+ }
+}
+
diff --git a/src/main/java/com/lowagie/text/pdf/PdfTransparencyGroup.java b/src/main/java/com/lowagie/text/pdf/PdfTransparencyGroup.java
new file mode 100644
index 0000000..873b065
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfTransparencyGroup.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2003 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+/** The transparency group dictionary.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PdfTransparencyGroup extends PdfDictionary {
+
+ /**
+ * Constructs a transparencyGroup.
+ */
+ public PdfTransparencyGroup() {
+ super();
+ put(PdfName.S, PdfName.TRANSPARENCY);
+ }
+
+ /**
+ * Determining the initial backdrop against which its stack is composited.
+ * @param isolated
+ */
+ public void setIsolated(boolean isolated) {
+ if (isolated)
+ put(PdfName.I, PdfBoolean.PDFTRUE);
+ else
+ remove(PdfName.I);
+ }
+
+ /**
+ * Determining whether the objects within the stack are composited with one another or only with the group's backdrop.
+ * @param knockout
+ */
+ public void setKnockout(boolean knockout) {
+ if (knockout)
+ put(PdfName.K, PdfBoolean.PDFTRUE);
+ else
+ remove(PdfName.K);
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/PdfWriter.java b/src/main/java/com/lowagie/text/pdf/PdfWriter.java
new file mode 100644
index 0000000..ed93b71
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfWriter.java
@@ -0,0 +1,2752 @@
+/*
+ * $Id: PdfWriter.java,v 1.114 2006/06/04 22:23:33 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.HashSet;
+
+import com.lowagie.text.DocListener;
+import com.lowagie.text.DocWriter;
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Image;
+import com.lowagie.text.ImgWMF;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Table;
+import com.lowagie.text.ImgPostscript;
+import com.lowagie.text.pdf.events.PdfPageEventForwarder;
+
+/**
+ * A DocWriter
class for PDF.
+ * PdfWriter
is added
+ * to a certain PdfDocument
, the PDF representation of every Element
+ * added to this Document will be written to the outputstream.PdfCrossReference
is an entry in the PDF Cross-Reference table.
+ */
+
+ static class PdfCrossReference implements Comparable {
+
+ // membervariables
+ private int type;
+
+ /** Byte offset in the PDF file. */
+ private int offset;
+
+ private int refnum;
+ /** generation of the object. */
+ private int generation;
+
+ // constructors
+ /**
+ * Constructs a cross-reference element for a PdfIndirectObject.
+ * @param refnum
+ * @param offset byte offset of the object
+ * @param generation generationnumber of the object
+ */
+
+ PdfCrossReference(int refnum, int offset, int generation) {
+ type = 0;
+ this.offset = offset;
+ this.refnum = refnum;
+ this.generation = generation;
+ }
+
+ /**
+ * Constructs a cross-reference element for a PdfIndirectObject.
+ * @param refnum
+ * @param offset byte offset of the object
+ */
+
+ PdfCrossReference(int refnum, int offset) {
+ type = 1;
+ this.offset = offset;
+ this.refnum = refnum;
+ this.generation = 0;
+ }
+
+ PdfCrossReference(int type, int refnum, int offset, int generation) {
+ this.type = type;
+ this.offset = offset;
+ this.refnum = refnum;
+ this.generation = generation;
+ }
+
+ int getRefnum() {
+ return refnum;
+ }
+
+ /**
+ * Returns the PDF representation of this PdfObject
.
+ * @param os
+ * @throws IOException
+ */
+
+ public void toPdf(OutputStream os) throws IOException {
+ // This code makes it more difficult to port the lib to JDK1.1.x:
+ // StringBuffer off = new StringBuffer("0000000000").append(offset);
+ // off.delete(0, off.length() - 10);
+ // StringBuffer gen = new StringBuffer("00000").append(generation);
+ // gen.delete(0, gen.length() - 5);
+ // so it was changed into this:
+ String s = "0000000000" + offset;
+ StringBuffer off = new StringBuffer(s.substring(s.length() - 10));
+ s = "00000" + generation;
+ String gen = s.substring(s.length() - 5);
+ if (generation == 65535) {
+ os.write(getISOBytes(off.append(' ').append(gen).append(" f \n").toString()));
+ }
+ else
+ os.write(getISOBytes(off.append(' ').append(gen).append(" n \n").toString()));
+ }
+
+ /**
+ * Writes PDF syntax to the OutputStream
+ * @param midSize
+ * @param os
+ * @throws IOException
+ */
+ public void toPdf(int midSize, OutputStream os) throws IOException {
+ os.write((byte)type);
+ while (--midSize >= 0)
+ os.write((byte)((offset >>> (8 * midSize)) & 0xff));
+ os.write((byte)((generation >>> 8) & 0xff));
+ os.write((byte)(generation & 0xff));
+ }
+
+ /**
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(Object o) {
+ PdfCrossReference other = (PdfCrossReference)o;
+ return (refnum < other.refnum ? -1 : (refnum==other.refnum ? 0 : 1));
+ }
+
+ /**
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj instanceof PdfCrossReference) {
+ PdfCrossReference other = (PdfCrossReference)obj;
+ return (refnum == other.refnum);
+ }
+ else
+ return false;
+ }
+
+ }
+
+ // membervariables
+
+ /** array containing the cross-reference table of the normal objects. */
+ private TreeSet xrefs;
+ private int refnum;
+ /** the current byteposition in the body. */
+ private int position;
+ private PdfWriter writer;
+ // constructors
+
+ /**
+ * Constructs a new PdfBody
.
+ * @param writer
+ */
+ PdfBody(PdfWriter writer) {
+ xrefs = new TreeSet();
+ xrefs.add(new PdfCrossReference(0, 0, 65535));
+ position = writer.getOs().getCounter();
+ refnum = 1;
+ this.writer = writer;
+ }
+
+ void setRefnum(int refnum) {
+ this.refnum = refnum;
+ }
+
+ // methods
+
+ private static final int OBJSINSTREAM = 200;
+
+ private ByteBuffer index;
+ private ByteBuffer streamObjects;
+ private int currentObjNum;
+ private int numObj = 0;
+
+ private PdfWriter.PdfBody.PdfCrossReference addToObjStm(PdfObject obj, int nObj) throws IOException {
+ if (numObj >= OBJSINSTREAM)
+ flushObjStm();
+ if (index == null) {
+ index = new ByteBuffer();
+ streamObjects = new ByteBuffer();
+ currentObjNum = getIndirectReferenceNumber();
+ numObj = 0;
+ }
+ int p = streamObjects.size();
+ int idx = numObj++;
+ PdfEncryption enc = writer.crypto;
+ writer.crypto = null;
+ obj.toPdf(writer, streamObjects);
+ writer.crypto = enc;
+ streamObjects.append(' ');
+ index.append(nObj).append(' ').append(p).append(' ');
+ return new PdfWriter.PdfBody.PdfCrossReference(2, nObj, currentObjNum, idx);
+ }
+
+ private void flushObjStm() throws IOException {
+ if (numObj == 0)
+ return;
+ int first = index.size();
+ index.append(streamObjects);
+ PdfStream stream = new PdfStream(index.toByteArray());
+ stream.flateCompress();
+ stream.put(PdfName.TYPE, PdfName.OBJSTM);
+ stream.put(PdfName.N, new PdfNumber(numObj));
+ stream.put(PdfName.FIRST, new PdfNumber(first));
+ add(stream, currentObjNum);
+ index = null;
+ streamObjects = null;
+ numObj = 0;
+ }
+
+ /**
+ * Adds a PdfObject
to the body.
+ * PdfIndirectObject
with a
+ * certain number, containing the given PdfObject
.
+ * It also adds a PdfCrossReference
for this object
+ * to an ArrayList
that will be used to build the
+ * Cross-reference Table.
+ *
+ * @param object a PdfObject
+ * @return a PdfIndirectObject
+ * @throws IOException
+ */
+
+ PdfIndirectObject add(PdfObject object) throws IOException {
+ return add(object, getIndirectReferenceNumber());
+ }
+
+ PdfIndirectObject add(PdfObject object, boolean inObjStm) throws IOException {
+ return add(object, getIndirectReferenceNumber(), inObjStm);
+ }
+
+ /**
+ * Gets a PdfIndirectReference for an object that will be created in the future.
+ * @return a PdfIndirectReference
+ */
+
+ PdfIndirectReference getPdfIndirectReference() {
+ return new PdfIndirectReference(0, getIndirectReferenceNumber());
+ }
+
+ int getIndirectReferenceNumber() {
+ int n = refnum++;
+ xrefs.add(new PdfCrossReference(n, 0, 65536));
+ return n;
+ }
+
+ /**
+ * Adds a PdfObject
to the body given an already existing
+ * PdfIndirectReference.
+ * PdfIndirectObject
with the number given by
+ * ref
, containing the given PdfObject
.
+ * It also adds a PdfCrossReference
for this object
+ * to an ArrayList
that will be used to build the
+ * Cross-reference Table.
+ *
+ * @param object a PdfObject
+ * @param ref a PdfIndirectReference
+ * @return a PdfIndirectObject
+ * @throws IOException
+ */
+
+ PdfIndirectObject add(PdfObject object, PdfIndirectReference ref) throws IOException {
+ return add(object, ref.getNumber());
+ }
+
+ PdfIndirectObject add(PdfObject object, PdfIndirectReference ref, boolean inObjStm) throws IOException {
+ return add(object, ref.getNumber(), inObjStm);
+ }
+
+ PdfIndirectObject add(PdfObject object, int refNumber) throws IOException {
+ return add(object, refNumber, true); // to false
+ }
+
+ PdfIndirectObject add(PdfObject object, int refNumber, boolean inObjStm) throws IOException {
+ if (inObjStm && object.canBeInObjStm() && writer.isFullCompression()) {
+ PdfCrossReference pxref = addToObjStm(object, refNumber);
+ PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer);
+ if (!xrefs.add(pxref)) {
+ xrefs.remove(pxref);
+ xrefs.add(pxref);
+ }
+ return indirect;
+ }
+ else {
+ PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer);
+ PdfCrossReference pxref = new PdfCrossReference(refNumber, position);
+ if (!xrefs.add(pxref)) {
+ xrefs.remove(pxref);
+ xrefs.add(pxref);
+ }
+ indirect.writeTo(writer.getOs());
+ position = writer.getOs().getCounter();
+ return indirect;
+ }
+ }
+
+ /**
+ * Adds a PdfResources
object to the body.
+ *
+ * @param object the PdfResources
+ * @return a PdfIndirectObject
+ */
+
+// PdfIndirectObject add(PdfResources object) {
+// return add(object);
+// }
+
+ /**
+ * Adds a PdfPages
object to the body.
+ *
+ * @param object the root of the document
+ * @return a PdfIndirectObject
+ */
+
+// PdfIndirectObject add(PdfPages object) throws IOException {
+// PdfIndirectObject indirect = new PdfIndirectObject(PdfWriter.ROOT, object, writer);
+// rootOffset = position;
+// indirect.writeTo(writer.getOs());
+// position = writer.getOs().getCounter();
+// return indirect;
+// }
+
+ /**
+ * Returns the offset of the Cross-Reference table.
+ *
+ * @return an offset
+ */
+
+ int offset() {
+ return position;
+ }
+
+ /**
+ * Returns the total number of objects contained in the CrossReferenceTable of this Body
.
+ *
+ * @return a number of objects
+ */
+
+ int size() {
+ return Math.max(((PdfCrossReference)xrefs.last()).getRefnum() + 1, refnum);
+ }
+
+ /**
+ * Returns the CrossReferenceTable of the Body
.
+ * @param os
+ * @param root
+ * @param info
+ * @param encryption
+ * @param fileID
+ * @param prevxref
+ * @throws IOException
+ */
+
+ void writeCrossReferenceTable(OutputStream os, PdfIndirectReference root, PdfIndirectReference info, PdfIndirectReference encryption, PdfObject fileID, int prevxref) throws IOException {
+ int refNumber = 0;
+ if (writer.isFullCompression()) {
+ flushObjStm();
+ refNumber = getIndirectReferenceNumber();
+ xrefs.add(new PdfCrossReference(refNumber, position));
+ }
+ PdfCrossReference entry = (PdfCrossReference)xrefs.first();
+ int first = entry.getRefnum();
+ int len = 0;
+ ArrayList sections = new ArrayList();
+ for (Iterator i = xrefs.iterator(); i.hasNext(); ) {
+ entry = (PdfCrossReference)i.next();
+ if (first + len == entry.getRefnum())
+ ++len;
+ else {
+ sections.add(new Integer(first));
+ sections.add(new Integer(len));
+ first = entry.getRefnum();
+ len = 1;
+ }
+ }
+ sections.add(new Integer(first));
+ sections.add(new Integer(len));
+ if (writer.isFullCompression()) {
+ int mid = 4;
+ int mask = 0xff000000;
+ for (; mid > 1; --mid) {
+ if ((mask & position) != 0)
+ break;
+ mask >>>= 8;
+ }
+ ByteBuffer buf = new ByteBuffer();
+
+ for (Iterator i = xrefs.iterator(); i.hasNext(); ) {
+ entry = (PdfCrossReference) i.next();
+ entry.toPdf(mid, buf);
+ }
+ PdfStream xr = new PdfStream(buf.toByteArray());
+ buf = null;
+ xr.flateCompress();
+ xr.put(PdfName.SIZE, new PdfNumber(size()));
+ xr.put(PdfName.ROOT, root);
+ if (info != null) {
+ xr.put(PdfName.INFO, info);
+ }
+ if (encryption != null)
+ xr.put(PdfName.ENCRYPT, encryption);
+ if (fileID != null)
+ xr.put(PdfName.ID, fileID);
+ xr.put(PdfName.W, new PdfArray(new int[]{1, mid, 2}));
+ xr.put(PdfName.TYPE, PdfName.XREF);
+ PdfArray idx = new PdfArray();
+ for (int k = 0; k < sections.size(); ++k)
+ idx.add(new PdfNumber(((Integer)sections.get(k)).intValue()));
+ xr.put(PdfName.INDEX, idx);
+ if (prevxref > 0)
+ xr.put(PdfName.PREV, new PdfNumber(prevxref));
+ PdfEncryption enc = writer.crypto;
+ writer.crypto = null;
+ PdfIndirectObject indirect = new PdfIndirectObject(refNumber, xr, writer);
+ indirect.writeTo(writer.getOs());
+ writer.crypto = enc;
+ }
+ else {
+ os.write(getISOBytes("xref\n"));
+ Iterator i = xrefs.iterator();
+ for (int k = 0; k < sections.size(); k += 2) {
+ first = ((Integer)sections.get(k)).intValue();
+ len = ((Integer)sections.get(k + 1)).intValue();
+ os.write(getISOBytes(String.valueOf(first)));
+ os.write(getISOBytes(" "));
+ os.write(getISOBytes(String.valueOf(len)));
+ os.write('\n');
+ while (len-- > 0) {
+ entry = (PdfCrossReference) i.next();
+ entry.toPdf(os);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * PdfTrailer
is the PDF Trailer object.
+ * PdfCrossReferenceTable
+ * @param offset offset of the PdfCrossReferenceTable
+ * @param root an indirect reference to the root of the PDF document
+ * @param info an indirect reference to the info object of the PDF document
+ * @param encryption
+ * @param fileID
+ * @param prevxref
+ */
+
+ PdfTrailer(int size, int offset, PdfIndirectReference root, PdfIndirectReference info, PdfIndirectReference encryption, PdfObject fileID, int prevxref) {
+ this.offset = offset;
+ put(PdfName.SIZE, new PdfNumber(size));
+ put(PdfName.ROOT, root);
+ if (info != null) {
+ put(PdfName.INFO, info);
+ }
+ if (encryption != null)
+ put(PdfName.ENCRYPT, encryption);
+ if (fileID != null)
+ put(PdfName.ID, fileID);
+ if (prevxref > 0)
+ put(PdfName.PREV, new PdfNumber(prevxref));
+ }
+
+ /**
+ * Returns the PDF representation of this PdfObject
.
+ * @param writer
+ * @param os
+ * @throws IOException
+ */
+ public void toPdf(PdfWriter writer, OutputStream os) throws IOException {
+ os.write(getISOBytes("trailer\n"));
+ super.toPdf(null, os);
+ os.write(getISOBytes("\nstartxref\n"));
+ os.write(getISOBytes(String.valueOf(offset)));
+ os.write(getISOBytes("\n%%EOF\n"));
+ }
+ }
+ // static membervariables
+
+ /** A viewer preference */
+ public static final int PageLayoutSinglePage = 1;
+ /** A viewer preference */
+ public static final int PageLayoutOneColumn = 2;
+ /** A viewer preference */
+ public static final int PageLayoutTwoColumnLeft = 4;
+ /** A viewer preference */
+ public static final int PageLayoutTwoColumnRight = 8;
+ /** A viewer preference */
+ public static final int PageLayoutTwoPageLeft = 1 << 22;
+ /** A viewer preference */
+ public static final int PageLayoutTwoPageRight = 1 << 23;
+
+ /** A viewer preference */
+ public static final int PageModeUseNone = 16;
+ /** A viewer preference */
+ public static final int PageModeUseOutlines = 32;
+ /** A viewer preference */
+ public static final int PageModeUseThumbs = 64;
+ /** A viewer preference */
+ public static final int PageModeFullScreen = 128;
+ /** A viewer preference */
+ public static final int PageModeUseOC = 1 << 20;
+ /** A viewer preference */
+ public static final int PageModeUseAttachments = 1 << 24;
+
+ /** A viewer preference */
+ public static final int HideToolbar = 256;
+ /** A viewer preference */
+ public static final int HideMenubar = 512;
+ /** A viewer preference */
+ public static final int HideWindowUI = 1024;
+ /** A viewer preference */
+ public static final int FitWindow = 2048;
+ /** A viewer preference */
+ public static final int CenterWindow = 4096;
+
+ /** A viewer preference */
+ public static final int NonFullScreenPageModeUseNone = 8192;
+ /** A viewer preference */
+ public static final int NonFullScreenPageModeUseOutlines = 16384;
+ /** A viewer preference */
+ public static final int NonFullScreenPageModeUseThumbs = 32768;
+ /** A viewer preference */
+ public static final int NonFullScreenPageModeUseOC = 1 << 19;
+
+ /** A viewer preference */
+ public static final int DirectionL2R = 1 << 16;
+ /** A viewer preference */
+ public static final int DirectionR2L = 1 << 17;
+ /** A viewer preference */
+ public static final int DisplayDocTitle = 1 << 18;
+ /** A viewer preference */
+ public static final int PrintScalingNone = 1 << 21;
+ /** The mask to decide if a ViewerPreferences dictionary is needed */
+ static final int ViewerPreferencesMask = 0xffff00;
+ /** The operation permitted when the document is opened with the user password */
+ public static final int AllowPrinting = 4 + 2048;
+ /** The operation permitted when the document is opened with the user password */
+ public static final int AllowModifyContents = 8;
+ /** The operation permitted when the document is opened with the user password */
+ public static final int AllowCopy = 16;
+ /** The operation permitted when the document is opened with the user password */
+ public static final int AllowModifyAnnotations = 32;
+ /** The operation permitted when the document is opened with the user password */
+ public static final int AllowFillIn = 256;
+ /** The operation permitted when the document is opened with the user password */
+ public static final int AllowScreenReaders = 512;
+ /** The operation permitted when the document is opened with the user password */
+ public static final int AllowAssembly = 1024;
+ /** The operation permitted when the document is opened with the user password */
+ public static final int AllowDegradedPrinting = 4;
+ /** Type of encryption */
+ public static final boolean STRENGTH40BITS = false;
+ /** Type of encryption */
+ public static final boolean STRENGTH128BITS = true;
+ /** action value */
+ public static final PdfName DOCUMENT_CLOSE = PdfName.WC;
+ /** action value */
+ public static final PdfName WILL_SAVE = PdfName.WS;
+ /** action value */
+ public static final PdfName DID_SAVE = PdfName.DS;
+ /** action value */
+ public static final PdfName WILL_PRINT = PdfName.WP;
+ /** action value */
+ public static final PdfName DID_PRINT = PdfName.DP;
+ /** action value */
+ public static final PdfName PAGE_OPEN = PdfName.O;
+ /** action value */
+ public static final PdfName PAGE_CLOSE = PdfName.C;
+
+ /** signature value */
+ public static final int SIGNATURE_EXISTS = 1;
+ /** signature value */
+ public static final int SIGNATURE_APPEND_ONLY = 2;
+
+ /** possible PDF version */
+ public static final char VERSION_1_2 = '2';
+ /** possible PDF version */
+ public static final char VERSION_1_3 = '3';
+ /** possible PDF version */
+ public static final char VERSION_1_4 = '4';
+ /** possible PDF version */
+ public static final char VERSION_1_5 = '5';
+ /** possible PDF version */
+ public static final char VERSION_1_6 = '6';
+
+ private static final int VPOINT = 7;
+ /** this is the header of a PDF document */
+ protected byte[] HEADER = getISOBytes("%PDF-1.4\n%\u00e2\u00e3\u00cf\u00d3\n");
+
+ protected int prevxref = 0;
+
+ protected PdfPages root = new PdfPages(this);
+
+ /** Dictionary, containing all the images of the PDF document */
+ protected PdfDictionary imageDictionary = new PdfDictionary();
+
+ /** This is the list with all the images in the document. */
+ private HashMap images = new HashMap();
+
+ /** The form XObjects in this document. The key is the xref and the value
+ is Object[]{PdfName, template}.*/
+ protected HashMap formXObjects = new HashMap();
+
+ /** The name counter for the form XObjects name. */
+ protected int formXObjectsCounter = 1;
+
+ /** The font number counter for the fonts in the document. */
+ protected int fontNumber = 1;
+
+ /** The color number counter for the colors in the document. */
+ protected int colorNumber = 1;
+
+ /** The patten number counter for the colors in the document. */
+ protected int patternNumber = 1;
+
+ /** The direct content in this document. */
+ protected PdfContentByte directContent;
+
+ /** The direct content under in this document. */
+ protected PdfContentByte directContentUnder;
+
+ /** The fonts of this document */
+ protected HashMap documentFonts = new HashMap();
+
+ /** The colors of this document */
+ protected HashMap documentColors = new HashMap();
+
+ /** The patterns of this document */
+ protected HashMap documentPatterns = new HashMap();
+
+ protected HashMap documentShadings = new HashMap();
+
+ protected HashMap documentShadingPatterns = new HashMap();
+
+ protected ColorDetails patternColorspaceRGB;
+ protected ColorDetails patternColorspaceGRAY;
+ protected ColorDetails patternColorspaceCMYK;
+ protected HashMap documentSpotPatterns = new HashMap();
+
+ protected HashMap documentExtGState = new HashMap();
+
+ protected HashMap documentProperties = new HashMap();
+ protected HashSet documentOCG = new HashSet();
+ protected ArrayList documentOCGorder = new ArrayList();
+ protected PdfOCProperties OCProperties;
+ protected PdfArray OCGRadioGroup = new PdfArray();
+
+ protected PdfDictionary defaultColorspace = new PdfDictionary();
+ protected float userunit = 0f;
+
+ /** PDF/X value */
+ public static final int PDFXNONE = 0;
+ /** PDF/X value */
+ public static final int PDFX1A2001 = 1;
+ /** PDF/X value */
+ public static final int PDFX32002 = 2;
+
+ private int pdfxConformance = PDFXNONE;
+
+ static final int PDFXKEY_COLOR = 1;
+ static final int PDFXKEY_CMYK = 2;
+ static final int PDFXKEY_RGB = 3;
+ static final int PDFXKEY_FONT = 4;
+ static final int PDFXKEY_IMAGE = 5;
+ static final int PDFXKEY_GSTATE = 6;
+ static final int PDFXKEY_LAYER = 7;
+
+ // membervariables
+
+ /** body of the PDF document */
+ protected PdfBody body;
+
+ /** the pdfdocument object. */
+ protected PdfDocument pdf;
+
+ /** The PdfPageEvent
for this document. */
+ private PdfPageEvent pageEvent;
+
+ protected PdfEncryption crypto;
+
+ protected HashMap importedPages = new HashMap();
+
+ protected PdfReaderInstance currentPdfReaderInstance;
+
+ /** The PdfIndirectReference to the pages. */
+ protected ArrayList pageReferences = new ArrayList();
+
+ protected int currentPageNumber = 1;
+
+ protected PdfDictionary group;
+
+ /** The default space-char ratio. */
+ public static final float SPACE_CHAR_RATIO_DEFAULT = 2.5f;
+ /** Disable the inter-character spacing. */
+ public static final float NO_SPACE_CHAR_RATIO = 10000000f;
+
+ /** Use the default run direction. */
+ public static final int RUN_DIRECTION_DEFAULT = 0;
+ /** Do not use bidirectional reordering. */
+ public static final int RUN_DIRECTION_NO_BIDI = 1;
+ /** Use bidirectional reordering with left-to-right
+ * preferential run direction.
+ */
+ public static final int RUN_DIRECTION_LTR = 2;
+ /** Use bidirectional reordering with right-to-left
+ * preferential run direction.
+ */
+ public static final int RUN_DIRECTION_RTL = 3;
+ protected int runDirection = RUN_DIRECTION_NO_BIDI;
+ /**
+ * The ratio between the extra word spacing and the extra character spacing.
+ * Extra word spacing will grow ratio
times more than extra character spacing.
+ */
+ private float spaceCharRatio = SPACE_CHAR_RATIO_DEFAULT;
+
+ /** Holds value of property extraCatalog. */
+ private PdfDictionary extraCatalog;
+
+ /** XMP Metadata for the document. */
+ protected byte[] xmpMetadata = null;
+ /**
+ * Holds value of property fullCompression.
+ */
+ protected boolean fullCompression = false;
+
+ protected boolean tagged = false;
+
+ protected PdfStructureTreeRoot structureTreeRoot;
+
+ // constructor
+
+ protected PdfWriter() {
+ }
+
+ /**
+ * Constructs a PdfWriter
.
+ * getInstance(Document document, OutputStream os)
.
+ *
+ * @param document The PdfDocument
that has to be written
+ * @param os The OutputStream
the writer has to write to.
+ */
+
+ protected PdfWriter(PdfDocument document, OutputStream os) {
+ super(document, os);
+ pdf = document;
+ directContent = new PdfContentByte(this);
+ directContentUnder = new PdfContentByte(this);
+ }
+
+ // get an instance of the PdfWriter
+
+ /**
+ * Gets an instance of the PdfWriter
.
+ *
+ * @param document The Document
that has to be written
+ * @param os The OutputStream
the writer has to write to.
+ * @return a new PdfWriter
+ *
+ * @throws DocumentException on error
+ */
+
+ public static PdfWriter getInstance(Document document, OutputStream os)
+ throws DocumentException {
+ PdfDocument pdf = new PdfDocument();
+ document.addDocListener(pdf);
+ PdfWriter writer = new PdfWriter(pdf, os);
+ pdf.addWriter(writer);
+ return writer;
+ }
+
+ /** Gets an instance of the PdfWriter
.
+ *
+ * @return a new PdfWriter
+ * @param document The Document
that has to be written
+ * @param os The OutputStream
the writer has to write to.
+ * @param listener A DocListener
to pass to the PdfDocument.
+ * @throws DocumentException on error
+ */
+
+ public static PdfWriter getInstance(Document document, OutputStream os, DocListener listener)
+ throws DocumentException {
+ PdfDocument pdf = new PdfDocument();
+ pdf.addDocListener(listener);
+ document.addDocListener(pdf);
+ PdfWriter writer = new PdfWriter(pdf, os);
+ pdf.addWriter(writer);
+ return writer;
+ }
+
+ // methods to write objects to the outputstream
+
+ /**
+ * Adds some PdfContents
to this Writer.
+ * PdfIndirectReference
+ * @param page the PdfPage
to add
+ * @param contents the PdfContents
of the page
+ * @throws PdfException on error
+ */
+
+ PdfIndirectReference add(PdfPage page, PdfContents contents) throws PdfException {
+ if (!open) {
+ throw new PdfException("The document isn't open.");
+ }
+ PdfIndirectObject object;
+ try {
+ object = addToBody(contents);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ page.add(object.getIndirectReference());
+ if (group != null) {
+ page.put(PdfName.GROUP, group);
+ group = null;
+ }
+ root.addPage(page);
+ currentPageNumber++;
+ return null;
+ }
+
+ /**
+ * Adds an image to the document but not to the page resources. It is used with
+ * templates and Document.add(Image)
.
+ * @param image the Image
to add
+ * @return the name of the image added
+ * @throws PdfException on error
+ * @throws DocumentException on error
+ */
+ public PdfName addDirectImageSimple(Image image) throws PdfException, DocumentException {
+ return addDirectImageSimple(image, null);
+ }
+
+ /**
+ * Adds an image to the document but not to the page resources. It is used with
+ * templates and Document.add(Image)
.
+ * @param image the Image
to add
+ * @param fixedRef the reference to used. It may be null
,
+ * a PdfIndirectReference
or a PRIndirectReference
.
+ * @return the name of the image added
+ * @throws PdfException on error
+ * @throws DocumentException on error
+ */
+ public PdfName addDirectImageSimple(Image image, PdfIndirectReference fixedRef) throws PdfException, DocumentException {
+ PdfName name;
+ // if the images is already added, just retrieve the name
+ if (images.containsKey(image.getMySerialId())) {
+ name = (PdfName) images.get(image.getMySerialId());
+ }
+ // if it's a new image, add it to the document
+ else {
+ if (image.isImgTemplate()) {
+ name = new PdfName("img" + images.size());
+ if (image.templateData() == null) {
+ if(image instanceof ImgWMF){
+ try {
+ ImgWMF wmf = (ImgWMF)image;
+ wmf.readWMF(getDirectContent().createTemplate(0, 0));
+ }
+ catch (Exception e) {
+ throw new DocumentException(e);
+ }
+ }else{
+ try {
+ ((ImgPostscript)image).readPostscript(getDirectContent().createTemplate(0, 0));
+ }
+ catch (Exception e) {
+ throw new DocumentException(e);
+ }
+
+ }
+ }
+ }
+ else {
+ PdfIndirectReference dref = image.getDirectReference();
+ if (dref != null) {
+ PdfName rname = new PdfName("img" + images.size());
+ images.put(image.getMySerialId(), rname);
+ imageDictionary.put(rname, dref);
+ return rname;
+ }
+ Image maskImage = image.getImageMask();
+ PdfIndirectReference maskRef = null;
+ if (maskImage != null) {
+ PdfName mname = (PdfName)images.get(maskImage.getMySerialId());
+ maskRef = getImageReference(mname);
+ }
+ PdfImage i = new PdfImage(image, "img" + images.size(), maskRef);
+ if (image.hasICCProfile()) {
+ PdfICCBased icc = new PdfICCBased(image.getICCProfile());
+ PdfIndirectReference iccRef = add(icc);
+ PdfArray iccArray = new PdfArray();
+ iccArray.add(PdfName.ICCBASED);
+ iccArray.add(iccRef);
+ PdfObject colorspace = i.get(PdfName.COLORSPACE);
+ if (colorspace != null && colorspace.isArray()) {
+ ArrayList ar = ((PdfArray)colorspace).getArrayList();
+ if (ar.size() > 1 && PdfName.INDEXED.equals(ar.get(0)))
+ ar.set(1, iccArray);
+ else
+ i.put(PdfName.COLORSPACE, iccArray);
+ }
+ else
+ i.put(PdfName.COLORSPACE, iccArray);
+ }
+ add(i, fixedRef);
+ name = i.name();
+ }
+ images.put(image.getMySerialId(), name);
+ }
+ return name;
+ }
+
+ /**
+ * Writes a PdfImage
to the outputstream.
+ *
+ * @param pdfImage the image to be added
+ * @return a PdfIndirectReference
to the encapsulated image
+ * @throws PdfException when a document isn't open yet, or has been closed
+ */
+
+ PdfIndirectReference add(PdfImage pdfImage, PdfIndirectReference fixedRef) throws PdfException {
+ if (! imageDictionary.contains(pdfImage.name())) {
+ checkPDFXConformance(this, PDFXKEY_IMAGE, pdfImage);
+ if (fixedRef != null && fixedRef instanceof PRIndirectReference) {
+ PRIndirectReference r2 = (PRIndirectReference)fixedRef;
+ fixedRef = new PdfIndirectReference(0, getNewObjectNumber(r2.getReader(), r2.getNumber(), r2.getGeneration()));
+ }
+ try {
+ if (fixedRef == null)
+ fixedRef = addToBody(pdfImage).getIndirectReference();
+ else
+ addToBody(pdfImage, fixedRef);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ imageDictionary.put(pdfImage.name(), fixedRef);
+ return fixedRef;
+ }
+ return (PdfIndirectReference) imageDictionary.get(pdfImage.name());
+ }
+
+ protected PdfIndirectReference add(PdfICCBased icc) throws PdfException {
+ PdfIndirectObject object;
+ try {
+ object = addToBody(icc);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ return object.getIndirectReference();
+ }
+
+ /**
+ * return the PdfIndirectReference
to the image with a given name.
+ *
+ * @param name the name of the image
+ * @return a PdfIndirectReference
+ */
+
+ PdfIndirectReference getImageReference(PdfName name) {
+ return (PdfIndirectReference) imageDictionary.get(name);
+ }
+
+ // methods to open and close the writer
+
+ /**
+ * Signals that the Document
has been opened and that
+ * Elements
can be added.
+ * Document
was closed and that no other
+ * Elements
will be added.
+ * Table
ends.
+ *
+ * For instance to avoid to add another table in a page that is ending up, because
+ * the new table will be probably splitted just after the header (it is an
+ * unpleasant effect, isn't it?).
+ *
+ * Added on September 8th, 2001
+ * by Francesco De Milato
+ * francesco.demilato@tiscalinet.it
+ * @param table the Table
+ * @return the bottom height of the just added table
+ */
+
+ public float getTableBottom(Table table) {
+ return pdf.bottom(table) - pdf.indentBottom();
+ }
+
+ /**
+ * Gets a pre-rendered table.
+ * (Contributed by dperezcar@fcc.es)
+ * @param table Contains the table definition. Its contents are deleted, after being pre-rendered.
+ * @return a PdfTable
+ */
+
+ public PdfTable getPdfTable(Table table) {
+ return pdf.getPdfTable(table, true);
+ }
+
+ /**
+ * Row additions to the original {@link Table} used to build the {@link PdfTable} are processed and pre-rendered,
+ * and then the contents are deleted.
+ * If the pre-rendered table doesn't fit, then it is fully rendered and its data discarded.
+ * There shouldn't be any column change in the underlying {@link Table} object.
+ * (Contributed by dperezcar@fcc.es)
+ *
+ * @param table The pre-rendered table obtained from {@link #getPdfTable(Table)}
+ * @return true if the table is rendered and emptied.
+ * @throws DocumentException
+ * @see #getPdfTable(Table)
+ */
+
+ public boolean breakTableIfDoesntFit(PdfTable table) throws DocumentException {
+ return pdf.breakTableIfDoesntFit(table);
+ }
+
+ /**
+ * Checks if a Table
fits the current page of the PdfDocument
.
+ *
+ * @param table the table that has to be checked
+ * @param margin a certain margin
+ * @return true
if the Table
fits the page, false
otherwise.
+ */
+
+ public boolean fitsPage(Table table, float margin) {
+ return pdf.bottom(table) > pdf.indentBottom() + margin;
+ }
+
+ /**
+ * Checks if a Table
fits the current page of the PdfDocument
.
+ *
+ * @param table the table that has to be checked
+ * @return true
if the Table
fits the page, false
otherwise.
+ */
+
+ public boolean fitsPage(Table table) {
+ return fitsPage(table, 0);
+ }
+
+ /**
+ * Checks if a PdfPTable
fits the current page of the PdfDocument
.
+ *
+ * @param table the table that has to be checked
+ * @param margin a certain margin
+ * @return true
if the PdfPTable
fits the page, false
otherwise.
+ */
+ public boolean fitsPage(PdfPTable table, float margin) {
+ return pdf.fitsPage(table, margin);
+ }
+
+ /**
+ * Checks if a PdfPTable
fits the current page of the PdfDocument
.
+ *
+ * @param table the table that has to be checked
+ * @return true
if the PdfPTable
fits the page, false
otherwise.
+ */
+ public boolean fitsPage(PdfPTable table) {
+ return pdf.fitsPage(table, 0);
+ }
+
+ /**
+ * Gets the current vertical page position.
+ * @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects
+ * for elements that do not terminate the lines they've started because those lines will get
+ * terminated.
+ * @return The current vertical page position.
+ */
+ public float getVerticalPosition(boolean ensureNewLine) {
+ return pdf.getVerticalPosition(ensureNewLine);
+ }
+
+ /**
+ * Checks if writing is paused.
+ *
+ * @return true
if writing temporarely has to be paused, false
otherwise.
+ */
+
+ boolean isPaused() {
+ return pause;
+ }
+
+ /**
+ * Gets the direct content for this document. There is only one direct content,
+ * multiple calls to this method will allways retrieve the same.
+ * @return the direct content
+ */
+
+ public PdfContentByte getDirectContent() {
+ if (!open)
+ throw new RuntimeException("The document is not open.");
+ return directContent;
+ }
+
+ /**
+ * Gets the direct content under for this document. There is only one direct content,
+ * multiple calls to this method will allways retrieve the same.
+ * @return the direct content
+ */
+
+ public PdfContentByte getDirectContentUnder() {
+ if (!open)
+ throw new RuntimeException("The document is not open.");
+ return directContentUnder;
+ }
+
+ /**
+ * Resets all the direct contents to empty. This happens when a new page is started.
+ */
+
+ void resetContent() {
+ directContent.reset();
+ directContentUnder.reset();
+ }
+
+ /** Gets the AcroForm object.
+ * @return the PdfAcroForm
+ */
+
+ public PdfAcroForm getAcroForm() {
+ return pdf.getAcroForm();
+ }
+
+ /** Gets the root outline.
+ * @return the root outline
+ */
+
+ public PdfOutline getRootOutline() {
+ return directContent.getRootOutline();
+ }
+
+ /**
+ * Returns the outputStreamCounter.
+ * @return the outputStreamCounter
+ */
+ public OutputStreamCounter getOs() {
+ return os;
+ }
+
+ /**
+ * Adds a BaseFont
to the document but not to the page resources.
+ * It is used for templates.
+ * @param bf the BaseFont
to add
+ * @return an Object[]
where position 0 is a PdfName
+ * and position 1 is an PdfIndirectReference
+ */
+
+ FontDetails addSimple(BaseFont bf) {
+ if (bf.getFontType() == BaseFont.FONT_TYPE_DOCUMENT) {
+ return new FontDetails(new PdfName("F" + (fontNumber++)), ((DocumentFont)bf).getIndirectReference(), bf);
+ }
+ FontDetails ret = (FontDetails)documentFonts.get(bf);
+ if (ret == null) {
+ checkPDFXConformance(this, PDFXKEY_FONT, bf);
+ ret = new FontDetails(new PdfName("F" + (fontNumber++)), body.getPdfIndirectReference(), bf);
+ documentFonts.put(bf, ret);
+ }
+ return ret;
+ }
+
+ void eliminateFontSubset(PdfDictionary fonts) {
+ for (Iterator it = documentFonts.values().iterator(); it.hasNext();) {
+ FontDetails ft = (FontDetails)it.next();
+ if (fonts.get(ft.getFontName()) != null)
+ ft.setSubset(false);
+ }
+ }
+
+ PdfName getColorspaceName() {
+ return new PdfName("CS" + (colorNumber++));
+ }
+
+ /**
+ * Adds a SpotColor
to the document but not to the page resources.
+ * @param spc the SpotColor
to add
+ * @return an Object[]
where position 0 is a PdfName
+ * and position 1 is an PdfIndirectReference
+ */
+
+ ColorDetails addSimple(PdfSpotColor spc) {
+ ColorDetails ret = (ColorDetails)documentColors.get(spc);
+ if (ret == null) {
+ ret = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), spc);
+ documentColors.put(spc, ret);
+ }
+ return ret;
+ }
+
+ ColorDetails addSimplePatternColorspace(Color color) {
+ int type = ExtendedColor.getType(color);
+ if (type == ExtendedColor.TYPE_PATTERN || type == ExtendedColor.TYPE_SHADING)
+ throw new RuntimeException("An uncolored tile pattern can not have another pattern or shading as color.");
+ try {
+ switch (type) {
+ case ExtendedColor.TYPE_RGB:
+ if (patternColorspaceRGB == null) {
+ patternColorspaceRGB = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
+ PdfArray array = new PdfArray(PdfName.PATTERN);
+ array.add(PdfName.DEVICERGB);
+ addToBody(array, patternColorspaceRGB.getIndirectReference());
+ }
+ return patternColorspaceRGB;
+ case ExtendedColor.TYPE_CMYK:
+ if (patternColorspaceCMYK == null) {
+ patternColorspaceCMYK = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
+ PdfArray array = new PdfArray(PdfName.PATTERN);
+ array.add(PdfName.DEVICECMYK);
+ addToBody(array, patternColorspaceCMYK.getIndirectReference());
+ }
+ return patternColorspaceCMYK;
+ case ExtendedColor.TYPE_GRAY:
+ if (patternColorspaceGRAY == null) {
+ patternColorspaceGRAY = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
+ PdfArray array = new PdfArray(PdfName.PATTERN);
+ array.add(PdfName.DEVICEGRAY);
+ addToBody(array, patternColorspaceGRAY.getIndirectReference());
+ }
+ return patternColorspaceGRAY;
+ case ExtendedColor.TYPE_SEPARATION: {
+ ColorDetails details = addSimple(((SpotColor)color).getPdfSpotColor());
+ ColorDetails patternDetails = (ColorDetails)documentSpotPatterns.get(details);
+ if (patternDetails == null) {
+ patternDetails = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null);
+ PdfArray array = new PdfArray(PdfName.PATTERN);
+ array.add(details.getIndirectReference());
+ addToBody(array, patternDetails.getIndirectReference());
+ documentSpotPatterns.put(details, patternDetails);
+ }
+ return patternDetails;
+ }
+ default:
+ throw new RuntimeException("Invalid color type in PdfWriter.addSimplePatternColorspace().");
+ }
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ void addSimpleShadingPattern(PdfShadingPattern shading) {
+ if (!documentShadingPatterns.containsKey(shading)) {
+ shading.setName(patternNumber);
+ ++patternNumber;
+ documentShadingPatterns.put(shading, null);
+ addSimpleShading(shading.getShading());
+ }
+ }
+
+ void addSimpleShading(PdfShading shading) {
+ if (!documentShadings.containsKey(shading)) {
+ documentShadings.put(shading, null);
+ shading.setName(documentShadings.size());
+ }
+ }
+
+ PdfObject[] addSimpleExtGState(PdfDictionary gstate) {
+ if (!documentExtGState.containsKey(gstate)) {
+ checkPDFXConformance(this, PDFXKEY_GSTATE, gstate);
+ documentExtGState.put(gstate, new PdfObject[]{new PdfName("GS" + (documentExtGState.size() + 1)), getPdfIndirectReference()});
+ }
+ return (PdfObject[])documentExtGState.get(gstate);
+ }
+
+ void registerLayer(PdfOCG layer) {
+ checkPDFXConformance(this, PDFXKEY_LAYER, null);
+ if (layer instanceof PdfLayer) {
+ PdfLayer la = (PdfLayer)layer;
+ if (la.getTitle() == null) {
+ if (!documentOCG.contains(layer)) {
+ documentOCG.add(layer);
+ documentOCGorder.add(layer);
+ }
+ }
+ else {
+ documentOCGorder.add(layer);
+ }
+ }
+ else
+ throw new IllegalArgumentException("Only PdfLayer is accepted.");
+ }
+
+ PdfObject[] addSimpleProperty(Object prop, PdfIndirectReference refi) {
+ if (!documentProperties.containsKey(prop)) {
+ if (prop instanceof PdfOCG)
+ checkPDFXConformance(this, PDFXKEY_LAYER, null);
+ documentProperties.put(prop, new PdfObject[]{new PdfName("Pr" + (documentProperties.size() + 1)), refi});
+ }
+ return (PdfObject[])documentProperties.get(prop);
+ }
+
+ boolean propertyExists(Object prop) {
+ return documentProperties.containsKey(prop);
+ }
+ /**
+ * Gets the PdfDocument
associated with this writer.
+ * @return the PdfDocument
+ */
+
+ PdfDocument getPdfDocument() {
+ return pdf;
+ }
+
+ /**
+ * Gets a PdfIndirectReference
for an object that
+ * will be created in the future.
+ * @return the PdfIndirectReference
+ */
+
+ public PdfIndirectReference getPdfIndirectReference() {
+ return body.getPdfIndirectReference();
+ }
+
+ int getIndirectReferenceNumber() {
+ return body.getIndirectReferenceNumber();
+ }
+
+ PdfName addSimplePattern(PdfPatternPainter painter) {
+ PdfName name = (PdfName)documentPatterns.get(painter);
+ try {
+ if ( name == null ) {
+ name = new PdfName("P" + patternNumber);
+ ++patternNumber;
+ documentPatterns.put(painter, name);
+ }
+ } catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ return name;
+ }
+
+ /**
+ * Adds a template to the document but not to the page resources.
+ * @param template the template to add
+ * @param forcedName the template name, rather than a generated one. Can be null
+ * @return the PdfName
for this template
+ */
+
+ PdfName addDirectTemplateSimple(PdfTemplate template, PdfName forcedName) {
+ PdfIndirectReference ref = template.getIndirectReference();
+ Object obj[] = (Object[])formXObjects.get(ref);
+ PdfName name = null;
+ try {
+ if (obj == null) {
+ if (forcedName == null) {
+ name = new PdfName("Xf" + formXObjectsCounter);
+ ++formXObjectsCounter;
+ }
+ else
+ name = forcedName;
+ if (template.getType() == PdfTemplate.TYPE_IMPORTED)
+ template = null;
+ formXObjects.put(ref, new Object[]{name, template});
+ }
+ else
+ name = (PdfName)obj[0];
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ return name;
+ }
+
+ /**
+ * Sets the PdfPageEvent
for this document.
+ * @param event the PdfPageEvent
for this document
+ */
+
+ public void setPageEvent(PdfPageEvent event) {
+ if (event == null) this.pageEvent = null;
+ else if (this.pageEvent == null) this.pageEvent = event;
+ else if (this.pageEvent instanceof PdfPageEventForwarder) ((PdfPageEventForwarder)this.pageEvent).addPageEvent(event);
+ else {
+ PdfPageEventForwarder forward = new PdfPageEventForwarder();
+ forward.addPageEvent(this.pageEvent);
+ forward.addPageEvent(event);
+ this.pageEvent = forward;
+ }
+ }
+
+ /**
+ * Gets the PdfPageEvent
for this document or null
+ * if none is set.
+ * @return the PdfPageEvent
for this document or null
+ * if none is set
+ */
+
+ public PdfPageEvent getPageEvent() {
+ return pageEvent;
+ }
+
+ /**
+ * Adds the local destinations to the body of the document.
+ * @param dest the HashMap
containing the destinations
+ * @throws IOException on error
+ */
+
+ void addLocalDestinations(TreeMap dest) throws IOException {
+ for (Iterator i = dest.keySet().iterator(); i.hasNext();) {
+ String name = (String)i.next();
+ Object obj[] = (Object[])dest.get(name);
+ PdfDestination destination = (PdfDestination)obj[2];
+ if (destination == null)
+ throw new RuntimeException("The name '" + name + "' has no local destination.");
+ if (obj[1] == null)
+ obj[1] = getPdfIndirectReference();
+ addToBody(destination, (PdfIndirectReference)obj[1]);
+ }
+ }
+
+ /**
+ * Gets the current pagenumber of this document.
+ *
+ * @return a page number
+ */
+
+ public int getPageNumber() {
+ return pdf.getPageNumber();
+ }
+
+ /**
+ * Sets the viewer preferences by ORing some constants.
+ *
+ *
+ * @param preferences the viewer preferences
+ */
+
+ public void setViewerPreferences(int preferences) {
+ pdf.setViewerPreferences(preferences);
+ }
+
+ /** Sets the encryption options for this document. The userPassword and the
+ * ownerPassword can be null or have zero length. In this case the ownerPassword
+ * is replaced by a random string. The open permissions for the document can be
+ * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
+ * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
+ * The permissions can be combined by ORing them.
+ * @param userPassword the user password. Can be null or empty
+ * @param ownerPassword the owner password. Can be null or empty
+ * @param permissions the user permissions
+ * @param strength128Bits
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * true
for 128 bit key length, false
for 40 bit key length
+ * @throws DocumentException if the document is already open
+ */
+ public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, boolean strength128Bits) throws DocumentException {
+ if (pdf.isOpen())
+ throw new DocumentException("Encryption can only be added before opening the document.");
+ crypto = new PdfEncryption();
+ crypto.setupAllKeys(userPassword, ownerPassword, permissions, strength128Bits);
+ }
+
+ /**
+ * Sets the encryption options for this document. The userPassword and the
+ * ownerPassword can be null or have zero length. In this case the ownerPassword
+ * is replaced by a random string. The open permissions for the document can be
+ * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations,
+ * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting.
+ * The permissions can be combined by ORing them.
+ * @param strength true
for 128 bit key length, false
for 40 bit key length
+ * @param userPassword the user password. Can be null or empty
+ * @param ownerPassword the owner password. Can be null or empty
+ * @param permissions the user permissions
+ * @throws DocumentException if the document is already open
+ */
+ public void setEncryption(boolean strength, String userPassword, String ownerPassword, int permissions) throws DocumentException {
+ setEncryption(getISOBytes(userPassword), getISOBytes(ownerPassword), permissions, strength);
+ }
+
+ /**
+ * Adds an object to the PDF body.
+ * @param object
+ * @return a PdfIndirectObject
+ * @throws IOException
+ */
+ public PdfIndirectObject addToBody(PdfObject object) throws IOException {
+ PdfIndirectObject iobj = body.add(object);
+ return iobj;
+ }
+
+ /**
+ * Adds an object to the PDF body.
+ * @param object
+ * @param inObjStm
+ * @return a PdfIndirectObject
+ * @throws IOException
+ */
+ public PdfIndirectObject addToBody(PdfObject object, boolean inObjStm) throws IOException {
+ PdfIndirectObject iobj = body.add(object, inObjStm);
+ return iobj;
+ }
+
+ /**
+ * Adds an object to the PDF body.
+ * @param object
+ * @param ref
+ * @return a PdfIndirectObject
+ * @throws IOException
+ */
+ public PdfIndirectObject addToBody(PdfObject object, PdfIndirectReference ref) throws IOException {
+ PdfIndirectObject iobj = body.add(object, ref);
+ return iobj;
+ }
+
+ /**
+ * Adds an object to the PDF body.
+ * @param object
+ * @param ref
+ * @param inObjStm
+ * @return a PdfIndirectObject
+ * @throws IOException
+ */
+ public PdfIndirectObject addToBody(PdfObject object, PdfIndirectReference ref, boolean inObjStm) throws IOException {
+ PdfIndirectObject iobj = body.add(object, ref, inObjStm);
+ return iobj;
+ }
+
+ /**
+ * Adds an object to the PDF body.
+ * @param object
+ * @param refNumber
+ * @return a PdfIndirectObject
+ * @throws IOException
+ */
+ public PdfIndirectObject addToBody(PdfObject object, int refNumber) throws IOException {
+ PdfIndirectObject iobj = body.add(object, refNumber);
+ return iobj;
+ }
+
+ /**
+ * Adds an object to the PDF body.
+ * @param object
+ * @param refNumber
+ * @param inObjStm
+ * @return a PdfIndirectObject
+ * @throws IOException
+ */
+ public PdfIndirectObject addToBody(PdfObject object, int refNumber, boolean inObjStm) throws IOException {
+ PdfIndirectObject iobj = body.add(object, refNumber, inObjStm);
+ return iobj;
+ }
+
+ /** When the document opens it will jump to the destination with
+ * this name.
+ * @param name the name of the destination to jump to
+ */
+ public void setOpenAction(String name) {
+ pdf.setOpenAction(name);
+ }
+
+ /** Additional-actions defining the actions to be taken in
+ * response to various trigger events affecting the document
+ * as a whole. The actions types allowed are: DOCUMENT_CLOSE
,
+ * WILL_SAVE
, DID_SAVE
, WILL_PRINT
+ * and DID_PRINT
.
+ *
+ * @param actionType the action type
+ * @param action the action to execute in response to the trigger
+ * @throws PdfException on invalid action type
+ */
+ public void setAdditionalAction(PdfName actionType, PdfAction action) throws PdfException {
+ if (!(actionType.equals(DOCUMENT_CLOSE) ||
+ actionType.equals(WILL_SAVE) ||
+ actionType.equals(DID_SAVE) ||
+ actionType.equals(WILL_PRINT) ||
+ actionType.equals(DID_PRINT))) {
+ throw new PdfException("Invalid additional action type: " + actionType.toString());
+ }
+ pdf.addAdditionalAction(actionType, action);
+ }
+
+ /** When the document opens this action
will be
+ * invoked.
+ * @param action the action to be invoked
+ */
+ public void setOpenAction(PdfAction action) {
+ pdf.setOpenAction(action);
+ }
+
+ /** Sets the page labels
+ * @param pageLabels the page labels
+ */
+ public void setPageLabels(PdfPageLabels pageLabels) {
+ pdf.setPageLabels(pageLabels);
+ }
+
+ PdfEncryption getEncryption() {
+ return crypto;
+ }
+
+ RandomAccessFileOrArray getReaderFile(PdfReader reader) {
+ return currentPdfReaderInstance.getReaderFile();
+ }
+
+ protected int getNewObjectNumber(PdfReader reader, int number, int generation) {
+ return currentPdfReaderInstance.getNewObjectNumber(number, generation);
+ }
+
+ /** Gets a page from other PDF document. The page can be used as
+ * any other PdfTemplate. Note that calling this method more than
+ * once with the same parameters will retrieve the same object.
+ * @param reader the PDF document where the page is
+ * @param pageNumber the page number. The first page is 1
+ * @return the template representing the imported page
+ */
+ public PdfImportedPage getImportedPage(PdfReader reader, int pageNumber) {
+ PdfReaderInstance inst = (PdfReaderInstance)importedPages.get(reader);
+ if (inst == null) {
+ inst = reader.getPdfReaderInstance(this);
+ importedPages.put(reader, inst);
+ }
+ return inst.getImportedPage(pageNumber);
+ }
+
+ /** Adds a JavaScript action at the document level. When the document
+ * opens all this JavaScript runs.
+ * @param js The JavaScrip action
+ */
+ public void addJavaScript(PdfAction js) {
+ pdf.addJavaScript(js);
+ }
+
+ /** Adds a JavaScript action at the document level. When the document
+ * opens all this JavaScript runs.
+ * @param code the JavaScript code
+ * @param unicode select JavaScript unicode. Note that the internal
+ * Acrobat JavaScript engine does not support unicode,
+ * so this may or may not work for you
+ */
+ public void addJavaScript(String code, boolean unicode) {
+ addJavaScript(PdfAction.javaScript(code, this, unicode));
+ }
+
+ /** Adds a JavaScript action at the document level. When the document
+ * opens all this JavaScript runs.
+ * @param code the JavaScript code
+ */
+ public void addJavaScript(String code) {
+ addJavaScript(code, false);
+ }
+
+ /** Adds a file attachment at the document level.
+ * @param description the file description
+ * @param fileStore an array with the file. If it's null
+ * the file will be read from the disk
+ * @param file the path to the file. It will only be used if
+ * fileStore
is not null
+ * @param fileDisplay the actual file name stored in the pdf
+ * @throws IOException on error
+ */
+ public void addFileAttachment(String description, byte fileStore[], String file, String fileDisplay) throws IOException {
+ addFileAttachment(description, PdfFileSpecification.fileEmbedded(this, file, fileDisplay, fileStore));
+ }
+
+ /** Adds a file attachment at the document level.
+ * @param description the file description
+ * @param fs the file specification
+ */
+ public void addFileAttachment(String description, PdfFileSpecification fs) throws IOException {
+ pdf.addFileAttachment(description, fs);
+ }
+
+ /** Sets the crop box. The crop box should not be rotated even if the
+ * page is rotated. This change only takes effect in the next
+ * page.
+ * @param crop the crop box
+ */
+ public void setCropBoxSize(Rectangle crop) {
+ pdf.setCropBoxSize(crop);
+ }
+
+ /** Gets a reference to a page existing or not. If the page does not exist
+ * yet the reference will be created in advance. If on closing the document, a
+ * page number greater than the total number of pages was requested, an
+ * exception is thrown.
+ * @param page the page number. The first page is 1
+ * @return the reference to the page
+ */
+ public PdfIndirectReference getPageReference(int page) {
+ --page;
+ if (page < 0)
+ throw new IndexOutOfBoundsException("The page numbers start at 1.");
+ PdfIndirectReference ref;
+ if (page < pageReferences.size()) {
+ ref = (PdfIndirectReference)pageReferences.get(page);
+ if (ref == null) {
+ ref = body.getPdfIndirectReference();
+ pageReferences.set(page, ref);
+ }
+ }
+ else {
+ int empty = page - pageReferences.size();
+ for (int k = 0; k < empty; ++k)
+ pageReferences.add(null);
+ ref = body.getPdfIndirectReference();
+ pageReferences.add(ref);
+ }
+ return ref;
+ }
+
+ PdfIndirectReference getCurrentPage() {
+ return getPageReference(currentPageNumber);
+ }
+
+ int getCurrentPageNumber() {
+ return currentPageNumber;
+ }
+
+ /** Adds the PdfAnnotation
to the calculation order
+ * array.
+ * @param annot the PdfAnnotation
to be added
+ */
+ public void addCalculationOrder(PdfFormField annot) {
+ pdf.addCalculationOrder(annot);
+ }
+
+ /** Set the signature flags.
+ * @param f the flags. This flags are ORed with current ones
+ */
+ public void setSigFlags(int f) {
+ pdf.setSigFlags(f);
+ }
+
+ /** Adds a PdfAnnotation
or a PdfFormField
+ * to the document. Only the top parent of a PdfFormField
+ * needs to be added.
+ * @param annot the PdfAnnotation
or the PdfFormField
to add
+ */
+ public void addAnnotation(PdfAnnotation annot) {
+ pdf.addAnnotation(annot);
+ }
+
+ void addAnnotation(PdfAnnotation annot, int page) {
+ addAnnotation(annot);
+ }
+
+ /** Sets the PDF version. Must be used right before the document
+ * is opened. Valid options are VERSION_1_2, VERSION_1_3,
+ * VERSION_1_4, VERSION_1_5 and VERSION_1_6. VERSION_1_4 is the default.
+ * @param version the version number
+ */
+ public void setPdfVersion(char version) {
+ if (HEADER.length > VPOINT)
+ HEADER[VPOINT] = (byte)version;
+ }
+
+ /** Reorder the pages in the document. A null
argument value
+ * only returns the number of pages to process. It is
+ * advisable to issue a Document.newPage()
+ * before using this method.
+ * @return the total number of pages
+ * @param order an array with the new page sequence. It must have the
+ * same size as the number of pages.
+ * @throws DocumentException if all the pages are not present in the array
+ */
+ public int reorderPages(int order[]) throws DocumentException {
+ return root.reorderPages(order);
+ }
+
+ /** Gets the space/character extra spacing ratio for
+ * fully justified text.
+ * @return the space/character extra spacing ratio
+ */
+ public float getSpaceCharRatio() {
+ return spaceCharRatio;
+ }
+
+ /** Sets the ratio between the extra word spacing and the extra character spacing
+ * when the text is fully justified.
+ * Extra word spacing will grow spaceCharRatio
times more than extra character spacing.
+ * If the ratio is PdfWriter.NO_SPACE_CHAR_RATIO
then the extra character spacing
+ * will be zero.
+ * @param spaceCharRatio the ratio between the extra word spacing and the extra character spacing
+ */
+ public void setSpaceCharRatio(float spaceCharRatio) {
+ if (spaceCharRatio < 0.001f)
+ this.spaceCharRatio = 0.001f;
+ else
+ this.spaceCharRatio = spaceCharRatio;
+ }
+
+ /** Sets the run direction. This is only used as a placeholder
+ * as it does not affect anything.
+ * @param runDirection the run direction
+ */
+ public void setRunDirection(int runDirection) {
+ if (runDirection < RUN_DIRECTION_NO_BIDI || runDirection > RUN_DIRECTION_RTL)
+ throw new RuntimeException("Invalid run direction: " + runDirection);
+ this.runDirection = runDirection;
+ }
+
+ /** Gets the run direction.
+ * @return the run direction
+ */
+ public int getRunDirection() {
+ return runDirection;
+ }
+
+ /**
+ * Sets the display duration for the page (for presentations)
+ * @param seconds the number of seconds to display the page
+ */
+ public void setDuration(int seconds) {
+ pdf.setDuration(seconds);
+ }
+
+ /**
+ * Sets the transition for the page
+ * @param transition the Transition object
+ */
+ public void setTransition(PdfTransition transition) {
+ pdf.setTransition(transition);
+ }
+
+ /** Writes the reader to the document and frees the memory used by it.
+ * The main use is when concatenating multiple documents to keep the
+ * memory usage restricted to the current appending document.
+ * @param reader the PdfReader
to free
+ * @throws IOException on error
+ */
+ public void freeReader(PdfReader reader) throws IOException {
+ currentPdfReaderInstance = (PdfReaderInstance)importedPages.get(reader);
+ if (currentPdfReaderInstance == null)
+ return;
+ currentPdfReaderInstance.writeAllPages();
+ currentPdfReaderInstance = null;
+ importedPages.remove(reader);
+ }
+
+ /** Sets the open and close page additional action.
+ * @param actionType the action type. It can be PdfWriter.PAGE_OPEN
+ * or PdfWriter.PAGE_CLOSE
+ * @param action the action to perform
+ * @throws PdfException if the action type is invalid
+ */
+ public void setPageAction(PdfName actionType, PdfAction action) throws PdfException {
+ if (!actionType.equals(PAGE_OPEN) && !actionType.equals(PAGE_CLOSE))
+ throw new PdfException("Invalid page additional action type: " + actionType.toString());
+ pdf.setPageAction(actionType, action);
+ }
+
+ /** Gets the current document size. This size only includes
+ * the data already writen to the output stream, it does not
+ * include templates or fonts. It is usefull if used with
+ * freeReader()
when concatenating many documents
+ * and an idea of the current size is needed.
+ * @return the approximate size without fonts or templates
+ */
+ public int getCurrentDocumentSize() {
+ return body.offset() + body.size() * 20 + 0x48;
+ }
+
+ /** Getter for property strictImageSequence.
+ * @return value of property strictImageSequence
+ *
+ */
+ public boolean isStrictImageSequence() {
+ return pdf.isStrictImageSequence();
+ }
+
+ /** Sets the image sequence to follow the text in strict order.
+ * @param strictImageSequence new value of property strictImageSequence
+ *
+ */
+ public void setStrictImageSequence(boolean strictImageSequence) {
+ pdf.setStrictImageSequence(strictImageSequence);
+ }
+
+ /**
+ * If you use setPageEmpty(false), invoking newPage() after a blank page will add a newPage.
+ * @param pageEmpty the state
+ */
+ public void setPageEmpty(boolean pageEmpty) {
+ pdf.setPageEmpty(pageEmpty);
+ }
+
+ /** Gets the info dictionary for changing.
+ * @return the info dictionary
+ */
+ public PdfDictionary getInfo() {
+ return ((PdfDocument)document).getInfo();
+ }
+
+ /**
+ * Sets extra keys to the catalog.
+ * @return the catalog to change
+ */
+ public PdfDictionary getExtraCatalog() {
+ if (extraCatalog == null)
+ extraCatalog = new PdfDictionary();
+ return this.extraCatalog;
+ }
+
+ /**
+ * Sets the document in a suitable way to do page reordering.
+ */
+ public void setLinearPageMode() {
+ root.setLinearMode(null);
+ }
+
+ /** Getter for property group.
+ * @return Value of property group.
+ *
+ */
+ public PdfDictionary getGroup() {
+ return this.group;
+ }
+
+ /** Setter for property group.
+ * @param group New value of property group.
+ *
+ */
+ public void setGroup(PdfDictionary group) {
+ this.group = group;
+ }
+
+ /**
+ * Sets the PDFX conformance level. Allowed values are PDFX1A2001 and PDFX32002. It
+ * must be called before opening the document.
+ * @param pdfxConformance the conformance level
+ */
+ public void setPDFXConformance(int pdfxConformance) {
+ if (this.pdfxConformance == pdfxConformance)
+ return;
+ if (pdf.isOpen())
+ throw new PdfXConformanceException("PDFX conformance can only be set before opening the document.");
+ if (crypto != null)
+ throw new PdfXConformanceException("A PDFX conforming document cannot be encrypted.");
+ if (pdfxConformance != PDFXNONE)
+ setPdfVersion(VERSION_1_3);
+ this.pdfxConformance = pdfxConformance;
+ }
+
+ /**
+ * Gets the PDFX conformance level.
+ * @return the PDFX conformance level
+ */
+ public int getPDFXConformance() {
+ return pdfxConformance;
+ }
+
+ static void checkPDFXConformance(PdfWriter writer, int key, Object obj1) {
+ if (writer == null || writer.pdfxConformance == PDFXNONE)
+ return;
+ int conf = writer.pdfxConformance;
+ switch (key) {
+ case PDFXKEY_COLOR:
+ switch (conf) {
+ case PDFX1A2001:
+ if (obj1 instanceof ExtendedColor) {
+ ExtendedColor ec = (ExtendedColor)obj1;
+ switch (ec.getType()) {
+ case ExtendedColor.TYPE_CMYK:
+ case ExtendedColor.TYPE_GRAY:
+ return;
+ case ExtendedColor.TYPE_RGB:
+ throw new PdfXConformanceException("Colorspace RGB is not allowed.");
+ case ExtendedColor.TYPE_SEPARATION:
+ SpotColor sc = (SpotColor)ec;
+ checkPDFXConformance(writer, PDFXKEY_COLOR, sc.getPdfSpotColor().getAlternativeCS());
+ break;
+ case ExtendedColor.TYPE_SHADING:
+ ShadingColor xc = (ShadingColor)ec;
+ checkPDFXConformance(writer, PDFXKEY_COLOR, xc.getPdfShadingPattern().getShading().getColorSpace());
+ break;
+ case ExtendedColor.TYPE_PATTERN:
+ PatternColor pc = (PatternColor)ec;
+ checkPDFXConformance(writer, PDFXKEY_COLOR, pc.getPainter().getDefaultColor());
+ break;
+ }
+ }
+ else if (obj1 instanceof Color)
+ throw new PdfXConformanceException("Colorspace RGB is not allowed.");
+ break;
+ }
+ break;
+ case PDFXKEY_CMYK:
+ break;
+ case PDFXKEY_RGB:
+ if (conf == PDFX1A2001)
+ throw new PdfXConformanceException("Colorspace RGB is not allowed.");
+ break;
+ case PDFXKEY_FONT:
+ if (!((BaseFont)obj1).isEmbedded())
+ throw new PdfXConformanceException("All the fonts must be embedded.");
+ break;
+ case PDFXKEY_IMAGE:
+ PdfImage image = (PdfImage)obj1;
+ if (image.get(PdfName.SMASK) != null)
+ throw new PdfXConformanceException("The /SMask key is not allowed in images.");
+ switch (conf) {
+ case PDFX1A2001:
+ PdfObject cs = image.get(PdfName.COLORSPACE);
+ if (cs == null)
+ return;
+ if (cs.isName()) {
+ if (PdfName.DEVICERGB.equals(cs))
+ throw new PdfXConformanceException("Colorspace RGB is not allowed.");
+ }
+ else if (cs.isArray()) {
+ if (PdfName.CALRGB.equals(((PdfArray)cs).getArrayList().get(0)))
+ throw new PdfXConformanceException("Colorspace CalRGB is not allowed.");
+ }
+ break;
+ }
+ break;
+ case PDFXKEY_GSTATE:
+ PdfDictionary gs = (PdfDictionary)obj1;
+ PdfObject obj = gs.get(PdfName.BM);
+ if (obj != null && !PdfGState.BM_NORMAL.equals(obj) && !PdfGState.BM_COMPATIBLE.equals(obj))
+ throw new PdfXConformanceException("Blend mode " + obj.toString() + " not allowed.");
+ obj = gs.get(PdfName.CA);
+ double v = 0.0;
+ if (obj != null && (v = ((PdfNumber)obj).doubleValue()) != 1.0)
+ throw new PdfXConformanceException("Transparency is not allowed: /CA = " + v);
+ obj = gs.get(PdfName.ca);
+ v = 0.0;
+ if (obj != null && (v = ((PdfNumber)obj).doubleValue()) != 1.0)
+ throw new PdfXConformanceException("Transparency is not allowed: /ca = " + v);
+ break;
+ case PDFXKEY_LAYER:
+ throw new PdfXConformanceException("Layers are not allowed.");
+ }
+ }
+
+ /**
+ * Sets the values of the output intent dictionary. Null values are allowed to
+ * suppress any key.
+ * @param outputConditionIdentifier a value
+ * @param outputCondition a value
+ * @param registryName a value
+ * @param info a value
+ * @param destOutputProfile a value
+ * @throws IOException on error
+ */
+ public void setOutputIntents(String outputConditionIdentifier, String outputCondition, String registryName, String info, byte destOutputProfile[]) throws IOException {
+ getExtraCatalog();
+ PdfDictionary out = new PdfDictionary(PdfName.OUTPUTINTENT);
+ if (outputCondition != null)
+ out.put(PdfName.OUTPUTCONDITION, new PdfString(outputCondition, PdfObject.TEXT_UNICODE));
+ if (outputConditionIdentifier != null)
+ out.put(PdfName.OUTPUTCONDITIONIDENTIFIER, new PdfString(outputConditionIdentifier, PdfObject.TEXT_UNICODE));
+ if (registryName != null)
+ out.put(PdfName.REGISTRYNAME, new PdfString(registryName, PdfObject.TEXT_UNICODE));
+ if (info != null)
+ out.put(PdfName.INFO, new PdfString(registryName, PdfObject.TEXT_UNICODE));
+ if (destOutputProfile != null) {
+ PdfStream stream = new PdfStream(destOutputProfile);
+ stream.flateCompress();
+ out.put(PdfName.DESTOUTPUTPROFILE, addToBody(stream).getIndirectReference());
+ }
+ out.put(PdfName.S, PdfName.GTS_PDFX);
+ extraCatalog.put(PdfName.OUTPUTINTENTS, new PdfArray(out));
+ }
+
+ private static String getNameString(PdfDictionary dic, PdfName key) {
+ PdfObject obj = PdfReader.getPdfObject(dic.get(key));
+ if (obj == null || !obj.isString())
+ return null;
+ return ((PdfString)obj).toUnicodeString();
+ }
+
+ /**
+ * Copies the output intent dictionary from other document to this one.
+ * @param reader the other document
+ * @param checkExistence true
to just check for the existence of a valid output intent
+ * dictionary, false
to insert the dictionary if it exists
+ * @throws IOException on error
+ * @return true
if the output intent dictionary exists, false
+ * otherwise
+ */
+ public boolean setOutputIntents(PdfReader reader, boolean checkExistence) throws IOException {
+ PdfDictionary catalog = reader.getCatalog();
+ PdfArray outs = (PdfArray)PdfReader.getPdfObject(catalog.get(PdfName.OUTPUTINTENTS));
+ if (outs == null)
+ return false;
+ ArrayList arr = outs.getArrayList();
+ if (arr.size() == 0)
+ return false;
+ PdfDictionary out = (PdfDictionary)PdfReader.getPdfObject((PdfObject)arr.get(0));
+ PdfObject obj = PdfReader.getPdfObject(out.get(PdfName.S));
+ if (obj == null || !PdfName.GTS_PDFX.equals(obj))
+ return false;
+ if (checkExistence)
+ return true;
+ PRStream stream = (PRStream)PdfReader.getPdfObject(out.get(PdfName.DESTOUTPUTPROFILE));
+ byte destProfile[] = null;
+ if (stream != null) {
+ destProfile = PdfReader.getStreamBytes(stream);
+ }
+ setOutputIntents(getNameString(out, PdfName.OUTPUTCONDITIONIDENTIFIER), getNameString(out, PdfName.OUTPUTCONDITION),
+ getNameString(out, PdfName.REGISTRYNAME), getNameString(out, PdfName.INFO), destProfile);
+ return true;
+ }
+
+ /**
+ * Sets the page box sizes. Allowed names are: "crop", "trim", "art" and "bleed".
+ * @param boxName the box size
+ * @param size the size
+ */
+ public void setBoxSize(String boxName, Rectangle size) {
+ pdf.setBoxSize(boxName, size);
+ }
+
+ /**
+ * Gives the size of a trim, art, crop or bleed box, or null if not defined.
+ * @param boxName crop, trim, art or bleed
+ */
+ public Rectangle getBoxSize(String boxName) {
+ return pdf.getBoxSize(boxName);
+ }
+
+ /**
+ * Gives the size of the media box.
+ * @return a Rectangle
+ */
+ public Rectangle getPageSize() {
+ return pdf.getPageSize();
+ }
+ /**
+ * Gets the default colorspaces.
+ * @return the default colorspaces
+ */
+ public PdfDictionary getDefaultColorspace() {
+ return defaultColorspace;
+ }
+
+ /**
+ * Sets the default colorspace that will be applied to all the document.
+ * The colorspace is only applied if another colorspace with the same name
+ * is not present in the content.
+ * PdfName.DEFAULTGRAY
, PdfName.DEFAULTRGB
+ * or PdfName.DEFAULTCMYK
+ * @param cs the colorspace. A null
or PdfNull
removes any colorspace with the same name
+ */
+ public void setDefaultColorspace(PdfName key, PdfObject cs) {
+ if (cs == null || cs.isNull())
+ defaultColorspace.remove(key);
+ defaultColorspace.put(key, cs);
+ }
+
+ /**
+ * Gets the 1.5 compression status.
+ * @return true
if the 1.5 compression is on
+ */
+ public boolean isFullCompression() {
+ return this.fullCompression;
+ }
+
+ /**
+ * Sets the document's compression to the new 1.5 mode with object streams and xref
+ * streams. It can be set at any time but once set it can't be unset.
+ * true
if the document is marked for tagging
+ */
+ public boolean isTagged() {
+ return tagged;
+ }
+
+ /**
+ * Gets the structure tree root. If the document is not marked for tagging it will return null
.
+ * @return the structure tree root
+ */
+ public PdfStructureTreeRoot getStructureTreeRoot() {
+ if (tagged && structureTreeRoot == null)
+ structureTreeRoot = new PdfStructureTreeRoot(this);
+ return structureTreeRoot;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PdfXConformanceException.java b/src/main/java/com/lowagie/text/pdf/PdfXConformanceException.java
new file mode 100644
index 0000000..fd9607c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PdfXConformanceException.java
@@ -0,0 +1,70 @@
+/*
+ * $Id: PdfXConformanceException.java,v 1.2 2005/02/17 09:20:54 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2004 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999-2005 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000-2005 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ *
+ * @author psoares
+ */
+public class PdfXConformanceException extends RuntimeException {
+
+ /** Creates a new instance of PdfXConformanceException. */
+ public PdfXConformanceException() {
+ }
+
+ /**
+ * Creates a new instance of PdfXConformanceException.
+ * @param s
+ */
+ public PdfXConformanceException(String s) {
+ super(s);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/Pfm2afm.java b/src/main/java/com/lowagie/text/pdf/Pfm2afm.java
new file mode 100644
index 0000000..1428f53
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/Pfm2afm.java
@@ -0,0 +1,773 @@
+/*
+ * Copyright 2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+/********************************************************************
+ * *
+ * Title: pfm2afm - Convert Windows .pfm files to .afm files *
+ * *
+ * Author: Ken Borgendale 10/9/91 Version 1.0 *
+ * *
+ * Function: *
+ * Convert a Windows .pfm (Printer Font Metrics) file to a *
+ * .afm (Adobe Font Metrics) file. The purpose of this is *
+ * to allow fonts put out for Windows to be used with OS/2. *
+ * *
+ * Syntax: *
+ * pfm2afm infile [outfile] -a *
+ * *
+ * Copyright: *
+ * pfm2afm - Copyright (C) IBM Corp., 1991 *
+ * *
+ * This code is released for public use as long as the *
+ * copyright remains intact. This code is provided asis *
+ * without any warrenties, express or implied. *
+ * *
+ * Notes: *
+ * 1. Much of the information in the original .afm file is *
+ * lost when the .pfm file is created, and thus cannot be *
+ * reconstructed by this utility. This is especially true *
+ * of data for characters not in the Windows character set. *
+ * *
+ * 2. This module is coded to be compiled by the MSC 6.0. *
+ * For other compilers, be careful of the packing of the *
+ * PFM structure. *
+ * *
+ ********************************************************************/
+
+/********************************************************************
+ * *
+ * Modifications by Rod Smith, 5/22/96 *
+ * *
+ * These changes look for the strings "italic", "bold", "black", *
+ * and "light" in the font's name and set the weight accordingly *
+ * and adds an ItalicAngle line with a value of "0" or "-12.00". *
+ * This allows OS/2 programs such as DeScribe to handle the bold *
+ * and italic attributes appropriately, which was not the case *
+ * when I used the original version on fonts from the KeyFonts *
+ * Pro 2002 font CD. *
+ * *
+ * I've also increased the size of the buffer used to load the *
+ * .PFM file; the old size was inadequate for most of the fonts *
+ * from the SoftKey collection. *
+ * *
+ * Compiled with Watcom C 10.6 *
+ * *
+ ********************************************************************/
+
+/********************************************************************
+ * *
+ * Further modifications, 4/21/98, by Rod Smith *
+ * *
+ * Minor changes to get the program to compile with gcc under *
+ * Linux (Red Hat 5.0, to be precise). I had to add an itoa *
+ * function from the net (the function was buggy, so I had to fix *
+ * it, too!). I also made the program more friendly towards *
+ * files with mixed-case filenames. *
+ * *
+ ********************************************************************/
+package com.lowagie.text.pdf;
+
+import java.io.*;
+
+/**
+ * Converts a PFM file into an AFM file.
+ */
+public class Pfm2afm {
+ private RandomAccessFileOrArray in;
+ private PrintWriter out;
+
+ /** Creates a new instance of Pfm2afm */
+ private Pfm2afm(RandomAccessFileOrArray in, OutputStream out) throws IOException {
+ this.in = in;
+ this.out = new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1"));
+ }
+
+ /**
+ * Converts a PFM file into an AFM file.
+ * @param in the PFM file
+ * @param out the AFM file
+ * @throws IOException on error
+ */
+ public static void convert(RandomAccessFileOrArray in, OutputStream out) throws IOException {
+ Pfm2afm p = new Pfm2afm(in, out);
+ p.openpfm();
+ p.putheader();
+ p.putchartab();
+ p.putkerntab();
+ p.puttrailer();
+ p.out.flush();
+ }
+
+ public static void main(String[] args) {
+ try {
+ RandomAccessFileOrArray in = new RandomAccessFileOrArray(args[0]);
+ OutputStream out = new FileOutputStream(args[1]);
+ convert(in, out);
+ in.close();
+ out.close();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private String readString(int n) throws IOException {
+ byte b[] = new byte[n];
+ in.readFully(b);
+ int k;
+ for (k = 0; k < b.length; ++k) {
+ if (b[k] == 0)
+ break;
+ }
+ return new String(b, 0, k, "ISO-8859-1");
+ }
+
+ private String readString() throws IOException {
+ StringBuffer buf = new StringBuffer();
+ while (true) {
+ int c = in.read();
+ if (c <= 0)
+ break;
+ buf.append((char)c);
+ }
+ return buf.toString();
+ }
+
+ private void outval(int n) {
+ out.print(' ');
+ out.print(n);
+ }
+
+ /*
+ * Output a character entry
+ */
+ private void outchar(int code, int width, String name) {
+ out.print("C ");
+ outval(code);
+ out.print(" ; WX ");
+ outval(width);
+ if (name != null) {
+ out.print(" ; N ");
+ out.print(name);
+ }
+ out.print(" ;\n");
+ }
+
+ private void openpfm() throws IOException {
+ in.seek(0);
+ vers = in.readShortLE();
+ h_len = in.readIntLE();
+ copyright = readString(60);
+ type = in.readShortLE();
+ points = in.readShortLE();
+ verres = in.readShortLE();
+ horres = in.readShortLE();
+ ascent = in.readShortLE();
+ intleading = in.readShortLE();
+ extleading = in.readShortLE();
+ italic = (byte)in.read();
+ uline = (byte)in.read();
+ overs = (byte)in.read();
+ weight = in.readShortLE();
+ charset = (byte)in.read();
+ pixwidth = in.readShortLE();
+ pixheight = in.readShortLE();
+ kind = (byte)in.read();
+ avgwidth = in.readShortLE();
+ maxwidth = in.readShortLE();
+ firstchar = in.read();
+ lastchar = in.read();
+ defchar = (byte)in.read();
+ brkchar = (byte)in.read();
+ widthby = in.readShortLE();
+ device = in.readIntLE();
+ face = in.readIntLE();
+ bits = in.readIntLE();
+ bitoff = in.readIntLE();
+ extlen = in.readShortLE();
+ psext = in.readIntLE();
+ chartab = in.readIntLE();
+ res1 = in.readIntLE();
+ kernpairs = in.readIntLE();
+ res2 = in.readIntLE();
+ fontname = in.readIntLE();
+ if (h_len != in.length() || extlen != 30 || fontname < 75 || fontname > 512)
+ throw new IOException("Not a valid PFM file.");
+ in.seek(psext + 14);
+ capheight = in.readShortLE();
+ xheight = in.readShortLE();
+ ascender = in.readShortLE();
+ descender = in.readShortLE();
+ }
+
+ private void putheader() throws IOException {
+ out.print("StartFontMetrics 2.0\n");
+ if (copyright.length() > 0)
+ out.print("Comment " + copyright + '\n');
+ out.print("FontName ");
+ in.seek(fontname);
+ String fname = readString();
+ out.print(fname);
+ out.print("\nEncodingScheme ");
+ if (charset != 0)
+ out.print("FontSpecific\n");
+ else
+ out.print("AdobeStandardEncoding\n");
+ /*
+ * The .pfm is missing full name, so construct from font name by
+ * changing the hyphen to a space. This actually works in a lot
+ * of cases.
+ */
+ out.print("FullName " + fname.replace('-', ' '));
+ if (face != 0) {
+ in.seek(face);
+ out.print("\nFamilyName " + readString());
+ }
+
+ out.print("\nWeight ");
+ if (weight > 475 || fname.toLowerCase().indexOf("bold") >= 0)
+ out.print("Bold");
+ else if ((weight < 325 && weight != 0) || fname.toLowerCase().indexOf("light") >= 0)
+ out.print("Light");
+ else if (fname.toLowerCase().indexOf("black") >= 0)
+ out.print("Black");
+ else
+ out.print("Medium");
+
+ out.print("\nItalicAngle ");
+ if (italic != 0 || fname.toLowerCase().indexOf("italic") >= 0)
+ out.print("-12.00");
+ /* this is a typical value; something else may work better for a
+ specific font */
+ else
+ out.print("0");
+
+ /*
+ * The mono flag in the pfm actually indicates whether there is a
+ * table of font widths, not if they are all the same.
+ */
+ out.print("\nIsFixedPitch ");
+ if ((kind & 1) == 0 || /* Flag for mono */
+ avgwidth == maxwidth ) { /* Avg width = max width */
+ out.print("true");
+ isMono = true;
+ }
+ else {
+ out.print("false");
+ isMono = false;
+ }
+
+ /*
+ * The font bounding box is lost, but try to reconstruct it.
+ * Much of this is just guess work. The bounding box is required in
+ * the .afm, but is not used by the PM font installer.
+ */
+ out.print("\nFontBBox");
+ if (isMono)
+ outval(-20); /* Just guess at left bounds */
+ else
+ outval(-100);
+ outval(-(descender+5)); /* Descender is given as positive value */
+ outval(maxwidth+10);
+ outval(ascent+5);
+
+ /*
+ * Give other metrics that were kept
+ */
+ out.print("\nCapHeight");
+ outval(capheight);
+ out.print("\nXHeight");
+ outval(xheight);
+ out.print("\nDescender");
+ outval(descender);
+ out.print("\nAscender");
+ outval(ascender);
+ out.print('\n');
+ }
+
+ private void putchartab() throws IOException {
+ int count = lastchar - firstchar + 1;
+ int ctabs[] = new int[count];
+ in.seek(chartab);
+ for (int k = 0; k < count; ++k)
+ ctabs[k] = in.readUnsignedShortLE();
+ int back[] = new int[256];
+ if (charset == 0) {
+ for (int i = firstchar; i <= lastchar; ++i) {
+ if (Win2PSStd[i] != 0)
+ back[Win2PSStd[i]] = i;
+ }
+ }
+ /* Put out the header */
+ out.print("StartCharMetrics");
+ outval(count);
+ out.print('\n');
+
+ /* Put out all encoded chars */
+ if (charset != 0) {
+ /*
+ * If the charset is not the Windows standard, just put out
+ * unnamed entries.
+ */
+ for (int i = firstchar; i <= lastchar; i++) {
+ if (ctabs[i - firstchar] != 0) {
+ outchar(i, ctabs[i - firstchar], null);
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < 256; i++) {
+ int j = back[i];
+ if (j != 0) {
+ outchar(i, ctabs[j - firstchar], WinChars[j]);
+ ctabs[j - firstchar] = 0;
+ }
+ }
+ /* Put out all non-encoded chars */
+ for (int i = firstchar; i <= lastchar; i++) {
+ if (ctabs[i - firstchar] != 0) {
+ outchar(-1, ctabs[i - firstchar], WinChars[i]);
+ }
+ }
+ }
+ /* Put out the trailer */
+ out.print("EndCharMetrics\n");
+
+ }
+
+ private void putkerntab() throws IOException {
+ if (kernpairs == 0)
+ return;
+ in.seek(kernpairs);
+ int count = in.readUnsignedShortLE();
+ int nzero = 0;
+ int kerns[] = new int[count * 3];
+ for (int k = 0; k < kerns.length;) {
+ kerns[k++] = in.read();
+ kerns[k++] = in.read();
+ if ((kerns[k++] = in.readShortLE()) != 0)
+ ++nzero;
+ }
+ if (nzero == 0)
+ return;
+ out.print("StartKernData\nStartKernPairs");
+ outval(nzero);
+ out.print('\n');
+ for (int k = 0; k < kerns.length; k += 3) {
+ if (kerns[k + 2] != 0) {
+ out.print("KPX ");
+ out.print(WinChars[kerns[k]]);
+ out.print(' ');
+ out.print(WinChars[kerns[k + 1]]);
+ outval(kerns[k + 2]);
+ out.print('\n');
+ }
+ }
+ /* Put out trailer */
+ out.print("EndKernPairs\nEndKernData\n");
+ }
+
+
+ private void puttrailer() {
+ out.print("EndFontMetrics\n");
+ }
+
+ private short vers;
+ private int h_len; /* Total length of .pfm file */
+ private String copyright; /* Copyright string [60]*/
+ private short type;
+ private short points;
+ private short verres;
+ private short horres;
+ private short ascent;
+ private short intleading;
+ private short extleading;
+ private byte italic;
+ private byte uline;
+ private byte overs;
+ private short weight;
+ private byte charset; /* 0=windows, otherwise nomap */
+ private short pixwidth; /* Width for mono fonts */
+ private short pixheight;
+ private byte kind; /* Lower bit off in mono */
+ private short avgwidth; /* Mono if avg=max width */
+ private short maxwidth; /* Use to compute bounding box */
+ private int firstchar; /* First char in table */
+ private int lastchar; /* Last char in table */
+ private byte defchar;
+ private byte brkchar;
+ private short widthby;
+ private int device;
+ private int face; /* Face name */
+ private int bits;
+ private int bitoff;
+ private short extlen;
+ private int psext; /* PostScript extension */
+ private int chartab; /* Character width tables */
+ private int res1;
+ private int kernpairs; /* Kerning pairs */
+ private int res2;
+ private int fontname; /* Font name */
+
+/*
+ * Some metrics from the PostScript extension
+ */
+ private short capheight; /* Cap height */
+ private short xheight; /* X height */
+ private short ascender; /* Ascender */
+ private short descender; /* Descender (positive) */
+
+
+ private boolean isMono;
+/*
+ * Translate table from 1004 to psstd. 1004 is an extension of the
+ * Windows translate table used in PM.
+ */
+ private int Win2PSStd[] = {
+ 0, 0, 0, 0, 197, 198, 199, 0, 202, 0, 205, 206, 207, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 33, 34, 35, 36, 37, 38, 169, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 193, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+ 0, 0, 184, 0, 185, 188, 178, 179, 94, 189, 0, 172, 234, 0, 0, 0,
+ 0, 96, 0, 170, 186, 0, 177, 208, 126, 0, 0, 173, 250, 0, 0, 0,
+ 0, 161, 162, 163, 168, 165, 0, 167, 200, 0, 227, 171, 0, 0, 0, 0,
+ 0, 0, 0, 0, 194, 0, 182, 180, 203, 0, 235, 187, 0, 0, 0, 191,
+ 0, 0, 0, 0, 0, 0, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 233, 0, 0, 0, 0, 0, 0, 251,
+ 0, 0, 0, 0, 0, 0, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 249, 0, 0, 0, 0, 0, 0, 0
+ };
+
+/*
+ * Character class. This is a minor attempt to overcome the problem that
+ * in the pfm file, all unused characters are given the width of space.
+ */
+ private int WinClass[] = {
+ 0, 0, 0, 0, 2, 2, 2, 0, 2, 0, 2, 2, 2, 0, 0, 0, /* 00 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* 70 */
+ 0, 0, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, /* 80 */
+ 0, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, /* 90 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a0 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b0 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c0 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* d0 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e0 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* f0 */
+ };
+
+/*
+ * Windows chararacter names. Give a name to the usused locations
+ * for when the all flag is specified.
+ */
+ private String WinChars[] = {
+ "W00", /* 00 */
+ "W01", /* 01 */
+ "W02", /* 02 */
+ "W03", /* 03 */
+ "macron", /* 04 */
+ "breve", /* 05 */
+ "dotaccent", /* 06 */
+ "W07", /* 07 */
+ "ring", /* 08 */
+ "W09", /* 09 */
+ "W0a", /* 0a */
+ "W0b", /* 0b */
+ "W0c", /* 0c */
+ "W0d", /* 0d */
+ "W0e", /* 0e */
+ "W0f", /* 0f */
+ "hungarumlaut", /* 10 */
+ "ogonek", /* 11 */
+ "caron", /* 12 */
+ "W13", /* 13 */
+ "W14", /* 14 */
+ "W15", /* 15 */
+ "W16", /* 16 */
+ "W17", /* 17 */
+ "W18", /* 18 */
+ "W19", /* 19 */
+ "W1a", /* 1a */
+ "W1b", /* 1b */
+ "W1c", /* 1c */
+ "W1d", /* 1d */
+ "W1e", /* 1e */
+ "W1f", /* 1f */
+ "space", /* 20 */
+ "exclam", /* 21 */
+ "quotedbl", /* 22 */
+ "numbersign", /* 23 */
+ "dollar", /* 24 */
+ "percent", /* 25 */
+ "ampersand", /* 26 */
+ "quotesingle", /* 27 */
+ "parenleft", /* 28 */
+ "parenright", /* 29 */
+ "asterisk", /* 2A */
+ "plus", /* 2B */
+ "comma", /* 2C */
+ "hyphen", /* 2D */
+ "period", /* 2E */
+ "slash", /* 2F */
+ "zero", /* 30 */
+ "one", /* 31 */
+ "two", /* 32 */
+ "three", /* 33 */
+ "four", /* 34 */
+ "five", /* 35 */
+ "six", /* 36 */
+ "seven", /* 37 */
+ "eight", /* 38 */
+ "nine", /* 39 */
+ "colon", /* 3A */
+ "semicolon", /* 3B */
+ "less", /* 3C */
+ "equal", /* 3D */
+ "greater", /* 3E */
+ "question", /* 3F */
+ "at", /* 40 */
+ "A", /* 41 */
+ "B", /* 42 */
+ "C", /* 43 */
+ "D", /* 44 */
+ "E", /* 45 */
+ "F", /* 46 */
+ "G", /* 47 */
+ "H", /* 48 */
+ "I", /* 49 */
+ "J", /* 4A */
+ "K", /* 4B */
+ "L", /* 4C */
+ "M", /* 4D */
+ "N", /* 4E */
+ "O", /* 4F */
+ "P", /* 50 */
+ "Q", /* 51 */
+ "R", /* 52 */
+ "S", /* 53 */
+ "T", /* 54 */
+ "U", /* 55 */
+ "V", /* 56 */
+ "W", /* 57 */
+ "X", /* 58 */
+ "Y", /* 59 */
+ "Z", /* 5A */
+ "bracketleft", /* 5B */
+ "backslash", /* 5C */
+ "bracketright", /* 5D */
+ "asciicircum", /* 5E */
+ "underscore", /* 5F */
+ "grave", /* 60 */
+ "a", /* 61 */
+ "b", /* 62 */
+ "c", /* 63 */
+ "d", /* 64 */
+ "e", /* 65 */
+ "f", /* 66 */
+ "g", /* 67 */
+ "h", /* 68 */
+ "i", /* 69 */
+ "j", /* 6A */
+ "k", /* 6B */
+ "l", /* 6C */
+ "m", /* 6D */
+ "n", /* 6E */
+ "o", /* 6F */
+ "p", /* 70 */
+ "q", /* 71 */
+ "r", /* 72 */
+ "s", /* 73 */
+ "t", /* 74 */
+ "u", /* 75 */
+ "v", /* 76 */
+ "w", /* 77 */
+ "x", /* 78 */
+ "y", /* 79 */
+ "z", /* 7A */
+ "braceleft", /* 7B */
+ "bar", /* 7C */
+ "braceright", /* 7D */
+ "asciitilde", /* 7E */
+ "W7f", /* 7F */
+ "W80", /* 80 */
+ "W81", /* 81 */
+ "quotesinglbase", /* 82 */
+ "W83", /* 83 */
+ "quotedblbase", /* 84 */
+ "ellipsis", /* 85 */
+ "dagger", /* 86 */
+ "daggerdbl", /* 87 */
+ "asciicircum", /* 88 */
+ "perthousand", /* 89 */
+ "Scaron", /* 8A */
+ "guilsinglleft", /* 8B */
+ "OE", /* 8C */
+ "W8d", /* 8D */
+ "W8e", /* 8E */
+ "W8f", /* 8F */
+ "W90", /* 90 */
+ "quoteleft", /* 91 */
+ "quoteright", /* 92 */
+ "quotedblleft", /* 93 */
+ "quotedblright", /* 94 */
+ "bullet1", /* 95 */
+ "endash", /* 96 */
+ "emdash", /* 97 */
+ "asciitilde", /* 98 */
+ "trademark", /* 99 */
+ "scaron", /* 9A */
+ "guilsinglright", /* 9B */
+ "oe", /* 9C */
+ "W9d", /* 9D */
+ "W9e", /* 9E */
+ "Ydieresis", /* 9F */
+ "reqspace", /* A0 */
+ "exclamdown", /* A1 */
+ "cent", /* A2 */
+ "sterling", /* A3 */
+ "currency", /* A4 */
+ "yen", /* A5 */
+ "brokenbar", /* A6 */
+ "section", /* A7 */
+ "dieresis", /* A8 */
+ "copyright", /* A9 */
+ "ordfeminine", /* AA */
+ "guillemotleft", /* AB */
+ "logicalnot", /* AC */
+ "syllable", /* AD */
+ "registered", /* AE */
+ "overbar", /* AF */
+ "degree", /* B0 */
+ "plusminus", /* B1 */
+ "twosuperior", /* B2 */
+ "threesuperior", /* B3 */
+ "acute", /* B4 */
+ "mu", /* B5 */
+ "paragraph", /* B6 */
+ "periodcentered", /* B7 */
+ "cedilla", /* B8 */
+ "onesuperior", /* B9 */
+ "ordmasculine", /* BA */
+ "guillemotright", /* BB */
+ "onequarter", /* BC */
+ "onehalf", /* BD */
+ "threequarters", /* BE */
+ "questiondown", /* BF */
+ "Agrave", /* C0 */
+ "Aacute", /* C1 */
+ "Acircumflex", /* C2 */
+ "Atilde", /* C3 */
+ "Adieresis", /* C4 */
+ "Aring", /* C5 */
+ "AE", /* C6 */
+ "Ccedilla", /* C7 */
+ "Egrave", /* C8 */
+ "Eacute", /* C9 */
+ "Ecircumflex", /* CA */
+ "Edieresis", /* CB */
+ "Igrave", /* CC */
+ "Iacute", /* CD */
+ "Icircumflex", /* CE */
+ "Idieresis", /* CF */
+ "Eth", /* D0 */
+ "Ntilde", /* D1 */
+ "Ograve", /* D2 */
+ "Oacute", /* D3 */
+ "Ocircumflex", /* D4 */
+ "Otilde", /* D5 */
+ "Odieresis", /* D6 */
+ "multiply", /* D7 */
+ "Oslash", /* D8 */
+ "Ugrave", /* D9 */
+ "Uacute", /* DA */
+ "Ucircumflex", /* DB */
+ "Udieresis", /* DC */
+ "Yacute", /* DD */
+ "Thorn", /* DE */
+ "germandbls", /* DF */
+ "agrave", /* E0 */
+ "aacute", /* E1 */
+ "acircumflex", /* E2 */
+ "atilde", /* E3 */
+ "adieresis", /* E4 */
+ "aring", /* E5 */
+ "ae", /* E6 */
+ "ccedilla", /* E7 */
+ "egrave", /* E8 */
+ "eacute", /* E9 */
+ "ecircumflex", /* EA */
+ "edieresis", /* EB */
+ "igrave", /* EC */
+ "iacute", /* ED */
+ "icircumflex", /* EE */
+ "idieresis", /* EF */
+ "eth", /* F0 */
+ "ntilde", /* F1 */
+ "ograve", /* F2 */
+ "oacute", /* F3 */
+ "ocircumflex", /* F4 */
+ "otilde", /* F5 */
+ "odieresis", /* F6 */
+ "divide", /* F7 */
+ "oslash", /* F8 */
+ "ugrave", /* F9 */
+ "uacute", /* FA */
+ "ucircumflex", /* FB */
+ "udieresis", /* FC */
+ "yacute", /* FD */
+ "thorn", /* FE */
+ "ydieresis" /* FF */
+ };
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/PushbuttonField.java b/src/main/java/com/lowagie/text/pdf/PushbuttonField.java
new file mode 100644
index 0000000..ff1769c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/PushbuttonField.java
@@ -0,0 +1,601 @@
+/*
+ * Copyright 2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Image;
+import com.lowagie.text.DocumentException;
+import java.io.IOException;
+/**
+ * Creates a pushbutton field. It supports all the text and icon alignments.
+ * The icon may be an image or a template.
+ *
+ * Document document = new Document(PageSize.A4, 50, 50, 50, 50);
+ * PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("output.pdf"));
+ * document.open();
+ * PdfContentByte cb = writer.getDirectContent();
+ * Image img = Image.getInstance("image.png");
+ * PushbuttonField bt = new PushbuttonField(writer, new Rectangle(100, 100, 200, 200), "Button1");
+ * bt.setText("My Caption");
+ * bt.setFontSize(0);
+ * bt.setImage(img);
+ * bt.setLayout(PushbuttonField.LAYOUT_ICON_TOP_LABEL_BOTTOM);
+ * bt.setBackgroundColor(Color.cyan);
+ * bt.setBorderStyle(PdfBorderDictionary.STYLE_SOLID);
+ * bt.setBorderColor(Color.red);
+ * bt.setBorderWidth(3);
+ * PdfFormField ff = bt.getField();
+ * PdfAction ac = PdfAction.createSubmitForm("http://www.submit-site.com", null, 0);
+ * ff.setAction(ac);
+ * writer.addAnnotation(ff);
+ * document.close();
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class PushbuttonField extends BaseField {
+
+ /** A layout option */
+ public static final int LAYOUT_LABEL_ONLY = 1;
+ /** A layout option */
+ public static final int LAYOUT_ICON_ONLY = 2;
+ /** A layout option */
+ public static final int LAYOUT_ICON_TOP_LABEL_BOTTOM = 3;
+ /** A layout option */
+ public static final int LAYOUT_LABEL_TOP_ICON_BOTTOM = 4;
+ /** A layout option */
+ public static final int LAYOUT_ICON_LEFT_LABEL_RIGHT = 5;
+ /** A layout option */
+ public static final int LAYOUT_LABEL_LEFT_ICON_RIGHT = 6;
+ /** A layout option */
+ public static final int LAYOUT_LABEL_OVER_ICON = 7;
+ /** An icon scaling option */
+ public static final int SCALE_ICON_ALWAYS = 1;
+ /** An icon scaling option */
+ public static final int SCALE_ICON_NEVER = 2;
+ /** An icon scaling option */
+ public static final int SCALE_ICON_IS_TOO_BIG = 3;
+ /** An icon scaling option */
+ public static final int SCALE_ICON_IS_TOO_SMALL = 4;
+
+ /**
+ * Holds value of property layout.
+ */
+ private int layout = LAYOUT_LABEL_ONLY;
+
+ /**
+ * Holds value of property image.
+ */
+ private Image image;
+
+ /**
+ * Holds value of property template.
+ */
+ private PdfTemplate template;
+
+ /**
+ * Holds value of property scaleIcon.
+ */
+ private int scaleIcon = SCALE_ICON_ALWAYS;
+
+ /**
+ * Holds value of property proportionalIcon.
+ */
+ private boolean proportionalIcon = true;
+
+ /**
+ * Holds value of property iconVerticalAdjustment.
+ */
+ private float iconVerticalAdjustment = 0.5f;
+
+ /**
+ * Holds value of property iconHorizontalAdjustment.
+ */
+ private float iconHorizontalAdjustment = 0.5f;
+
+ /**
+ * Holds value of property iconFitToBounds.
+ */
+ private boolean iconFitToBounds;
+
+ private PdfTemplate tp;
+
+ /**
+ * Creates a new instance of PushbuttonField
+ * @param writer the document PdfWriter
+ * @param box the field location and dimensions
+ * @param fieldName the field name. If null
only the widget keys
+ * will be included in the field allowing it to be used as a kid field.
+ */
+ public PushbuttonField(PdfWriter writer, Rectangle box, String fieldName) {
+ super(writer, box, fieldName);
+ }
+
+ /**
+ * Getter for property layout.
+ * @return Value of property layout.
+ */
+ public int getLayout() {
+ return this.layout;
+ }
+
+ /**
+ * Sets the icon and label layout. Possible values are LAYOUT_LABEL_ONLY
,
+ * LAYOUT_ICON_ONLY
, LAYOUT_ICON_TOP_LABEL_BOTTOM
,
+ * LAYOUT_LABEL_TOP_ICON_BOTTOM
, LAYOUT_ICON_LEFT_LABEL_RIGHT
,
+ * LAYOUT_LABEL_LEFT_ICON_RIGHT
and LAYOUT_LABEL_OVER_ICON
.
+ * The default is LAYOUT_LABEL_ONLY
.
+ * @param layout New value of property layout.
+ */
+ public void setLayout(int layout) {
+ if (layout < LAYOUT_LABEL_ONLY || layout > LAYOUT_LABEL_OVER_ICON)
+ throw new IllegalArgumentException("Layout out of bounds.");
+ this.layout = layout;
+ }
+
+ /**
+ * Getter for property image.
+ * @return Value of property image.
+ */
+ public Image getImage() {
+ return this.image;
+ }
+
+ /**
+ * Sets the icon as an image.
+ * @param image the image
+ */
+ public void setImage(Image image) {
+ this.image = image;
+ template = null;
+ }
+
+ /**
+ * Getter for property template.
+ * @return Value of property template.
+ */
+ public PdfTemplate getTemplate() {
+ return this.template;
+ }
+
+ /**
+ * Sets the icon as a template.
+ * @param template the template
+ */
+ public void setTemplate(PdfTemplate template) {
+ this.template = template;
+ image = null;
+ }
+
+ /**
+ * Getter for property scaleIcon.
+ * @return Value of property scaleIcon.
+ */
+ public int getScaleIcon() {
+ return this.scaleIcon;
+ }
+
+ /**
+ * Sets the way the icon will be scaled. Possible values are
+ * SCALE_ICON_ALWAYS
, SCALE_ICON_NEVER
,
+ * SCALE_ICON_IS_TOO_BIG
and SCALE_ICON_IS_TOO_SMALL
.
+ * The default is SCALE_ICON_ALWAYS
.
+ * @param scaleIcon the way the icon will be scaled
+ */
+ public void setScaleIcon(int scaleIcon) {
+ if (scaleIcon < SCALE_ICON_ALWAYS || scaleIcon > SCALE_ICON_IS_TOO_SMALL)
+ scaleIcon = SCALE_ICON_ALWAYS;
+ this.scaleIcon = scaleIcon;
+ }
+
+ /**
+ * Getter for property proportionalIcon.
+ * @return Value of property proportionalIcon.
+ */
+ public boolean isProportionalIcon() {
+ return this.proportionalIcon;
+ }
+
+ /**
+ * Sets the way the icon is scaled. If true
the icon is scaled proportionally,
+ * if false
the scaling is done anamorphicaly.
+ * @param proportionalIcon the way the icon is scaled
+ */
+ public void setProportionalIcon(boolean proportionalIcon) {
+ this.proportionalIcon = proportionalIcon;
+ }
+
+ /**
+ * Getter for property iconVerticalAdjustment.
+ * @return Value of property iconVerticalAdjustment.
+ */
+ public float getIconVerticalAdjustment() {
+ return this.iconVerticalAdjustment;
+ }
+
+ /**
+ * A number between 0 and 1 indicating the fraction of leftover space to allocate at the bottom of the icon.
+ * A value of 0 positions the icon at the bottom of the annotation rectangle.
+ * A value of 0.5 centers it within the rectangle. The default is 0.5.
+ * @param iconVerticalAdjustment a number between 0 and 1 indicating the fraction of leftover space to allocate at the bottom of the icon
+ */
+ public void setIconVerticalAdjustment(float iconVerticalAdjustment) {
+ if (iconVerticalAdjustment < 0)
+ iconVerticalAdjustment = 0;
+ else if (iconVerticalAdjustment > 1)
+ iconVerticalAdjustment = 1;
+ this.iconVerticalAdjustment = iconVerticalAdjustment;
+ }
+
+ /**
+ * Getter for property iconHorizontalAdjustment.
+ * @return Value of property iconHorizontalAdjustment.
+ */
+ public float getIconHorizontalAdjustment() {
+ return this.iconHorizontalAdjustment;
+ }
+
+ /**
+ * A number between 0 and 1 indicating the fraction of leftover space to allocate at the left of the icon.
+ * A value of 0 positions the icon at the left of the annotation rectangle.
+ * A value of 0.5 centers it within the rectangle. The default is 0.5.
+ * @param iconHorizontalAdjustment a number between 0 and 1 indicating the fraction of leftover space to allocate at the left of the icon
+ */
+ public void setIconHorizontalAdjustment(float iconHorizontalAdjustment) {
+ if (iconHorizontalAdjustment < 0)
+ iconHorizontalAdjustment = 0;
+ else if (iconHorizontalAdjustment > 1)
+ iconHorizontalAdjustment = 1;
+ this.iconHorizontalAdjustment = iconHorizontalAdjustment;
+ }
+
+ private float calculateFontSize(float w, float h) throws IOException, DocumentException {
+ BaseFont ufont = getRealFont();
+ float fsize = fontSize;
+ if (fsize == 0) {
+ float bw = ufont.getWidthPoint(text, 1);
+ if (bw == 0)
+ fsize = 12;
+ else
+ fsize = w / bw;
+ float nfsize = h / (1 - ufont.getFontDescriptor(BaseFont.DESCENT, 1));
+ fsize = Math.min(fsize, nfsize);
+ if (fsize < 4)
+ fsize = 4;
+ }
+ return fsize;
+ }
+
+ /**
+ * Gets the button appearance.
+ * @throws IOException on error
+ * @throws DocumentException on error
+ * @return the button appearance
+ */
+ public PdfAppearance getAppearance() throws IOException, DocumentException {
+ PdfAppearance app = getBorderAppearance();
+ Rectangle box = new Rectangle(app.getBoundingBox());
+ if ((text == null || text.length() == 0) && (layout == LAYOUT_LABEL_ONLY || (image == null && template == null))) {
+ return app;
+ }
+ if (layout == LAYOUT_ICON_ONLY && image == null && template == null)
+ return app;
+ BaseFont ufont = getRealFont();
+ boolean borderExtra = borderStyle == PdfBorderDictionary.STYLE_BEVELED || borderStyle == PdfBorderDictionary.STYLE_INSET;
+ float h = box.height() - borderWidth * 2;
+ float bw2 = borderWidth;
+ if (borderExtra) {
+ h -= borderWidth * 2;
+ bw2 *= 2;
+ }
+ float offsetX = (borderExtra ? 2 * borderWidth : borderWidth);
+ offsetX = Math.max(offsetX, 1);
+ float offX = Math.min(bw2, offsetX);
+ tp = null;
+ float textX = Float.NaN;
+ float textY = 0;
+ float fsize = fontSize;
+ float wt = box.width() - 2 * offX - 2;
+ float ht = box.height() - 2 * offX;
+ float adj = (iconFitToBounds ? 0 : offX + 1);
+ int nlayout = layout;
+ if (image == null && template == null)
+ nlayout = LAYOUT_LABEL_ONLY;
+ Rectangle iconBox = null;
+ while (true) {
+ switch (nlayout) {
+ case LAYOUT_LABEL_ONLY:
+ case LAYOUT_LABEL_OVER_ICON:
+ if (text != null && text.length() > 0 && wt > 0 && ht > 0) {
+ fsize = calculateFontSize(wt, ht);
+ textX = (box.width() - ufont.getWidthPoint(text, fsize)) / 2;
+ textY = (box.height() - ufont.getFontDescriptor(BaseFont.ASCENT, fsize)) / 2;
+ }
+ case LAYOUT_ICON_ONLY:
+ if (nlayout == LAYOUT_LABEL_OVER_ICON || nlayout == LAYOUT_ICON_ONLY)
+ iconBox = new Rectangle(box.left() + adj, box.bottom() + adj, box.right() - adj, box.top() - adj);
+ break;
+ case LAYOUT_ICON_TOP_LABEL_BOTTOM:
+ if (text == null || text.length() == 0 || wt <= 0 || ht <= 0) {
+ nlayout = LAYOUT_ICON_ONLY;
+ continue;
+ }
+ float nht = box.height() * 0.35f - offX;
+ if (nht > 0)
+ fsize = calculateFontSize(wt, nht);
+ else
+ fsize = 4;
+ textX = (box.width() - ufont.getWidthPoint(text, fsize)) / 2;
+ textY = offX - ufont.getFontDescriptor(BaseFont.DESCENT, fsize);
+ iconBox = new Rectangle(box.left() + adj, textY + fsize, box.right() - adj, box.top() - adj);
+ break;
+ case LAYOUT_LABEL_TOP_ICON_BOTTOM:
+ if (text == null || text.length() == 0 || wt <= 0 || ht <= 0) {
+ nlayout = LAYOUT_ICON_ONLY;
+ continue;
+ }
+ nht = box.height() * 0.35f - offX;
+ if (nht > 0)
+ fsize = calculateFontSize(wt, nht);
+ else
+ fsize = 4;
+ textX = (box.width() - ufont.getWidthPoint(text, fsize)) / 2;
+ textY = box.height() - offX - fsize;
+ if (textY < offX)
+ textY = offX;
+ iconBox = new Rectangle(box.left() + adj, box.bottom() + adj, box.right() - adj, textY + ufont.getFontDescriptor(BaseFont.DESCENT, fsize));
+ break;
+ case LAYOUT_LABEL_LEFT_ICON_RIGHT:
+ if (text == null || text.length() == 0 || wt <= 0 || ht <= 0) {
+ nlayout = LAYOUT_ICON_ONLY;
+ continue;
+ }
+ float nw = box.width() * 0.35f - offX;
+ if (nw > 0)
+ fsize = calculateFontSize(wt, nw);
+ else
+ fsize = 4;
+ if (ufont.getWidthPoint(text, fsize) >= wt) {
+ nlayout = LAYOUT_LABEL_ONLY;
+ fsize = fontSize;
+ continue;
+ }
+ textX = offX + 1;
+ textY = (box.height() - ufont.getFontDescriptor(BaseFont.ASCENT, fsize)) / 2;
+ iconBox = new Rectangle(textX + ufont.getWidthPoint(text, fsize), box.bottom() + adj, box.right() - adj, box.top() - adj);
+ break;
+ case LAYOUT_ICON_LEFT_LABEL_RIGHT:
+ if (text == null || text.length() == 0 || wt <= 0 || ht <= 0) {
+ nlayout = LAYOUT_ICON_ONLY;
+ continue;
+ }
+ nw = box.width() * 0.35f - offX;
+ if (nw > 0)
+ fsize = calculateFontSize(wt, nw);
+ else
+ fsize = 4;
+ if (ufont.getWidthPoint(text, fsize) >= wt) {
+ nlayout = LAYOUT_LABEL_ONLY;
+ fsize = fontSize;
+ continue;
+ }
+ textX = box.width() - ufont.getWidthPoint(text, fsize) - offX - 1;
+ textY = (box.height() - ufont.getFontDescriptor(BaseFont.ASCENT, fsize)) / 2;
+ iconBox = new Rectangle(box.left() + adj, box.bottom() + adj, textX - 1, box.top() - adj);
+ break;
+ }
+ break;
+ }
+ if (textY < box.bottom() + offX)
+ textY = box.bottom() + offX;
+ if (iconBox != null && (iconBox.width() <= 0 || iconBox.height() <= 0))
+ iconBox = null;
+ if (iconBox != null) {
+ if (image != null) {
+ tp = new PdfTemplate(writer);
+ tp.setBoundingBox(new Rectangle(image));
+ writer.addDirectTemplateSimple(tp, new PdfName("FRM"));
+ tp.addImage(image, image.width(), 0, 0, image.height(), 0, 0);
+ }
+ else if (template != null) {
+ tp = new PdfTemplate(writer);
+ tp.setBoundingBox(new Rectangle(template.getWidth(), template.getHeight()));
+ writer.addDirectTemplateSimple(tp, new PdfName("FRM"));
+ tp.addTemplate(template, template.getBoundingBox().left(), template.getBoundingBox().bottom());
+ }
+ }
+ if (tp != null) {
+ float icx = iconBox.width() / tp.getBoundingBox().width();
+ float icy = iconBox.height() / tp.getBoundingBox().height();
+ if (proportionalIcon) {
+ switch (scaleIcon) {
+ case SCALE_ICON_IS_TOO_BIG:
+ icx = Math.min(icx, icy);
+ icx = Math.min(icx, 1);
+ break;
+ case SCALE_ICON_IS_TOO_SMALL:
+ icx = Math.min(icx, icy);
+ icx = Math.max(icx, 1);
+ break;
+ case SCALE_ICON_NEVER:
+ icx = 1;
+ break;
+ default:
+ icx = Math.min(icx, icy);
+ break;
+ }
+ icy = icx;
+ }
+ else {
+ switch (scaleIcon) {
+ case SCALE_ICON_IS_TOO_BIG:
+ icx = Math.min(icx, 1);
+ icy = Math.min(icy, 1);
+ break;
+ case SCALE_ICON_IS_TOO_SMALL:
+ icx = Math.max(icx, 1);
+ icy = Math.max(icy, 1);
+ break;
+ case SCALE_ICON_NEVER:
+ icx = icy = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ float xpos = iconBox.left() + (iconBox.width() - (tp.getBoundingBox().width() * icx)) * iconHorizontalAdjustment;
+ float ypos = iconBox.bottom() + (iconBox.height() - (tp.getBoundingBox().height() * icy)) * iconVerticalAdjustment;
+ app.saveState();
+ app.rectangle(iconBox.left(), iconBox.bottom(), iconBox.width(), iconBox.height());
+ app.clip();
+ app.newPath();
+ app.addTemplate(tp, icx, 0, 0, icy, xpos, ypos);
+ app.restoreState();
+ }
+ if (!Float.isNaN(textX)) {
+ app.saveState();
+ app.rectangle(offX, offX, box.width() - 2 * offX, box.height() - 2 * offX);
+ app.clip();
+ app.newPath();
+ if (textColor == null)
+ app.resetGrayFill();
+ else
+ app.setColorFill(textColor);
+ app.beginText();
+ app.setFontAndSize(ufont, fsize);
+ app.setTextMatrix(textX, textY);
+ app.showText(text);
+ app.endText();
+ app.restoreState();
+ }
+ return app;
+ }
+
+ /**
+ * Gets the pushbutton field.
+ * @throws IOException on error
+ * @throws DocumentException on error
+ * @return the pushbutton field
+ */
+ public PdfFormField getField() throws IOException, DocumentException {
+ PdfFormField field = PdfFormField.createPushButton(writer);
+ field.setWidget(box, PdfAnnotation.HIGHLIGHT_INVERT);
+ if (fieldName != null) {
+ field.setFieldName(fieldName);
+ if ((options & READ_ONLY) != 0)
+ field.setFieldFlags(PdfFormField.FF_READ_ONLY);
+ if ((options & REQUIRED) != 0)
+ field.setFieldFlags(PdfFormField.FF_REQUIRED);
+ }
+ if (text != null)
+ field.setMKNormalCaption(text);
+ if (rotation != 0)
+ field.setMKRotation(rotation);
+ field.setBorderStyle(new PdfBorderDictionary(borderWidth, borderStyle, new PdfDashPattern(3)));
+ PdfAppearance tpa = getAppearance();
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tpa);
+ PdfAppearance da = (PdfAppearance)tpa.getDuplicate();
+ da.setFontAndSize(getRealFont(), fontSize);
+ if (textColor == null)
+ da.setGrayFill(0);
+ else
+ da.setColorFill(textColor);
+ field.setDefaultAppearanceString(da);
+ if (borderColor != null)
+ field.setMKBorderColor(borderColor);
+ if (backgroundColor != null)
+ field.setMKBackgroundColor(backgroundColor);
+ switch (visibility) {
+ case HIDDEN:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_HIDDEN);
+ break;
+ case VISIBLE_BUT_DOES_NOT_PRINT:
+ break;
+ case HIDDEN_BUT_PRINTABLE:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_NOVIEW);
+ break;
+ default:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT);
+ break;
+ }
+ if (tp != null)
+ field.setMKNormalIcon(tp);
+ field.setMKTextPosition(layout - 1);
+ PdfName scale = PdfName.A;
+ if (scaleIcon == SCALE_ICON_IS_TOO_BIG)
+ scale = PdfName.B;
+ else if (scaleIcon == SCALE_ICON_IS_TOO_SMALL)
+ scale = PdfName.S;
+ else if (scaleIcon == SCALE_ICON_NEVER)
+ scale = PdfName.N;
+ field.setMKIconFit(scale, proportionalIcon ? PdfName.P : PdfName.A, iconHorizontalAdjustment,
+ iconVerticalAdjustment, iconFitToBounds);
+ return field;
+ }
+
+ /**
+ * Getter for property iconFitToBounds.
+ * @return Value of property iconFitToBounds.
+ */
+ public boolean isIconFitToBounds() {
+ return this.iconFitToBounds;
+ }
+
+ /**
+ * If true
the icon will be scaled to fit fully within the bounds of the annotation,
+ * if false
the border width will be taken into account. The default
+ * is false
.
+ * @param iconFitToBounds if true
the icon will be scaled to fit fully within the bounds of the annotation,
+ * if false
the border width will be taken into account
+ */
+ public void setIconFitToBounds(boolean iconFitToBounds) {
+ this.iconFitToBounds = iconFitToBounds;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/RadioCheckField.java b/src/main/java/com/lowagie/text/pdf/RadioCheckField.java
new file mode 100644
index 0000000..e6a254e
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/RadioCheckField.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.DocumentException;
+import java.io.IOException;
+
+/**
+ * Creates a radio or a check field.
+ *
+ * Document document = new Document(PageSize.A4, 50, 50, 50, 50);
+ * PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("output.pdf"));
+ * document.open();
+ * PdfContentByte cb = writer.getDirectContent();
+ * RadioCheckField bt = new RadioCheckField(writer, new Rectangle(100, 100, 200, 200), "radio", "v1");
+ * bt.setCheckType(RadioCheckField.TYPE_CIRCLE);
+ * bt.setBackgroundColor(Color.cyan);
+ * bt.setBorderStyle(PdfBorderDictionary.STYLE_SOLID);
+ * bt.setBorderColor(Color.red);
+ * bt.setTextColor(Color.yellow);
+ * bt.setBorderWidth(BaseField.BORDER_WIDTH_THICK);
+ * bt.setChecked(false);
+ * PdfFormField f1 = bt.getRadioField();
+ * bt.setOnValue("v2");
+ * bt.setChecked(true);
+ * bt.setBox(new Rectangle(100, 300, 200, 400));
+ * PdfFormField f2 = bt.getRadioField();
+ * bt.setChecked(false);
+ * PdfFormField top = bt.getRadioGroup(true, false);
+ * bt.setOnValue("v3");
+ * bt.setBox(new Rectangle(100, 500, 200, 600));
+ * PdfFormField f3 = bt.getRadioField();
+ * top.addKid(f1);
+ * top.addKid(f2);
+ * top.addKid(f3);
+ * writer.addAnnotation(top);
+ * bt = new RadioCheckField(writer, new Rectangle(300, 300, 400, 400), "check1", "Yes");
+ * bt.setCheckType(RadioCheckField.TYPE_CHECK);
+ * bt.setBorderWidth(BaseField.BORDER_WIDTH_THIN);
+ * bt.setBorderColor(Color.black);
+ * bt.setBackgroundColor(Color.white);
+ * PdfFormField ck = bt.getCheckField();
+ * writer.addAnnotation(ck);
+ * document.close();
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class RadioCheckField extends BaseField {
+
+ /** A field with the symbol check */
+ public static final int TYPE_CHECK = 1;
+ /** A field with the symbol circle */
+ public static final int TYPE_CIRCLE = 2;
+ /** A field with the symbol cross */
+ public static final int TYPE_CROSS = 3;
+ /** A field with the symbol diamond */
+ public static final int TYPE_DIAMOND = 4;
+ /** A field with the symbol square */
+ public static final int TYPE_SQUARE = 5;
+ /** A field with the symbol star */
+ public static final int TYPE_STAR = 6;
+
+ private static String typeChars[] = {"4", "l", "8", "u", "n", "H"};
+
+ /**
+ * Holds value of property checkType.
+ */
+ private int checkType;
+
+ /**
+ * Holds value of property onValue.
+ */
+ private String onValue;
+
+ /**
+ * Holds value of property checked.
+ */
+ private boolean checked;
+
+ /**
+ * Creates a new instance of RadioCheckField
+ * @param writer the document PdfWriter
+ * @param box the field location and dimensions
+ * @param fieldName the field name. It must not be null
+ * @param onValue the value when the field is checked
+ */
+ public RadioCheckField(PdfWriter writer, Rectangle box, String fieldName, String onValue) {
+ super(writer, box, fieldName);
+ setOnValue(onValue);
+ setCheckType(TYPE_CIRCLE);
+ }
+
+ /**
+ * Getter for property checkType.
+ * @return Value of property checkType.
+ */
+ public int getCheckType() {
+ return this.checkType;
+ }
+
+ /**
+ * Sets the checked symbol. It can be
+ * TYPE_CHECK
,
+ * TYPE_CIRCLE
,
+ * TYPE_CROSS
,
+ * TYPE_DIAMOND
,
+ * TYPE_SQUARE
and
+ * TYPE_STAR
.
+ * @param checkType the checked symbol
+ */
+ public void setCheckType(int checkType) {
+ if (checkType < TYPE_CHECK || checkType > TYPE_STAR)
+ checkType = TYPE_CIRCLE;
+ this.checkType = checkType;
+ setText(typeChars[checkType - 1]);
+ try {
+ setFont(BaseFont.createFont(BaseFont.ZAPFDINGBATS, BaseFont.WINANSI, false));
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Getter for property onValue.
+ * @return Value of property onValue.
+ */
+ public String getOnValue() {
+ return this.onValue;
+ }
+
+ /**
+ * Sets the value when the field is checked.
+ * @param onValue the value when the field is checked
+ */
+ public void setOnValue(String onValue) {
+ this.onValue = onValue;
+ }
+
+ /**
+ * Getter for property checked.
+ * @return Value of property checked.
+ */
+ public boolean isChecked() {
+ return this.checked;
+ }
+
+ /**
+ * Sets the state of the field to checked or unchecked.
+ * @param checked the state of the field, true
for checked
+ * and false
for unchecked
+ */
+ public void setChecked(boolean checked) {
+ this.checked = checked;
+ }
+
+ /**
+ * Gets the field appearance.
+ * @param isRadio true
for a radio field and false
+ * for a check field
+ * @param on true
for the checked state, false
+ * otherwise
+ * @throws IOException on error
+ * @throws DocumentException on error
+ * @return the appearance
+ */
+ public PdfAppearance getAppearance(boolean isRadio, boolean on) throws IOException, DocumentException {
+ if (isRadio && checkType == TYPE_CIRCLE)
+ return getAppearanceRadioCircle(on);
+ PdfAppearance app = getBorderAppearance();
+ if (!on)
+ return app;
+ BaseFont ufont = getRealFont();
+ boolean borderExtra = borderStyle == PdfBorderDictionary.STYLE_BEVELED || borderStyle == PdfBorderDictionary.STYLE_INSET;
+ float h = box.height() - borderWidth * 2;
+ float bw2 = borderWidth;
+ if (borderExtra) {
+ h -= borderWidth * 2;
+ bw2 *= 2;
+ }
+ float offsetX = (borderExtra ? 2 * borderWidth : borderWidth);
+ offsetX = Math.max(offsetX, 1);
+ float offX = Math.min(bw2, offsetX);
+ float wt = box.width() - 2 * offX;
+ float ht = box.height() - 2 * offX;
+ float fsize = fontSize;
+ if (fsize == 0) {
+ float bw = ufont.getWidthPoint(text, 1);
+ if (bw == 0)
+ fsize = 12;
+ else
+ fsize = wt / bw;
+ float nfsize = h / (ufont.getFontDescriptor(BaseFont.ASCENT, 1));
+ fsize = Math.min(fsize, nfsize);
+ }
+ app.saveState();
+ app.rectangle(offX, offX, box.width() - 2 * offX, box.height() - 2 * offX);
+ app.clip();
+ app.newPath();
+ if (textColor == null)
+ app.resetGrayFill();
+ else
+ app.setColorFill(textColor);
+ app.beginText();
+ app.setFontAndSize(ufont, fsize);
+ app.setTextMatrix((box.width() - ufont.getWidthPoint(text, fsize)) / 2,
+ (box.height() - ufont.getAscentPoint(text, fsize)) / 2);
+ app.showText(text);
+ app.endText();
+ app.restoreState();
+ return app;
+ }
+
+ /**
+ * Gets the special field appearance for the radio circle.
+ * @param on true
for the checked state, false
+ * otherwise
+ * @return the appearance
+ */
+ public PdfAppearance getAppearanceRadioCircle(boolean on) {
+ PdfAppearance app = new PdfContentByte(writer).createAppearance(box.width(), box.height());
+ switch (rotation) {
+ case 90:
+ app.setMatrix(0, 1, -1, 0, box.height(), 0);
+ break;
+ case 180:
+ app.setMatrix(-1, 0, 0, -1, box.width(), box.height());
+ break;
+ case 270:
+ app.setMatrix(0, -1, 1, 0, 0, box.width());
+ break;
+ }
+ Rectangle box = new Rectangle(app.getBoundingBox());
+ float cx = box.width() / 2;
+ float cy = box.height() / 2;
+ float r = (Math.min(box.width(), box.height()) - borderWidth) / 2;
+ if (r <= 0)
+ return app;
+ if (backgroundColor != null) {
+ app.setColorFill(backgroundColor);
+ app.circle(cx, cy, r + borderWidth / 2);
+ app.fill();
+ }
+ if (borderWidth > 0 && borderColor != null) {
+ app.setLineWidth(borderWidth);
+ app.setColorStroke(borderColor);
+ app.circle(cx, cy, r);
+ app.stroke();
+ }
+ if (on) {
+ if (textColor == null)
+ app.resetGrayFill();
+ else
+ app.setColorFill(textColor);
+ app.circle(cx, cy, r / 2);
+ app.fill();
+ }
+ return app;
+ }
+
+ /**
+ * Gets a radio group. It's composed of the field specific keys, without the widget
+ * ones. This field is to be used as a field aggregator with {@link PdfFormField#addKid(PdfFormField) addKid()}.
+ * @param noToggleToOff if true
, exactly one radio button must be selected at all
+ * times; clicking the currently selected button has no effect.
+ * If false
, clicking
+ * the selected button deselects it, leaving no button selected.
+ * @param radiosInUnison if true
, a group of radio buttons within a radio button field that
+ * use the same value for the on state will turn on and off in unison; that is if
+ * one is checked, they are all checked. If false
, the buttons are mutually exclusive
+ * (the same behavior as HTML radio buttons)
+ * @return the radio group
+ */
+ public PdfFormField getRadioGroup(boolean noToggleToOff, boolean radiosInUnison) {
+ PdfFormField field = PdfFormField.createRadioButton(writer, noToggleToOff);
+ if (radiosInUnison)
+ field.setFieldFlags(PdfFormField.FF_RADIOSINUNISON);
+ field.setFieldName(fieldName);
+ if ((options & READ_ONLY) != 0)
+ field.setFieldFlags(PdfFormField.FF_READ_ONLY);
+ if ((options & REQUIRED) != 0)
+ field.setFieldFlags(PdfFormField.FF_REQUIRED);
+ field.setValueAsName(checked ? onValue : "Off");
+ return field;
+ }
+
+ /**
+ * Gets the radio field. It's only composed of the widget keys and must be used
+ * with {@link #getRadioGroup(boolean,boolean)}.
+ * @return the radio field
+ * @throws IOException on error
+ * @throws DocumentException on error
+ */
+ public PdfFormField getRadioField() throws IOException, DocumentException {
+ return getField(true);
+ }
+
+ /**
+ * Gets the check field.
+ * @return the check field
+ * @throws IOException on error
+ * @throws DocumentException on error
+ */
+ public PdfFormField getCheckField() throws IOException, DocumentException {
+ return getField(false);
+ }
+
+ /**
+ * Gets a radio or check field.
+ * @param isRadio true
to get a radio field, false
to get
+ * a check field
+ * @throws IOException on error
+ * @throws DocumentException on error
+ * @return the field
+ */
+ protected PdfFormField getField(boolean isRadio) throws IOException, DocumentException {
+ PdfFormField field = null;
+ if (isRadio)
+ field = PdfFormField.createEmpty(writer);
+ else
+ field = PdfFormField.createCheckBox(writer);
+ field.setWidget(box, PdfAnnotation.HIGHLIGHT_INVERT);
+ if (!isRadio) {
+ field.setFieldName(fieldName);
+ if ((options & READ_ONLY) != 0)
+ field.setFieldFlags(PdfFormField.FF_READ_ONLY);
+ if ((options & REQUIRED) != 0)
+ field.setFieldFlags(PdfFormField.FF_REQUIRED);
+ field.setValueAsName(checked ? onValue : "Off");
+ }
+ if (text != null)
+ field.setMKNormalCaption(text);
+ if (rotation != 0)
+ field.setMKRotation(rotation);
+ field.setBorderStyle(new PdfBorderDictionary(borderWidth, borderStyle, new PdfDashPattern(3)));
+ PdfAppearance tpon = getAppearance(isRadio, true);
+ PdfAppearance tpoff = getAppearance(isRadio, false);
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, onValue, tpon);
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, "Off", tpoff);
+ field.setAppearanceState(checked ? onValue : "Off");
+ PdfAppearance da = (PdfAppearance)tpon.getDuplicate();
+ da.setFontAndSize(getRealFont(), fontSize);
+ if (textColor == null)
+ da.setGrayFill(0);
+ else
+ da.setColorFill(textColor);
+ field.setDefaultAppearanceString(da);
+ if (borderColor != null)
+ field.setMKBorderColor(borderColor);
+ if (backgroundColor != null)
+ field.setMKBackgroundColor(backgroundColor);
+ switch (visibility) {
+ case HIDDEN:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_HIDDEN);
+ break;
+ case VISIBLE_BUT_DOES_NOT_PRINT:
+ break;
+ case HIDDEN_BUT_PRINTABLE:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_NOVIEW);
+ break;
+ default:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT);
+ break;
+ }
+ return field;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/RandomAccessFileOrArray.java b/src/main/java/com/lowagie/text/pdf/RandomAccessFileOrArray.java
new file mode 100644
index 0000000..212a6c8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/RandomAccessFileOrArray.java
@@ -0,0 +1,609 @@
+/*
+ * $Id: RandomAccessFileOrArray.java,v 1.49 2005/09/12 16:15:23 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.DataInputStream;
+import java.io.DataInput;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.io.RandomAccessFile;
+import java.io.File;
+import java.io.InputStream;
+import java.io.ByteArrayOutputStream;
+import java.net.URL;
+/** An implementation of a RandomAccessFile for input only
+ * that accepts a file or a byte array as data source.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class RandomAccessFileOrArray implements DataInput {
+
+ RandomAccessFile rf;
+ String filename;
+ byte arrayIn[];
+ int arrayInPtr;
+ byte back;
+ boolean isBack = false;
+
+ /** Holds value of property startOffset. */
+ private int startOffset = 0;
+
+ public RandomAccessFileOrArray(String filename) throws IOException {
+ this(filename, false);
+ }
+
+ public RandomAccessFileOrArray(String filename, boolean forceRead) throws IOException {
+ File file = new File(filename);
+ if (!file.canRead()) {
+ if (filename.startsWith("file:/") || filename.startsWith("http://") || filename.startsWith("https://") || filename.startsWith("jar:")) {
+ InputStream is = new URL(filename).openStream();
+ try {
+ this.arrayIn = InputStreamToArray(is);
+ return;
+ }
+ finally {
+ try {is.close();}catch(IOException ioe){}
+ }
+ }
+ else {
+ InputStream is = BaseFont.getResourceStream(filename);
+ if (is == null)
+ throw new IOException(filename + " not found as file or resource.");
+ try {
+ this.arrayIn = InputStreamToArray(is);
+ return;
+ }
+ finally {
+ try {is.close();}catch(IOException ioe){}
+ }
+ }
+ }
+ else if (forceRead) {
+ InputStream s = null;
+ try {
+ s = new FileInputStream(file);
+ this.arrayIn = InputStreamToArray(s);
+ }
+ finally {
+ try {s.close();}catch(Exception e){}
+ }
+ return;
+ }
+ this.filename = filename;
+ rf = new RandomAccessFile(filename, "r");
+ }
+
+ public RandomAccessFileOrArray(URL url) throws IOException {
+ InputStream is = url.openStream();
+ try {
+ this.arrayIn = InputStreamToArray(is);
+ }
+ finally {
+ try {is.close();}catch(IOException ioe){}
+ }
+ }
+
+ public RandomAccessFileOrArray(InputStream is) throws IOException {
+ this.arrayIn = InputStreamToArray(is);
+ }
+
+ public static byte[] InputStreamToArray(InputStream is) throws IOException {
+ byte b[] = new byte[8192];
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ while (true) {
+ int read = is.read(b);
+ if (read < 1)
+ break;
+ out.write(b, 0, read);
+ }
+ return out.toByteArray();
+ }
+
+ public RandomAccessFileOrArray(byte arrayIn[]) {
+ this.arrayIn = arrayIn;
+ }
+
+ public RandomAccessFileOrArray(RandomAccessFileOrArray file) {
+ filename = file.filename;
+ arrayIn = file.arrayIn;
+ startOffset = file.startOffset;
+ }
+
+ public void pushBack(byte b) {
+ back = b;
+ isBack = true;
+ }
+
+ public int read() throws IOException {
+ if(isBack) {
+ isBack = false;
+ return back & 0xff;
+ }
+ if (arrayIn == null)
+ return rf.read();
+ else {
+ if (arrayInPtr >= arrayIn.length)
+ return -1;
+ return arrayIn[arrayInPtr++] & 0xff;
+ }
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (len == 0)
+ return 0;
+ int n = 0;
+ if (isBack) {
+ isBack = false;
+ if (len == 1) {
+ b[off] = back;
+ return 1;
+ }
+ else {
+ n = 1;
+ b[off++] = back;
+ --len;
+ }
+ }
+ if (arrayIn == null) {
+ return rf.read(b, off, len) + n;
+ }
+ else {
+ if (arrayInPtr >= arrayIn.length)
+ return -1;
+ if (arrayInPtr + len > arrayIn.length)
+ len = arrayIn.length - arrayInPtr;
+ System.arraycopy(arrayIn, arrayInPtr, b, off, len);
+ arrayInPtr += len;
+ return len + n;
+ }
+ }
+
+ public int read(byte b[]) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ public void readFully(byte b[]) throws IOException {
+ readFully(b, 0, b.length);
+ }
+
+ public void readFully(byte b[], int off, int len) throws IOException {
+ int n = 0;
+ do {
+ int count = read(b, off + n, len - n);
+ if (count < 0)
+ throw new EOFException();
+ n += count;
+ } while (n < len);
+ }
+
+ public long skip(long n) throws IOException {
+ return skipBytes((int)n);
+ }
+
+ public int skipBytes(int n) throws IOException {
+ if (n <= 0) {
+ return 0;
+ }
+ int adj = 0;
+ if (isBack) {
+ isBack = false;
+ if (n == 1) {
+ return 1;
+ }
+ else {
+ --n;
+ adj = 1;
+ }
+ }
+ int pos;
+ int len;
+ int newpos;
+
+ pos = getFilePointer();
+ len = length();
+ newpos = pos + n;
+ if (newpos > len) {
+ newpos = len;
+ }
+ seek(newpos);
+
+ /* return the actual number of bytes skipped */
+ return newpos - pos + adj;
+ }
+
+ public void reOpen() throws IOException {
+ if (filename != null && rf == null)
+ rf = new RandomAccessFile(filename, "r");
+ seek(0);
+ }
+
+ protected void insureOpen() throws IOException {
+ if (filename != null && rf == null) {
+ reOpen();
+ }
+ }
+
+ public boolean isOpen() {
+ return (filename == null || rf != null);
+ }
+
+ public void close() throws IOException {
+ isBack = false;
+ if (rf != null) {
+ rf.close();
+ rf = null;
+ }
+ }
+
+ public int length() throws IOException {
+ if (arrayIn == null) {
+ insureOpen();
+ return (int)rf.length() - startOffset;
+ }
+ else
+ return arrayIn.length - startOffset;
+ }
+
+ public void seek(int pos) throws IOException {
+ pos += startOffset;
+ isBack = false;
+ if (arrayIn == null) {
+ insureOpen();
+ rf.seek(pos);
+ }
+ else
+ arrayInPtr = pos;
+ }
+
+ public void seek(long pos) throws IOException {
+ seek((int)pos);
+ }
+
+ public int getFilePointer() throws IOException {
+ insureOpen();
+ int n = isBack ? 1 : 0;
+ if (arrayIn == null) {
+ return (int)rf.getFilePointer() - n - startOffset;
+ }
+ else
+ return arrayInPtr - n - startOffset;
+ }
+
+ public boolean readBoolean() throws IOException {
+ int ch = this.read();
+ if (ch < 0)
+ throw new EOFException();
+ return (ch != 0);
+ }
+
+ public byte readByte() throws IOException {
+ int ch = this.read();
+ if (ch < 0)
+ throw new EOFException();
+ return (byte)(ch);
+ }
+
+ public int readUnsignedByte() throws IOException {
+ int ch = this.read();
+ if (ch < 0)
+ throw new EOFException();
+ return ch;
+ }
+
+ public short readShort() throws IOException {
+ int ch1 = this.read();
+ int ch2 = this.read();
+ if ((ch1 | ch2) < 0)
+ throw new EOFException();
+ return (short)((ch1 << 8) + ch2);
+ }
+
+ /**
+ * Reads a signed 16-bit number from this stream in little-endian order.
+ * The method reads two
+ * bytes from this stream, starting at the current stream pointer.
+ * If the two bytes read, in order, are
+ * b1
and b2
, where each of the two values is
+ * between 0
and 255
, inclusive, then the
+ * result is equal to:
+ *
+ *
+ * (short)((b2 << 8) | b1)
+ *
b1
and b2
, where
+ * 0 <= b1, b2 <= 255
,
+ * then the result is equal to:
+ *
+ *
+ * (b2 << 8) | b1
+ *
b1
and b2
, where
+ * 0 <= b1, b2 <= 255
,
+ * then the result is equal to:
+ *
+ *
+ * (char)((b2 << 8) | b1)
+ *
b1
,
+ * b2
, b3
, and b4
, where
+ * 0 <= b1, b2, b3, b4 <= 255
,
+ * then the result is equal to:
+ *
+ *
+ * (b4 << 24) | (b3 << 16) + (b2 << 8) + b1
+ *
int
.
+ * @exception EOFException if this stream reaches the end before reading
+ * four bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public final int readIntLE() throws IOException {
+ int ch1 = this.read();
+ int ch2 = this.read();
+ int ch3 = this.read();
+ int ch4 = this.read();
+ if ((ch1 | ch2 | ch3 | ch4) < 0)
+ throw new EOFException();
+ return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0));
+ }
+
+ /**
+ * Reads an unsigned 32-bit integer from this stream. This method reads 4
+ * bytes from the stream, starting at the current stream pointer.
+ * If the bytes read, in order, are b1
,
+ * b2
, b3
, and b4
, where
+ * 0 <= b1, b2, b3, b4 <= 255
,
+ * then the result is equal to:
+ *
+ *
+ * (b1 << 24) | (b2 << 16) + (b3 << 8) + b4
+ *
long
.
+ * @exception EOFException if this stream reaches the end before reading
+ * four bytes.
+ * @exception IOException if an I/O error occurs.
+ */
+ public final long readUnsignedInt() throws IOException {
+ long ch1 = this.read();
+ long ch2 = this.read();
+ long ch3 = this.read();
+ long ch4 = this.read();
+ if ((ch1 | ch2 | ch3 | ch4) < 0)
+ throw new EOFException();
+ return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
+ }
+
+ public final long readUnsignedIntLE() throws IOException {
+ long ch1 = this.read();
+ long ch2 = this.read();
+ long ch3 = this.read();
+ long ch4 = this.read();
+ if ((ch1 | ch2 | ch3 | ch4) < 0)
+ throw new EOFException();
+ return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0));
+ }
+
+ public long readLong() throws IOException {
+ return ((long)(readInt()) << 32) + (readInt() & 0xFFFFFFFFL);
+ }
+
+ public final long readLongLE() throws IOException {
+ int i1 = readIntLE();
+ int i2 = readIntLE();
+ return ((long)i2 << 32) + (i1 & 0xFFFFFFFFL);
+ }
+
+ public float readFloat() throws IOException {
+ return Float.intBitsToFloat(readInt());
+ }
+
+ public final float readFloatLE() throws IOException {
+ return Float.intBitsToFloat(readIntLE());
+ }
+
+ public double readDouble() throws IOException {
+ return Double.longBitsToDouble(readLong());
+ }
+
+ public final double readDoubleLE() throws IOException {
+ return Double.longBitsToDouble(readLongLE());
+ }
+
+ public String readLine() throws IOException {
+ StringBuffer input = new StringBuffer();
+ int c = -1;
+ boolean eol = false;
+
+ while (!eol) {
+ switch (c = read()) {
+ case -1:
+ case '\n':
+ eol = true;
+ break;
+ case '\r':
+ eol = true;
+ int cur = getFilePointer();
+ if ((read()) != '\n') {
+ seek(cur);
+ }
+ break;
+ default:
+ input.append((char)c);
+ break;
+ }
+ }
+
+ if ((c == -1) && (input.length() == 0)) {
+ return null;
+ }
+ return input.toString();
+ }
+
+ public String readUTF() throws IOException {
+ return DataInputStream.readUTF(this);
+ }
+
+ /** Getter for property startOffset.
+ * @return Value of property startOffset.
+ *
+ */
+ public int getStartOffset() {
+ return this.startOffset;
+ }
+
+ /** Setter for property startOffset.
+ * @param startOffset New value of property startOffset.
+ *
+ */
+ public void setStartOffset(int startOffset) {
+ this.startOffset = startOffset;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/SequenceList.java b/src/main/java/com/lowagie/text/pdf/SequenceList.java
new file mode 100644
index 0000000..e00d1d4
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/SequenceList.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.util.List;
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+/**
+ * This class expands a string into a list of numbers. The main use is to select a
+ * range of pages.
+ *
+ * [!][o][odd][e][even]start-end
+ * Integer
+ */
+ public static List expand(String ranges, int maxNumber) {
+ SequenceList parse = new SequenceList(ranges);
+ LinkedList list = new LinkedList();
+ boolean sair = false;
+ while (!sair) {
+ sair = parse.getAttributes();
+ if (parse.low == -1 && parse.high == -1 && !parse.even && !parse.odd)
+ continue;
+ if (parse.low < 1)
+ parse.low = 1;
+ if (parse.high < 1 || parse.high > maxNumber)
+ parse.high = maxNumber;
+ if (parse.low > maxNumber)
+ parse.low = maxNumber;
+
+ //System.out.println("low="+parse.low+",high="+parse.high+",odd="+parse.odd+",even="+parse.even+",inverse="+parse.inverse);
+ int inc = 1;
+ if (parse.inverse) {
+ if (parse.low > parse.high) {
+ int t = parse.low;
+ parse.low = parse.high;
+ parse.high = t;
+ }
+ for (ListIterator it = list.listIterator(); it.hasNext();) {
+ int n = ((Integer)it.next()).intValue();
+ if (parse.even && (n & 1) == 1)
+ continue;
+ if (parse.odd && (n & 1) == 0)
+ continue;
+ if (n >= parse.low && n <= parse.high)
+ it.remove();
+ }
+ }
+ else {
+ if (parse.low > parse.high) {
+ inc = -1;
+ if (parse.odd || parse.even) {
+ --inc;
+ if (parse.even)
+ parse.low &= ~1;
+ else
+ parse.low -= ((parse.low & 1) == 1 ? 0 : 1);
+ }
+ for (int k = parse.low; k >= parse.high; k += inc)
+ list.add(new Integer(k));
+ }
+ else {
+ if (parse.odd || parse.even) {
+ ++inc;
+ if (parse.odd)
+ parse.low |= 1;
+ else
+ parse.low += ((parse.low & 1) == 1 ? 1 : 0);
+ }
+ for (int k = parse.low; k <= parse.high; k += inc) {
+ list.add(new Integer(k));
+ }
+ }
+ }
+// for (int k = 0; k < list.size(); ++k)
+// System.out.print(((Integer)list.get(k)).intValue() + ",");
+// System.out.println();
+ }
+ return list;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/ShadingColor.java b/src/main/java/com/lowagie/text/pdf/ShadingColor.java
new file mode 100644
index 0000000..8d38277
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/ShadingColor.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/** Implements a shading pattern as a Color
.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class ShadingColor extends ExtendedColor {
+
+ PdfShadingPattern shadingPattern;
+
+ /**
+ * Creates a shading color.
+ * @param shadingPattern
+ */
+ public ShadingColor(PdfShadingPattern shadingPattern) {
+ super(TYPE_SHADING, .5f, .5f, .5f);
+ this.shadingPattern = shadingPattern;
+ }
+
+ /**
+ * Gets the shading pattern.
+ * @return a shading pattern.
+ */
+ public PdfShadingPattern getPdfShadingPattern() {
+ return shadingPattern;
+ }
+
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ public int hashCode() {
+ return shadingPattern.hashCode();
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/SimpleBookmark.java b/src/main/java/com/lowagie/text/pdf/SimpleBookmark.java
new file mode 100644
index 0000000..f7bb5a8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/SimpleBookmark.java
@@ -0,0 +1,745 @@
+/*
+ * Copyright 2003 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.Writer;
+import java.io.Reader;
+import java.io.BufferedWriter;
+import java.io.OutputStreamWriter;
+import java.util.Stack;
+/**
+ * Bookmark processing in a simple way. It has some limitations, mainly the only
+ * action types supported are GoTo, GoToR, URI and Launch.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class SimpleBookmark implements SimpleXMLDocHandler {
+
+ private ArrayList topList;
+ private Stack attr = new Stack();
+
+ /** Creates a new instance of SimpleBookmark */
+ private SimpleBookmark() {
+ }
+
+ private static List bookmarkDepth(PdfReader reader, PdfDictionary outline, IntHashtable pages) {
+ ArrayList list = new ArrayList();
+ while (outline != null) {
+ HashMap map = new HashMap();
+ PdfString title = (PdfString)PdfReader.getPdfObjectRelease(outline.get(PdfName.TITLE));
+ map.put("Title", title.toUnicodeString());
+ PdfArray color = (PdfArray)PdfReader.getPdfObjectRelease(outline.get(PdfName.C));
+ if (color != null && color.getArrayList().size() == 3) {
+ ByteBuffer out = new ByteBuffer();
+ ArrayList arr = color.getArrayList();
+ out.append(((PdfNumber)arr.get(0)).floatValue()).append(' ');
+ out.append(((PdfNumber)arr.get(1)).floatValue()).append(' ');
+ out.append(((PdfNumber)arr.get(2)).floatValue());
+ map.put("Color", PdfEncodings.convertToString(out.toByteArray(), null));
+ }
+ PdfNumber style = (PdfNumber)PdfReader.getPdfObjectRelease(outline.get(PdfName.F));
+ if (style != null) {
+ int f = style.intValue();
+ String s = "";
+ if ((f & 1) != 0)
+ s += "italic ";
+ if ((f & 2) != 0)
+ s += "bold ";
+ s = s.trim();
+ if (s.length() != 0)
+ map.put("Style", s);
+ }
+ PdfNumber count = (PdfNumber)PdfReader.getPdfObjectRelease(outline.get(PdfName.COUNT));
+ if (count != null && count.intValue() < 0)
+ map.put("Open", "false");
+ try {
+ PdfObject dest = PdfReader.getPdfObjectRelease(outline.get(PdfName.DEST));
+ if (dest != null) {
+ mapGotoBookmark(map, dest, pages); //changed by ujihara 2004-06-13
+ }
+ else {
+ PdfDictionary action = (PdfDictionary)PdfReader.getPdfObjectRelease(outline.get(PdfName.A));
+ if (action != null) {
+ if (PdfName.GOTO.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
+ dest = PdfReader.getPdfObjectRelease(action.get(PdfName.D));
+ if (dest != null) {
+ mapGotoBookmark(map, dest, pages);
+ }
+ }
+ else if (PdfName.URI.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
+ map.put("Action", "URI");
+ map.put("URI", ((PdfString)PdfReader.getPdfObjectRelease(action.get(PdfName.URI))).toUnicodeString());
+ }
+ else if (PdfName.GOTOR.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
+ dest = PdfReader.getPdfObjectRelease(action.get(PdfName.D));
+ if (dest != null) {
+ if (dest.isString())
+ map.put("Named", dest.toString());
+ else if (dest.isName())
+ map.put("NamedN", PdfName.decodeName(dest.toString()));
+ else if (dest.isArray()) {
+ ArrayList arr = ((PdfArray)dest).getArrayList();
+ StringBuffer s = new StringBuffer();
+ s.append(arr.get(0).toString());
+ s.append(' ').append(arr.get(1).toString());
+ for (int k = 2; k < arr.size(); ++k)
+ s.append(' ').append(arr.get(k).toString());
+ map.put("Page", s.toString());
+ }
+ }
+ map.put("Action", "GoToR");
+ PdfObject file = PdfReader.getPdfObjectRelease(action.get(PdfName.F));
+ if (file != null) {
+ if (file.isString())
+ map.put("File", ((PdfString)file).toUnicodeString());
+ else if (file.isDictionary()) {
+ file = PdfReader.getPdfObject(((PdfDictionary)file).get(PdfName.F));
+ if (file.isString())
+ map.put("File", ((PdfString)file).toUnicodeString());
+ }
+ }
+ PdfObject newWindow = PdfReader.getPdfObjectRelease(action.get(PdfName.NEWWINDOW));
+ if (newWindow != null)
+ map.put("NewWindow", newWindow.toString());
+ }
+ else if (PdfName.LAUNCH.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
+ map.put("Action", "Launch");
+ PdfObject file = PdfReader.getPdfObjectRelease(action.get(PdfName.F));
+ if (file == null)
+ file = PdfReader.getPdfObjectRelease(action.get(PdfName.WIN));
+ if (file != null) {
+ if (file.isString())
+ map.put("File", ((PdfString)file).toUnicodeString());
+ else if (file.isDictionary()) {
+ file = PdfReader.getPdfObjectRelease(((PdfDictionary)file).get(PdfName.F));
+ if (file.isString())
+ map.put("File", ((PdfString)file).toUnicodeString());
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e) {
+ //empty on purpose
+ }
+ PdfDictionary first = (PdfDictionary)PdfReader.getPdfObjectRelease(outline.get(PdfName.FIRST));
+ if (first != null) {
+ map.put("Kids", bookmarkDepth(reader, first, pages));
+ }
+ list.add(map);
+ outline = (PdfDictionary)PdfReader.getPdfObjectRelease(outline.get(PdfName.NEXT));
+ }
+ return list;
+ }
+
+ private static void mapGotoBookmark(HashMap map, PdfObject dest, IntHashtable pages)
+ {
+ if (dest.isString())
+ map.put("Named", dest.toString());
+ else if (dest.isName())
+ map.put("Named", PdfName.decodeName(dest.toString()));
+ else if (dest.isArray())
+ map.put("Page", makeBookmarkParam((PdfArray)dest, pages)); //changed by ujihara 2004-06-13
+ map.put("Action", "GoTo");
+ }
+
+ private static String makeBookmarkParam(PdfArray dest, IntHashtable pages)
+ {
+ ArrayList arr = dest.getArrayList();
+ StringBuffer s = new StringBuffer();
+ s.append(pages.get(getNumber((PdfIndirectReference)arr.get(0)))); //changed by ujihara 2004-06-13
+ s.append(' ').append(arr.get(1).toString().substring(1));
+ for (int k = 2; k < arr.size(); ++k)
+ s.append(' ').append(arr.get(k).toString());
+ return s.toString();
+ }
+
+ /**
+ * Gets number of indirect. If type of directed indirect is PAGES, it refers PAGE object through KIDS.
+ * (Contributed by Kazuya Ujihara)
+ * @param indirect
+ * 2004-06-13
+ */
+ private static int getNumber(PdfIndirectReference indirect)
+ {
+ PdfDictionary pdfObj = (PdfDictionary)PdfReader.getPdfObjectRelease(indirect);
+ if (pdfObj.contains(PdfName.TYPE) && pdfObj.get(PdfName.TYPE).equals(PdfName.PAGES) && pdfObj.contains(PdfName.KIDS))
+ {
+ PdfArray kids = (PdfArray)pdfObj.get(PdfName.KIDS);
+ indirect = (PdfIndirectReference)kids.arrayList.get(0);
+ }
+ return indirect.getNumber();
+ }
+
+ /**
+ * Gets a List
with the bookmarks. It returns null
if
+ * the document doesn't have any bookmarks.
+ * @param reader the document
+ * @return a List
with the bookmarks or null
if the
+ * document doesn't have any
+ */
+ public static List getBookmark(PdfReader reader) {
+ PdfDictionary catalog = reader.getCatalog();
+ PdfObject obj = PdfReader.getPdfObjectRelease(catalog.get(PdfName.OUTLINES));
+ if (obj == null || !obj.isDictionary())
+ return null;
+ PdfDictionary outlines = (PdfDictionary)obj;
+ IntHashtable pages = new IntHashtable();
+ int numPages = reader.getNumberOfPages();
+ for (int k = 1; k <= numPages; ++k) {
+ pages.put(reader.getPageOrigRef(k).getNumber(), k);
+ reader.releasePage(k);
+ }
+ return bookmarkDepth(reader, (PdfDictionary)PdfReader.getPdfObjectRelease(outlines.get(PdfName.FIRST)), pages);
+ }
+
+ /**
+ * Removes the bookmark entries for a number of page ranges. The page ranges
+ * consists of a number of pairs with the start/end page range. The page numbers
+ * are inclusive.
+ * @param list the bookmarks
+ * @param pageRange the page ranges, always in pairs.
+ */
+ public static void eliminatePages(List list, int pageRange[]) {
+ if (list == null)
+ return;
+ for (Iterator it = list.listIterator(); it.hasNext();) {
+ HashMap map = (HashMap)it.next();
+ boolean hit = false;
+ if ("GoTo".equals(map.get("Action"))) {
+ String page = (String)map.get("Page");
+ if (page != null) {
+ page = page.trim();
+ int idx = page.indexOf(' ');
+ int pageNum;
+ if (idx < 0)
+ pageNum = Integer.parseInt(page);
+ else
+ pageNum = Integer.parseInt(page.substring(0, idx));
+ int len = pageRange.length & 0xfffffffe;
+ for (int k = 0; k < len; k += 2) {
+ if (pageNum >= pageRange[k] && pageNum <= pageRange[k + 1]) {
+ hit = true;
+ break;
+ }
+ }
+ }
+ }
+ List kids = (List)map.get("Kids");
+ if (kids != null) {
+ eliminatePages(kids, pageRange);
+ if (kids.size() == 0) {
+ map.remove("Kids");
+ kids = null;
+ }
+ }
+ if (hit) {
+ if (kids == null)
+ it.remove();
+ else {
+ map.remove("Action");
+ map.remove("Page");
+ map.remove("Named");
+ }
+ }
+ }
+ }
+
+ /**
+ * For the pages in range add the pageShift
to the page number.
+ * The page ranges
+ * consists of a number of pairs with the start/end page range. The page numbers
+ * are inclusive.
+ * @param list the bookmarks
+ * @param pageShift the number to add to the pages in range
+ * @param pageRange the page ranges, always in pairs. It can be null
+ * to include all the pages
+ */
+ public static void shiftPageNumbers(List list, int pageShift, int pageRange[]) {
+ if (list == null)
+ return;
+ for (Iterator it = list.listIterator(); it.hasNext();) {
+ HashMap map = (HashMap)it.next();
+ if ("GoTo".equals(map.get("Action"))) {
+ String page = (String)map.get("Page");
+ if (page != null) {
+ page = page.trim();
+ int idx = page.indexOf(' ');
+ int pageNum;
+ if (idx < 0)
+ pageNum = Integer.parseInt(page);
+ else
+ pageNum = Integer.parseInt(page.substring(0, idx));
+ boolean hit = false;
+ if (pageRange == null)
+ hit = true;
+ else {
+ int len = pageRange.length & 0xfffffffe;
+ for (int k = 0; k < len; k += 2) {
+ if (pageNum >= pageRange[k] && pageNum <= pageRange[k + 1]) {
+ hit = true;
+ break;
+ }
+ }
+ }
+ if (hit) {
+ if (idx < 0)
+ page = (pageNum + pageShift) + "";
+ else
+ page = (pageNum + pageShift) + page.substring(idx);
+ }
+ map.put("Page", page);
+ }
+ }
+ List kids = (List)map.get("Kids");
+ if (kids != null)
+ shiftPageNumbers(kids, pageShift, pageRange);
+ }
+ }
+
+ static void createOutlineAction(PdfDictionary outline, HashMap map, PdfWriter writer, boolean namedAsNames) throws IOException {
+ try {
+ String action = (String)map.get("Action");
+ if ("GoTo".equals(action)) {
+ String p;
+ if ((p = (String)map.get("Named")) != null) {
+ if (namedAsNames)
+ outline.put(PdfName.DEST, new PdfName(p));
+ else
+ outline.put(PdfName.DEST, new PdfString(p, null));
+ }
+ else if ((p = (String)map.get("Page")) != null) {
+ PdfArray ar = new PdfArray();
+ StringTokenizer tk = new StringTokenizer(p);
+ int n = Integer.parseInt(tk.nextToken());
+ ar.add(writer.getPageReference(n));
+ if (!tk.hasMoreTokens()) {
+ ar.add(PdfName.XYZ);
+ ar.add(new float[]{0, 10000, 0});
+ }
+ else {
+ String fn = tk.nextToken();
+ if (fn.startsWith("/"))
+ fn = fn.substring(1);
+ ar.add(new PdfName(fn));
+ for (int k = 0; k < 4 && tk.hasMoreTokens(); ++k) {
+ fn = tk.nextToken();
+ if (fn.equals("null"))
+ ar.add(PdfNull.PDFNULL);
+ else
+ ar.add(new PdfNumber(fn));
+ }
+ }
+ outline.put(PdfName.DEST, ar);
+ }
+ }
+ else if ("GoToR".equals(action)) {
+ String p;
+ PdfDictionary dic = new PdfDictionary();
+ if ((p = (String)map.get("Named")) != null)
+ dic.put(PdfName.D, new PdfString(p, null));
+ else if ((p = (String)map.get("NamedN")) != null)
+ dic.put(PdfName.D, new PdfName(p));
+ else if ((p = (String)map.get("Page")) != null){
+ PdfArray ar = new PdfArray();
+ StringTokenizer tk = new StringTokenizer(p);
+ ar.add(new PdfNumber(tk.nextToken()));
+ if (!tk.hasMoreTokens()) {
+ ar.add(PdfName.XYZ);
+ ar.add(new float[]{0, 10000, 0});
+ }
+ else {
+ String fn = tk.nextToken();
+ if (fn.startsWith("/"))
+ fn = fn.substring(1);
+ ar.add(new PdfName(fn));
+ for (int k = 0; k < 4 && tk.hasMoreTokens(); ++k) {
+ fn = tk.nextToken();
+ if (fn.equals("null"))
+ ar.add(PdfNull.PDFNULL);
+ else
+ ar.add(new PdfNumber(fn));
+ }
+ }
+ dic.put(PdfName.D, ar);
+ }
+ String file = (String)map.get("File");
+ if (dic.size() > 0 && file != null) {
+ dic.put(PdfName.S, PdfName.GOTOR);
+ dic.put(PdfName.F, new PdfString(file));
+ String nw = (String)map.get("NewWindow");
+ if (nw != null) {
+ if (nw.equals("true"))
+ dic.put(PdfName.NEWWINDOW, PdfBoolean.PDFTRUE);
+ else if (nw.equals("false"))
+ dic.put(PdfName.NEWWINDOW, PdfBoolean.PDFFALSE);
+ }
+ outline.put(PdfName.A, dic);
+ }
+ }
+ else if ("URI".equals(action)) {
+ String uri = (String)map.get("URI");
+ if (uri != null) {
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.S, PdfName.URI);
+ dic.put(PdfName.URI, new PdfString(uri));
+ outline.put(PdfName.A, dic);
+ }
+ }
+ else if ("Launch".equals(action)) {
+ String file = (String)map.get("File");
+ if (file != null) {
+ PdfDictionary dic = new PdfDictionary();
+ dic.put(PdfName.S, PdfName.LAUNCH);
+ dic.put(PdfName.F, new PdfString(file));
+ outline.put(PdfName.A, dic);
+ }
+ }
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+
+ public static Object[] iterateOutlines(PdfWriter writer, PdfIndirectReference parent, List kids, boolean namedAsNames) throws IOException {
+ PdfIndirectReference refs[] = new PdfIndirectReference[kids.size()];
+ for (int k = 0; k < refs.length; ++k)
+ refs[k] = writer.getPdfIndirectReference();
+ int ptr = 0;
+ int count = 0;
+ for (Iterator it = kids.listIterator(); it.hasNext(); ++ptr) {
+ HashMap map = (HashMap)it.next();
+ Object lower[] = null;
+ List subKid = (List)map.get("Kids");
+ if (subKid != null && subKid.size() > 0)
+ lower = iterateOutlines(writer, refs[ptr], subKid, namedAsNames);
+ PdfDictionary outline = new PdfDictionary();
+ ++count;
+ if (lower != null) {
+ outline.put(PdfName.FIRST, (PdfIndirectReference)lower[0]);
+ outline.put(PdfName.LAST, (PdfIndirectReference)lower[1]);
+ int n = ((Integer)lower[2]).intValue();
+ if ("false".equals(map.get("Open"))) {
+ outline.put(PdfName.COUNT, new PdfNumber(-n));
+ }
+ else {
+ outline.put(PdfName.COUNT, new PdfNumber(n));
+ count += n;
+ }
+ }
+ outline.put(PdfName.PARENT, parent);
+ if (ptr > 0)
+ outline.put(PdfName.PREV, refs[ptr - 1]);
+ if (ptr < refs.length - 1)
+ outline.put(PdfName.NEXT, refs[ptr + 1]);
+ outline.put(PdfName.TITLE, new PdfString((String)map.get("Title"), PdfObject.TEXT_UNICODE));
+ String color = (String)map.get("Color");
+ if (color != null) {
+ try {
+ PdfArray arr = new PdfArray();
+ StringTokenizer tk = new StringTokenizer(color);
+ for (int k = 0; k < 3; ++k) {
+ float f = Float.valueOf(tk.nextToken()).intValue();
+ if (f < 0) f = 0;
+ if (f > 1) f = 1;
+ arr.add(new PdfNumber(f));
+ }
+ outline.put(PdfName.C, arr);
+ } catch(Exception e){} //in case it's malformed
+ }
+ String style = (String)map.get("Style");
+ if (style != null) {
+ style = style.toLowerCase();
+ int bits = 0;
+ if (style.indexOf("italic") >= 0)
+ bits |= 1;
+ if (style.indexOf("bold") >= 0)
+ bits |= 2;
+ if (bits != 0)
+ outline.put(PdfName.F, new PdfNumber(bits));
+ }
+ createOutlineAction(outline, map, writer, namedAsNames);
+ writer.addToBody(outline, refs[ptr]);
+ }
+ return new Object[]{refs[0], refs[refs.length - 1], new Integer(count)};
+ }
+
+ /**
+ * Exports the bookmarks to XML. Only of use if the generation is to be include in
+ * some other XML document.
+ * @param list the bookmarks
+ * @param out the export destination. The writer is not closed
+ * @param indent the indentation level. Pretty printing significant only
+ * @param onlyASCII codes above 127 will always be escaped with &#nn; if true
,
+ * whatever the encoding
+ * @throws IOException on error
+ */
+ public static void exportToXMLNode(List list, Writer out, int indent, boolean onlyASCII) throws IOException {
+ String dep = "";
+ for (int k = 0; k < indent; ++k)
+ dep += " ";
+ for (Iterator it = list.iterator(); it.hasNext();) {
+ HashMap map = (HashMap)it.next();
+ String title = null;
+ out.write(dep);
+ out.write("
+ * <?xml version='1.0' encoding='UTF-8'?>
+ * <!ELEMENT Title (#PCDATA|Title)*>
+ * <!ATTLIST Title
+ * Action CDATA #IMPLIED
+ * Open CDATA #IMPLIED
+ * Page CDATA #IMPLIED
+ * URI CDATA #IMPLIED
+ * File CDATA #IMPLIED
+ * Named CDATA #IMPLIED
+ * NamedN CDATA #IMPLIED
+ * NewWindow CDATA #IMPLIED
+ * Style CDATA #IMPLIED
+ * Color CDATA #IMPLIED
+ * >
+ * <!ELEMENT Bookmark (Title)*>
+ *
+ * @param list the bookmarks
+ * @param out the export destination. The stream is not closed
+ * @param encoding the encoding according to IANA conventions
+ * @param onlyASCII codes above 127 will always be escaped with &#nn; if true
,
+ * whatever the encoding
+ * @throws IOException on error
+ */
+ public static void exportToXML(List list, OutputStream out, String encoding, boolean onlyASCII) throws IOException {
+ String jenc = SimpleXMLParser.getJavaEncoding(encoding);
+ Writer wrt = new BufferedWriter(new OutputStreamWriter(out, jenc));
+ exportToXML(list, wrt, encoding, onlyASCII);
+ }
+
+ /**
+ * Exports the bookmarks to XML.
+ * @param list the bookmarks
+ * @param wrt the export destination. The writer is not closed
+ * @param encoding the encoding according to IANA conventions
+ * @param onlyASCII codes above 127 will always be escaped with &#nn; if true
,
+ * whatever the encoding
+ * @throws IOException on error
+ */
+ public static void exportToXML(List list, Writer wrt, String encoding, boolean onlyASCII) throws IOException {
+ wrt.write("\n
+ * <?xml version='1.0' encoding='UTF-8'?>
+ * <!ELEMENT Name (#PCDATA)>
+ * <!ATTLIST Name
+ * Page CDATA #IMPLIED
+ * >
+ * <!ELEMENT Destination (Name)*>
+ *
+ * @param names the names
+ * @param out the export destination. The stream is not closed
+ * @param encoding the encoding according to IANA conventions
+ * @param onlyASCII codes above 127 will always be escaped with &#nn; if true
,
+ * whatever the encoding
+ * @throws IOException on error
+ */
+ public static void exportToXML(HashMap names, OutputStream out, String encoding, boolean onlyASCII) throws IOException {
+ String jenc = SimpleXMLParser.getJavaEncoding(encoding);
+ Writer wrt = new BufferedWriter(new OutputStreamWriter(out, jenc));
+ exportToXML(names, wrt, encoding, onlyASCII);
+ }
+
+ /**
+ * Exports the bookmarks to XML.
+ * @param names the names
+ * @param wrt the export destination. The writer is not closed
+ * @param encoding the encoding according to IANA conventions
+ * @param onlyASCII codes above 127 will always be escaped with &#nn; if true
,
+ * whatever the encoding
+ * @throws IOException on error
+ */
+ public static void exportToXML(HashMap names, Writer wrt, String encoding, boolean onlyASCII) throws IOException {
+ wrt.write("\nSimpleXMLParser
.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public interface SimpleXMLDocHandler {
+ /**
+ * Called when a start tag is found.
+ * @param tag the tag name
+ * @param h the tag's attributes
+ */
+ public void startElement(String tag, HashMap h);
+ /**
+ * Called when an end tag is found.
+ * @param tag the tag name
+ */
+ public void endElement(String tag);
+ /**
+ * Called when the document starts to be parsed.
+ */
+ public void startDocument();
+ /**
+ * Called after the document is parsed.
+ */
+ public void endDocument();
+ /**
+ * Called when a text element is found.
+ * @param str the text element, probably a fragment.
+ */
+ public void text(String str);
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/SimpleXMLDocHandlerComment.java b/src/main/java/com/lowagie/text/pdf/SimpleXMLDocHandlerComment.java
new file mode 100644
index 0000000..4d2ceb9
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/SimpleXMLDocHandlerComment.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2003 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+/**
+ * The handler for the events fired by SimpleXMLParser
.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public interface SimpleXMLDocHandlerComment {
+ /**
+ * Called when a comment is found.
+ * @param text the comment text
+ */
+ public void comment(String text);
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/SimpleXMLParser.java b/src/main/java/com/lowagie/text/pdf/SimpleXMLParser.java
new file mode 100644
index 0000000..b00c2d7
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/SimpleXMLParser.java
@@ -0,0 +1,1177 @@
+/*
+ * Copyright 2003 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+import java.io.*;
+import java.util.Stack;
+import java.util.HashMap;
+
+/**
+ * A simple XML and HTML parser. This parser is, like the SAX parser,
+ * an event based parser, but with much less functionality.
+ *
+ *
+ * <[CDATA[ ... ]]>
construct
+ * \r\n
and \r
to \n
on input, in accordance with the XML Specification, Section 2.11
+ * true
+ * @return the escaped string
+ */
+ public static String escapeXML(String s, boolean onlyASCII) {
+ char cc[] = s.toCharArray();
+ int len = cc.length;
+ StringBuffer sb = new StringBuffer();
+ for (int k = 0; k < len; ++k) {
+ int c = cc[k];
+ switch (c) {
+ case '<':
+ sb.append("<");
+ break;
+ case '>':
+ sb.append(">");
+ break;
+ case '&':
+ sb.append("&");
+ break;
+ case '"':
+ sb.append(""");
+ break;
+ case '\'':
+ sb.append("'");
+ break;
+ default:
+ if (onlyASCII && c > 127)
+ sb.append("").append(c).append(";");
+ else
+ sb.append((char)c);
+ }
+ }
+ return sb.toString();
+ }
+
+ public static char decodeEntity(String s) {
+ Character c = (Character)entityMap.get(s);
+ if (c == null)
+ return '\0';
+ else
+ return c.charValue();
+ }
+
+ private static String getEncodingName(byte[] b4) {
+
+ // UTF-16, with BOM
+ int b0 = b4[0] & 0xFF;
+ int b1 = b4[1] & 0xFF;
+ if (b0 == 0xFE && b1 == 0xFF) {
+ // UTF-16, big-endian
+ return "UTF-16BE";
+ }
+ if (b0 == 0xFF && b1 == 0xFE) {
+ // UTF-16, little-endian
+ return "UTF-16LE";
+ }
+
+ // UTF-8 with a BOM
+ int b2 = b4[2] & 0xFF;
+ if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
+ return "UTF-8";
+ }
+
+ // other encodings
+ int b3 = b4[3] & 0xFF;
+ if (b0 == 0x00 && b1 == 0x00 && b2 == 0x00 && b3 == 0x3C) {
+ // UCS-4, big endian (1234)
+ return "ISO-10646-UCS-4";
+ }
+ if (b0 == 0x3C && b1 == 0x00 && b2 == 0x00 && b3 == 0x00) {
+ // UCS-4, little endian (4321)
+ return "ISO-10646-UCS-4";
+ }
+ if (b0 == 0x00 && b1 == 0x00 && b2 == 0x3C && b3 == 0x00) {
+ // UCS-4, unusual octet order (2143)
+ // REVISIT: What should this be?
+ return "ISO-10646-UCS-4";
+ }
+ if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x00) {
+ // UCS-4, unusual octect order (3412)
+ // REVISIT: What should this be?
+ return "ISO-10646-UCS-4";
+ }
+ if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) {
+ // UTF-16, big-endian, no BOM
+ // (or could turn out to be UCS-2...
+ // REVISIT: What should this be?
+ return "UTF-16BE";
+ }
+ if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) {
+ // UTF-16, little-endian, no BOM
+ // (or could turn out to be UCS-2...
+ return "UTF-16LE";
+ }
+ if (b0 == 0x4C && b1 == 0x6F && b2 == 0xA7 && b3 == 0x94) {
+ // EBCDIC
+ // a la xerces1, return CP037 instead of EBCDIC here
+ return "CP037";
+ }
+
+ // default encoding
+ return "UTF-8";
+ }
+
+ static {
+ // add IANA to Java encoding mappings.
+ fIANA2JavaMap.put("BIG5", "Big5");
+ fIANA2JavaMap.put("CSBIG5", "Big5");
+ fIANA2JavaMap.put("CP037", "CP037");
+ fIANA2JavaMap.put("IBM037", "CP037");
+ fIANA2JavaMap.put("CSIBM037", "CP037");
+ fIANA2JavaMap.put("EBCDIC-CP-US", "CP037");
+ fIANA2JavaMap.put("EBCDIC-CP-CA", "CP037");
+ fIANA2JavaMap.put("EBCDIC-CP-NL", "CP037");
+ fIANA2JavaMap.put("EBCDIC-CP-WT", "CP037");
+ fIANA2JavaMap.put("IBM277", "CP277");
+ fIANA2JavaMap.put("CP277", "CP277");
+ fIANA2JavaMap.put("CSIBM277", "CP277");
+ fIANA2JavaMap.put("EBCDIC-CP-DK", "CP277");
+ fIANA2JavaMap.put("EBCDIC-CP-NO", "CP277");
+ fIANA2JavaMap.put("IBM278", "CP278");
+ fIANA2JavaMap.put("CP278", "CP278");
+ fIANA2JavaMap.put("CSIBM278", "CP278");
+ fIANA2JavaMap.put("EBCDIC-CP-FI", "CP278");
+ fIANA2JavaMap.put("EBCDIC-CP-SE", "CP278");
+ fIANA2JavaMap.put("IBM280", "CP280");
+ fIANA2JavaMap.put("CP280", "CP280");
+ fIANA2JavaMap.put("CSIBM280", "CP280");
+ fIANA2JavaMap.put("EBCDIC-CP-IT", "CP280");
+ fIANA2JavaMap.put("IBM284", "CP284");
+ fIANA2JavaMap.put("CP284", "CP284");
+ fIANA2JavaMap.put("CSIBM284", "CP284");
+ fIANA2JavaMap.put("EBCDIC-CP-ES", "CP284");
+ fIANA2JavaMap.put("EBCDIC-CP-GB", "CP285");
+ fIANA2JavaMap.put("IBM285", "CP285");
+ fIANA2JavaMap.put("CP285", "CP285");
+ fIANA2JavaMap.put("CSIBM285", "CP285");
+ fIANA2JavaMap.put("EBCDIC-CP-FR", "CP297");
+ fIANA2JavaMap.put("IBM297", "CP297");
+ fIANA2JavaMap.put("CP297", "CP297");
+ fIANA2JavaMap.put("CSIBM297", "CP297");
+ fIANA2JavaMap.put("EBCDIC-CP-AR1", "CP420");
+ fIANA2JavaMap.put("IBM420", "CP420");
+ fIANA2JavaMap.put("CP420", "CP420");
+ fIANA2JavaMap.put("CSIBM420", "CP420");
+ fIANA2JavaMap.put("EBCDIC-CP-HE", "CP424");
+ fIANA2JavaMap.put("IBM424", "CP424");
+ fIANA2JavaMap.put("CP424", "CP424");
+ fIANA2JavaMap.put("CSIBM424", "CP424");
+ fIANA2JavaMap.put("EBCDIC-CP-CH", "CP500");
+ fIANA2JavaMap.put("IBM500", "CP500");
+ fIANA2JavaMap.put("CP500", "CP500");
+ fIANA2JavaMap.put("CSIBM500", "CP500");
+ fIANA2JavaMap.put("EBCDIC-CP-CH", "CP500");
+ fIANA2JavaMap.put("EBCDIC-CP-BE", "CP500");
+ fIANA2JavaMap.put("IBM868", "CP868");
+ fIANA2JavaMap.put("CP868", "CP868");
+ fIANA2JavaMap.put("CSIBM868", "CP868");
+ fIANA2JavaMap.put("CP-AR", "CP868");
+ fIANA2JavaMap.put("IBM869", "CP869");
+ fIANA2JavaMap.put("CP869", "CP869");
+ fIANA2JavaMap.put("CSIBM869", "CP869");
+ fIANA2JavaMap.put("CP-GR", "CP869");
+ fIANA2JavaMap.put("IBM870", "CP870");
+ fIANA2JavaMap.put("CP870", "CP870");
+ fIANA2JavaMap.put("CSIBM870", "CP870");
+ fIANA2JavaMap.put("EBCDIC-CP-ROECE", "CP870");
+ fIANA2JavaMap.put("EBCDIC-CP-YU", "CP870");
+ fIANA2JavaMap.put("IBM871", "CP871");
+ fIANA2JavaMap.put("CP871", "CP871");
+ fIANA2JavaMap.put("CSIBM871", "CP871");
+ fIANA2JavaMap.put("EBCDIC-CP-IS", "CP871");
+ fIANA2JavaMap.put("IBM918", "CP918");
+ fIANA2JavaMap.put("CP918", "CP918");
+ fIANA2JavaMap.put("CSIBM918", "CP918");
+ fIANA2JavaMap.put("EBCDIC-CP-AR2", "CP918");
+ fIANA2JavaMap.put("EUC-JP", "EUCJIS");
+ fIANA2JavaMap.put("CSEUCPkdFmtJapanese", "EUCJIS");
+ fIANA2JavaMap.put("EUC-KR", "KSC5601");
+ fIANA2JavaMap.put("GB2312", "GB2312");
+ fIANA2JavaMap.put("CSGB2312", "GB2312");
+ fIANA2JavaMap.put("ISO-2022-JP", "JIS");
+ fIANA2JavaMap.put("CSISO2022JP", "JIS");
+ fIANA2JavaMap.put("ISO-2022-KR", "ISO2022KR");
+ fIANA2JavaMap.put("CSISO2022KR", "ISO2022KR");
+ fIANA2JavaMap.put("ISO-2022-CN", "ISO2022CN");
+
+ fIANA2JavaMap.put("X0201", "JIS0201");
+ fIANA2JavaMap.put("CSISO13JISC6220JP", "JIS0201");
+ fIANA2JavaMap.put("X0208", "JIS0208");
+ fIANA2JavaMap.put("ISO-IR-87", "JIS0208");
+ fIANA2JavaMap.put("X0208dbiJIS_X0208-1983", "JIS0208");
+ fIANA2JavaMap.put("CSISO87JISX0208", "JIS0208");
+ fIANA2JavaMap.put("X0212", "JIS0212");
+ fIANA2JavaMap.put("ISO-IR-159", "JIS0212");
+ fIANA2JavaMap.put("CSISO159JISX02121990", "JIS0212");
+ fIANA2JavaMap.put("SHIFT_JIS", "SJIS");
+ fIANA2JavaMap.put("CSSHIFT_JIS", "SJIS");
+ fIANA2JavaMap.put("MS_Kanji", "SJIS");
+
+ // Add support for Cp1252 and its friends
+ fIANA2JavaMap.put("WINDOWS-1250", "Cp1250");
+ fIANA2JavaMap.put("WINDOWS-1251", "Cp1251");
+ fIANA2JavaMap.put("WINDOWS-1252", "Cp1252");
+ fIANA2JavaMap.put("WINDOWS-1253", "Cp1253");
+ fIANA2JavaMap.put("WINDOWS-1254", "Cp1254");
+ fIANA2JavaMap.put("WINDOWS-1255", "Cp1255");
+ fIANA2JavaMap.put("WINDOWS-1256", "Cp1256");
+ fIANA2JavaMap.put("WINDOWS-1257", "Cp1257");
+ fIANA2JavaMap.put("WINDOWS-1258", "Cp1258");
+ fIANA2JavaMap.put("TIS-620", "TIS620");
+
+ fIANA2JavaMap.put("ISO-8859-1", "ISO8859_1");
+ fIANA2JavaMap.put("ISO-IR-100", "ISO8859_1");
+ fIANA2JavaMap.put("ISO_8859-1", "ISO8859_1");
+ fIANA2JavaMap.put("LATIN1", "ISO8859_1");
+ fIANA2JavaMap.put("CSISOLATIN1", "ISO8859_1");
+ fIANA2JavaMap.put("L1", "ISO8859_1");
+ fIANA2JavaMap.put("IBM819", "ISO8859_1");
+ fIANA2JavaMap.put("CP819", "ISO8859_1");
+
+ fIANA2JavaMap.put("ISO-8859-2", "ISO8859_2");
+ fIANA2JavaMap.put("ISO-IR-101", "ISO8859_2");
+ fIANA2JavaMap.put("ISO_8859-2", "ISO8859_2");
+ fIANA2JavaMap.put("LATIN2", "ISO8859_2");
+ fIANA2JavaMap.put("CSISOLATIN2", "ISO8859_2");
+ fIANA2JavaMap.put("L2", "ISO8859_2");
+
+ fIANA2JavaMap.put("ISO-8859-3", "ISO8859_3");
+ fIANA2JavaMap.put("ISO-IR-109", "ISO8859_3");
+ fIANA2JavaMap.put("ISO_8859-3", "ISO8859_3");
+ fIANA2JavaMap.put("LATIN3", "ISO8859_3");
+ fIANA2JavaMap.put("CSISOLATIN3", "ISO8859_3");
+ fIANA2JavaMap.put("L3", "ISO8859_3");
+
+ fIANA2JavaMap.put("ISO-8859-4", "ISO8859_4");
+ fIANA2JavaMap.put("ISO-IR-110", "ISO8859_4");
+ fIANA2JavaMap.put("ISO_8859-4", "ISO8859_4");
+ fIANA2JavaMap.put("LATIN4", "ISO8859_4");
+ fIANA2JavaMap.put("CSISOLATIN4", "ISO8859_4");
+ fIANA2JavaMap.put("L4", "ISO8859_4");
+
+ fIANA2JavaMap.put("ISO-8859-5", "ISO8859_5");
+ fIANA2JavaMap.put("ISO-IR-144", "ISO8859_5");
+ fIANA2JavaMap.put("ISO_8859-5", "ISO8859_5");
+ fIANA2JavaMap.put("CYRILLIC", "ISO8859_5");
+ fIANA2JavaMap.put("CSISOLATINCYRILLIC", "ISO8859_5");
+
+ fIANA2JavaMap.put("ISO-8859-6", "ISO8859_6");
+ fIANA2JavaMap.put("ISO-IR-127", "ISO8859_6");
+ fIANA2JavaMap.put("ISO_8859-6", "ISO8859_6");
+ fIANA2JavaMap.put("ECMA-114", "ISO8859_6");
+ fIANA2JavaMap.put("ASMO-708", "ISO8859_6");
+ fIANA2JavaMap.put("ARABIC", "ISO8859_6");
+ fIANA2JavaMap.put("CSISOLATINARABIC", "ISO8859_6");
+
+ fIANA2JavaMap.put("ISO-8859-7", "ISO8859_7");
+ fIANA2JavaMap.put("ISO-IR-126", "ISO8859_7");
+ fIANA2JavaMap.put("ISO_8859-7", "ISO8859_7");
+ fIANA2JavaMap.put("ELOT_928", "ISO8859_7");
+ fIANA2JavaMap.put("ECMA-118", "ISO8859_7");
+ fIANA2JavaMap.put("GREEK", "ISO8859_7");
+ fIANA2JavaMap.put("CSISOLATINGREEK", "ISO8859_7");
+ fIANA2JavaMap.put("GREEK8", "ISO8859_7");
+
+ fIANA2JavaMap.put("ISO-8859-8", "ISO8859_8");
+ fIANA2JavaMap.put("ISO-8859-8-I", "ISO8859_8"); // added since this encoding only differs w.r.t. presentation
+ fIANA2JavaMap.put("ISO-IR-138", "ISO8859_8");
+ fIANA2JavaMap.put("ISO_8859-8", "ISO8859_8");
+ fIANA2JavaMap.put("HEBREW", "ISO8859_8");
+ fIANA2JavaMap.put("CSISOLATINHEBREW", "ISO8859_8");
+
+ fIANA2JavaMap.put("ISO-8859-9", "ISO8859_9");
+ fIANA2JavaMap.put("ISO-IR-148", "ISO8859_9");
+ fIANA2JavaMap.put("ISO_8859-9", "ISO8859_9");
+ fIANA2JavaMap.put("LATIN5", "ISO8859_9");
+ fIANA2JavaMap.put("CSISOLATIN5", "ISO8859_9");
+ fIANA2JavaMap.put("L5", "ISO8859_9");
+
+ fIANA2JavaMap.put("KOI8-R", "KOI8_R");
+ fIANA2JavaMap.put("CSKOI8-R", "KOI8_R");
+ fIANA2JavaMap.put("US-ASCII", "ASCII");
+ fIANA2JavaMap.put("ISO-IR-6", "ASCII");
+ fIANA2JavaMap.put("ANSI_X3.4-1986", "ASCII");
+ fIANA2JavaMap.put("ISO_646.IRV:1991", "ASCII");
+ fIANA2JavaMap.put("ASCII", "ASCII");
+ fIANA2JavaMap.put("CSASCII", "ASCII");
+ fIANA2JavaMap.put("ISO646-US", "ASCII");
+ fIANA2JavaMap.put("US", "ASCII");
+ fIANA2JavaMap.put("IBM367", "ASCII");
+ fIANA2JavaMap.put("CP367", "ASCII");
+ fIANA2JavaMap.put("UTF-8", "UTF8");
+ fIANA2JavaMap.put("UTF-16", "Unicode");
+ fIANA2JavaMap.put("UTF-16BE", "UnicodeBig");
+ fIANA2JavaMap.put("UTF-16LE", "UnicodeLittle");
+
+ entityMap.put("nbsp", new Character('\u00a0')); // no-break space = non-breaking space, U+00A0 ISOnum
+ entityMap.put("iexcl", new Character('\u00a1')); // inverted exclamation mark, U+00A1 ISOnum
+ entityMap.put("cent", new Character('\u00a2')); // cent sign, U+00A2 ISOnum
+ entityMap.put("pound", new Character('\u00a3')); // pound sign, U+00A3 ISOnum
+ entityMap.put("curren", new Character('\u00a4')); // currency sign, U+00A4 ISOnum
+ entityMap.put("yen", new Character('\u00a5')); // yen sign = yuan sign, U+00A5 ISOnum
+ entityMap.put("brvbar", new Character('\u00a6')); // broken bar = broken vertical bar, U+00A6 ISOnum
+ entityMap.put("sect", new Character('\u00a7')); // section sign, U+00A7 ISOnum
+ entityMap.put("uml", new Character('\u00a8')); // diaeresis = spacing diaeresis, U+00A8 ISOdia
+ entityMap.put("copy", new Character('\u00a9')); // copyright sign, U+00A9 ISOnum
+ entityMap.put("ordf", new Character('\u00aa')); // feminine ordinal indicator, U+00AA ISOnum
+ entityMap.put("laquo", new Character('\u00ab')); // left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum
+ entityMap.put("not", new Character('\u00ac')); // not sign, U+00AC ISOnum
+ entityMap.put("shy", new Character('\u00ad')); // soft hyphen = discretionary hyphen, U+00AD ISOnum
+ entityMap.put("reg", new Character('\u00ae')); // registered sign = registered trade mark sign, U+00AE ISOnum
+ entityMap.put("macr", new Character('\u00af')); // macron = spacing macron = overline = APL overbar, U+00AF ISOdia
+ entityMap.put("deg", new Character('\u00b0')); // degree sign, U+00B0 ISOnum
+ entityMap.put("plusmn", new Character('\u00b1')); // plus-minus sign = plus-or-minus sign, U+00B1 ISOnum
+ entityMap.put("sup2", new Character('\u00b2')); // superscript two = superscript digit two = squared, U+00B2 ISOnum
+ entityMap.put("sup3", new Character('\u00b3')); // superscript three = superscript digit three = cubed, U+00B3 ISOnum
+ entityMap.put("acute", new Character('\u00b4')); // acute accent = spacing acute, U+00B4 ISOdia
+ entityMap.put("micro", new Character('\u00b5')); // micro sign, U+00B5 ISOnum
+ entityMap.put("para", new Character('\u00b6')); // pilcrow sign = paragraph sign, U+00B6 ISOnum
+ entityMap.put("middot", new Character('\u00b7')); // middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum
+ entityMap.put("cedil", new Character('\u00b8')); // cedilla = spacing cedilla, U+00B8 ISOdia
+ entityMap.put("sup1", new Character('\u00b9')); // superscript one = superscript digit one, U+00B9 ISOnum
+ entityMap.put("ordm", new Character('\u00ba')); // masculine ordinal indicator, U+00BA ISOnum
+ entityMap.put("raquo", new Character('\u00bb')); // right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum
+ entityMap.put("frac14", new Character('\u00bc')); // vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum
+ entityMap.put("frac12", new Character('\u00bd')); // vulgar fraction one half = fraction one half, U+00BD ISOnum
+ entityMap.put("frac34", new Character('\u00be')); // vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum
+ entityMap.put("iquest", new Character('\u00bf')); // inverted question mark = turned question mark, U+00BF ISOnum
+ entityMap.put("Agrave", new Character('\u00c0')); // latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1
+ entityMap.put("Aacute", new Character('\u00c1')); // latin capital letter A with acute, U+00C1 ISOlat1
+ entityMap.put("Acirc", new Character('\u00c2')); // latin capital letter A with circumflex, U+00C2 ISOlat1
+ entityMap.put("Atilde", new Character('\u00c3')); // latin capital letter A with tilde, U+00C3 ISOlat1
+ entityMap.put("Auml", new Character('\u00c4')); // latin capital letter A with diaeresis, U+00C4 ISOlat1
+ entityMap.put("Aring", new Character('\u00c5')); // latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1
+ entityMap.put("AElig", new Character('\u00c6')); // latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1
+ entityMap.put("Ccedil", new Character('\u00c7')); // latin capital letter C with cedilla, U+00C7 ISOlat1
+ entityMap.put("Egrave", new Character('\u00c8')); // latin capital letter E with grave, U+00C8 ISOlat1
+ entityMap.put("Eacute", new Character('\u00c9')); // latin capital letter E with acute, U+00C9 ISOlat1
+ entityMap.put("Ecirc", new Character('\u00ca')); // latin capital letter E with circumflex, U+00CA ISOlat1
+ entityMap.put("Euml", new Character('\u00cb')); // latin capital letter E with diaeresis, U+00CB ISOlat1
+ entityMap.put("Igrave", new Character('\u00cc')); // latin capital letter I with grave, U+00CC ISOlat1
+ entityMap.put("Iacute", new Character('\u00cd')); // latin capital letter I with acute, U+00CD ISOlat1
+ entityMap.put("Icirc", new Character('\u00ce')); // latin capital letter I with circumflex, U+00CE ISOlat1
+ entityMap.put("Iuml", new Character('\u00cf')); // latin capital letter I with diaeresis, U+00CF ISOlat1
+ entityMap.put("ETH", new Character('\u00d0')); // latin capital letter ETH, U+00D0 ISOlat1
+ entityMap.put("Ntilde", new Character('\u00d1')); // latin capital letter N with tilde, U+00D1 ISOlat1
+ entityMap.put("Ograve", new Character('\u00d2')); // latin capital letter O with grave, U+00D2 ISOlat1
+ entityMap.put("Oacute", new Character('\u00d3')); // latin capital letter O with acute, U+00D3 ISOlat1
+ entityMap.put("Ocirc", new Character('\u00d4')); // latin capital letter O with circumflex, U+00D4 ISOlat1
+ entityMap.put("Otilde", new Character('\u00d5')); // latin capital letter O with tilde, U+00D5 ISOlat1
+ entityMap.put("Ouml", new Character('\u00d6')); // latin capital letter O with diaeresis, U+00D6 ISOlat1
+ entityMap.put("times", new Character('\u00d7')); // multiplication sign, U+00D7 ISOnum
+ entityMap.put("Oslash", new Character('\u00d8')); // latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1
+ entityMap.put("Ugrave", new Character('\u00d9')); // latin capital letter U with grave, U+00D9 ISOlat1
+ entityMap.put("Uacute", new Character('\u00da')); // latin capital letter U with acute, U+00DA ISOlat1
+ entityMap.put("Ucirc", new Character('\u00db')); // latin capital letter U with circumflex, U+00DB ISOlat1
+ entityMap.put("Uuml", new Character('\u00dc')); // latin capital letter U with diaeresis, U+00DC ISOlat1
+ entityMap.put("Yacute", new Character('\u00dd')); // latin capital letter Y with acute, U+00DD ISOlat1
+ entityMap.put("THORN", new Character('\u00de')); // latin capital letter THORN, U+00DE ISOlat1
+ entityMap.put("szlig", new Character('\u00df')); // latin small letter sharp s = ess-zed, U+00DF ISOlat1
+ entityMap.put("agrave", new Character('\u00e0')); // latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1
+ entityMap.put("aacute", new Character('\u00e1')); // latin small letter a with acute, U+00E1 ISOlat1
+ entityMap.put("acirc", new Character('\u00e2')); // latin small letter a with circumflex, U+00E2 ISOlat1
+ entityMap.put("atilde", new Character('\u00e3')); // latin small letter a with tilde, U+00E3 ISOlat1
+ entityMap.put("auml", new Character('\u00e4')); // latin small letter a with diaeresis, U+00E4 ISOlat1
+ entityMap.put("aring", new Character('\u00e5')); // latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1
+ entityMap.put("aelig", new Character('\u00e6')); // latin small letter ae = latin small ligature ae, U+00E6 ISOlat1
+ entityMap.put("ccedil", new Character('\u00e7')); // latin small letter c with cedilla, U+00E7 ISOlat1
+ entityMap.put("egrave", new Character('\u00e8')); // latin small letter e with grave, U+00E8 ISOlat1
+ entityMap.put("eacute", new Character('\u00e9')); // latin small letter e with acute, U+00E9 ISOlat1
+ entityMap.put("ecirc", new Character('\u00ea')); // latin small letter e with circumflex, U+00EA ISOlat1
+ entityMap.put("euml", new Character('\u00eb')); // latin small letter e with diaeresis, U+00EB ISOlat1
+ entityMap.put("igrave", new Character('\u00ec')); // latin small letter i with grave, U+00EC ISOlat1
+ entityMap.put("iacute", new Character('\u00ed')); // latin small letter i with acute, U+00ED ISOlat1
+ entityMap.put("icirc", new Character('\u00ee')); // latin small letter i with circumflex, U+00EE ISOlat1
+ entityMap.put("iuml", new Character('\u00ef')); // latin small letter i with diaeresis, U+00EF ISOlat1
+ entityMap.put("eth", new Character('\u00f0')); // latin small letter eth, U+00F0 ISOlat1
+ entityMap.put("ntilde", new Character('\u00f1')); // latin small letter n with tilde, U+00F1 ISOlat1
+ entityMap.put("ograve", new Character('\u00f2')); // latin small letter o with grave, U+00F2 ISOlat1
+ entityMap.put("oacute", new Character('\u00f3')); // latin small letter o with acute, U+00F3 ISOlat1
+ entityMap.put("ocirc", new Character('\u00f4')); // latin small letter o with circumflex, U+00F4 ISOlat1
+ entityMap.put("otilde", new Character('\u00f5')); // latin small letter o with tilde, U+00F5 ISOlat1
+ entityMap.put("ouml", new Character('\u00f6')); // latin small letter o with diaeresis, U+00F6 ISOlat1
+ entityMap.put("divide", new Character('\u00f7')); // division sign, U+00F7 ISOnum
+ entityMap.put("oslash", new Character('\u00f8')); // latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1
+ entityMap.put("ugrave", new Character('\u00f9')); // latin small letter u with grave, U+00F9 ISOlat1
+ entityMap.put("uacute", new Character('\u00fa')); // latin small letter u with acute, U+00FA ISOlat1
+ entityMap.put("ucirc", new Character('\u00fb')); // latin small letter u with circumflex, U+00FB ISOlat1
+ entityMap.put("uuml", new Character('\u00fc')); // latin small letter u with diaeresis, U+00FC ISOlat1
+ entityMap.put("yacute", new Character('\u00fd')); // latin small letter y with acute, U+00FD ISOlat1
+ entityMap.put("thorn", new Character('\u00fe')); // latin small letter thorn, U+00FE ISOlat1
+ entityMap.put("yuml", new Character('\u00ff')); // latin small letter y with diaeresis, U+00FF ISOlat1
+ // Latin Extended-B
+ entityMap.put("fnof", new Character('\u0192')); // latin small f with hook = function = florin, U+0192 ISOtech
+ // Greek
+ entityMap.put("Alpha", new Character('\u0391')); // greek capital letter alpha, U+0391
+ entityMap.put("Beta", new Character('\u0392')); // greek capital letter beta, U+0392
+ entityMap.put("Gamma", new Character('\u0393')); // greek capital letter gamma, U+0393 ISOgrk3
+ entityMap.put("Delta", new Character('\u0394')); // greek capital letter delta, U+0394 ISOgrk3
+ entityMap.put("Epsilon", new Character('\u0395')); // greek capital letter epsilon, U+0395
+ entityMap.put("Zeta", new Character('\u0396')); // greek capital letter zeta, U+0396
+ entityMap.put("Eta", new Character('\u0397')); // greek capital letter eta, U+0397
+ entityMap.put("Theta", new Character('\u0398')); // greek capital letter theta, U+0398 ISOgrk3
+ entityMap.put("Iota", new Character('\u0399')); // greek capital letter iota, U+0399
+ entityMap.put("Kappa", new Character('\u039a')); // greek capital letter kappa, U+039A
+ entityMap.put("Lambda", new Character('\u039b')); // greek capital letter lambda, U+039B ISOgrk3
+ entityMap.put("Mu", new Character('\u039c')); // greek capital letter mu, U+039C
+ entityMap.put("Nu", new Character('\u039d')); // greek capital letter nu, U+039D
+ entityMap.put("Xi", new Character('\u039e')); // greek capital letter xi, U+039E ISOgrk3
+ entityMap.put("Omicron", new Character('\u039f')); // greek capital letter omicron, U+039F
+ entityMap.put("Pi", new Character('\u03a0')); // greek capital letter pi, U+03A0 ISOgrk3
+ entityMap.put("Rho", new Character('\u03a1')); // greek capital letter rho, U+03A1
+ // there is no Sigmaf, and no U+03A2 character either
+ entityMap.put("Sigma", new Character('\u03a3')); // greek capital letter sigma, U+03A3 ISOgrk3
+ entityMap.put("Tau", new Character('\u03a4')); // greek capital letter tau, U+03A4
+ entityMap.put("Upsilon", new Character('\u03a5')); // greek capital letter upsilon, U+03A5 ISOgrk3
+ entityMap.put("Phi", new Character('\u03a6')); // greek capital letter phi, U+03A6 ISOgrk3
+ entityMap.put("Chi", new Character('\u03a7')); // greek capital letter chi, U+03A7
+ entityMap.put("Psi", new Character('\u03a8')); // greek capital letter psi, U+03A8 ISOgrk3
+ entityMap.put("Omega", new Character('\u03a9')); // greek capital letter omega, U+03A9 ISOgrk3
+ entityMap.put("alpha", new Character('\u03b1')); // greek small letter alpha, U+03B1 ISOgrk3
+ entityMap.put("beta", new Character('\u03b2')); // greek small letter beta, U+03B2 ISOgrk3
+ entityMap.put("gamma", new Character('\u03b3')); // greek small letter gamma, U+03B3 ISOgrk3
+ entityMap.put("delta", new Character('\u03b4')); // greek small letter delta, U+03B4 ISOgrk3
+ entityMap.put("epsilon", new Character('\u03b5')); // greek small letter epsilon, U+03B5 ISOgrk3
+ entityMap.put("zeta", new Character('\u03b6')); // greek small letter zeta, U+03B6 ISOgrk3
+ entityMap.put("eta", new Character('\u03b7')); // greek small letter eta, U+03B7 ISOgrk3
+ entityMap.put("theta", new Character('\u03b8')); // greek small letter theta, U+03B8 ISOgrk3
+ entityMap.put("iota", new Character('\u03b9')); // greek small letter iota, U+03B9 ISOgrk3
+ entityMap.put("kappa", new Character('\u03ba')); // greek small letter kappa, U+03BA ISOgrk3
+ entityMap.put("lambda", new Character('\u03bb')); // greek small letter lambda, U+03BB ISOgrk3
+ entityMap.put("mu", new Character('\u03bc')); // greek small letter mu, U+03BC ISOgrk3
+ entityMap.put("nu", new Character('\u03bd')); // greek small letter nu, U+03BD ISOgrk3
+ entityMap.put("xi", new Character('\u03be')); // greek small letter xi, U+03BE ISOgrk3
+ entityMap.put("omicron", new Character('\u03bf')); // greek small letter omicron, U+03BF NEW
+ entityMap.put("pi", new Character('\u03c0')); // greek small letter pi, U+03C0 ISOgrk3
+ entityMap.put("rho", new Character('\u03c1')); // greek small letter rho, U+03C1 ISOgrk3
+ entityMap.put("sigmaf", new Character('\u03c2')); // greek small letter final sigma, U+03C2 ISOgrk3
+ entityMap.put("sigma", new Character('\u03c3')); // greek small letter sigma, U+03C3 ISOgrk3
+ entityMap.put("tau", new Character('\u03c4')); // greek small letter tau, U+03C4 ISOgrk3
+ entityMap.put("upsilon", new Character('\u03c5')); // greek small letter upsilon, U+03C5 ISOgrk3
+ entityMap.put("phi", new Character('\u03c6')); // greek small letter phi, U+03C6 ISOgrk3
+ entityMap.put("chi", new Character('\u03c7')); // greek small letter chi, U+03C7 ISOgrk3
+ entityMap.put("psi", new Character('\u03c8')); // greek small letter psi, U+03C8 ISOgrk3
+ entityMap.put("omega", new Character('\u03c9')); // greek small letter omega, U+03C9 ISOgrk3
+ entityMap.put("thetasym", new Character('\u03d1')); // greek small letter theta symbol, U+03D1 NEW
+ entityMap.put("upsih", new Character('\u03d2')); // greek upsilon with hook symbol, U+03D2 NEW
+ entityMap.put("piv", new Character('\u03d6')); // greek pi symbol, U+03D6 ISOgrk3
+ // General Punctuation
+ entityMap.put("bull", new Character('\u2022')); // bullet = black small circle, U+2022 ISOpub
+ // bullet is NOT the same as bullet operator, U+2219
+ entityMap.put("hellip", new Character('\u2026')); // horizontal ellipsis = three dot leader, U+2026 ISOpub
+ entityMap.put("prime", new Character('\u2032')); // prime = minutes = feet, U+2032 ISOtech
+ entityMap.put("Prime", new Character('\u2033')); // double prime = seconds = inches, U+2033 ISOtech
+ entityMap.put("oline", new Character('\u203e')); // overline = spacing overscore, U+203E NEW
+ entityMap.put("frasl", new Character('\u2044')); // fraction slash, U+2044 NEW
+ // Letterlike Symbols
+ entityMap.put("weierp", new Character('\u2118')); // script capital P = power set = Weierstrass p, U+2118 ISOamso
+ entityMap.put("image", new Character('\u2111')); // blackletter capital I = imaginary part, U+2111 ISOamso
+ entityMap.put("real", new Character('\u211c')); // blackletter capital R = real part symbol, U+211C ISOamso
+ entityMap.put("trade", new Character('\u2122')); // trade mark sign, U+2122 ISOnum
+ entityMap.put("alefsym", new Character('\u2135')); // alef symbol = first transfinite cardinal, U+2135 NEW
+ // alef symbol is NOT the same as hebrew letter alef,
+ // U+05D0 although the same glyph could be used to depict both characters
+ // Arrows
+ entityMap.put("larr", new Character('\u2190')); // leftwards arrow, U+2190 ISOnum
+ entityMap.put("uarr", new Character('\u2191')); // upwards arrow, U+2191 ISOnum
+ entityMap.put("rarr", new Character('\u2192')); // rightwards arrow, U+2192 ISOnum
+ entityMap.put("darr", new Character('\u2193')); // downwards arrow, U+2193 ISOnum
+ entityMap.put("harr", new Character('\u2194')); // left right arrow, U+2194 ISOamsa
+ entityMap.put("crarr", new Character('\u21b5')); // downwards arrow with corner leftwards = carriage return, U+21B5 NEW
+ entityMap.put("lArr", new Character('\u21d0')); // leftwards double arrow, U+21D0 ISOtech
+ // ISO 10646 does not say that lArr is the same as the 'is implied by' arrow
+ // but also does not have any other character for that function. So ? lArr can
+ // be used for 'is implied by' as ISOtech suggests
+ entityMap.put("uArr", new Character('\u21d1')); // upwards double arrow, U+21D1 ISOamsa
+ entityMap.put("rArr", new Character('\u21d2')); // rightwards double arrow, U+21D2 ISOtech
+ // ISO 10646 does not say this is the 'implies' character but does not have
+ // another character with this function so ?
+ // rArr can be used for 'implies' as ISOtech suggests
+ entityMap.put("dArr", new Character('\u21d3')); // downwards double arrow, U+21D3 ISOamsa
+ entityMap.put("hArr", new Character('\u21d4')); // left right double arrow, U+21D4 ISOamsa
+ // Mathematical Operators
+ entityMap.put("forall", new Character('\u2200')); // for all, U+2200 ISOtech
+ entityMap.put("part", new Character('\u2202')); // partial differential, U+2202 ISOtech
+ entityMap.put("exist", new Character('\u2203')); // there exists, U+2203 ISOtech
+ entityMap.put("empty", new Character('\u2205')); // empty set = null set = diameter, U+2205 ISOamso
+ entityMap.put("nabla", new Character('\u2207')); // nabla = backward difference, U+2207 ISOtech
+ entityMap.put("isin", new Character('\u2208')); // element of, U+2208 ISOtech
+ entityMap.put("notin", new Character('\u2209')); // not an element of, U+2209 ISOtech
+ entityMap.put("ni", new Character('\u220b')); // contains as member, U+220B ISOtech
+ // should there be a more memorable name than 'ni'?
+ entityMap.put("prod", new Character('\u220f')); // n-ary product = product sign, U+220F ISOamsb
+ // prod is NOT the same character as U+03A0 'greek capital letter pi' though
+ // the same glyph might be used for both
+ entityMap.put("sum", new Character('\u2211')); // n-ary sumation, U+2211 ISOamsb
+ // sum is NOT the same character as U+03A3 'greek capital letter sigma'
+ // though the same glyph might be used for both
+ entityMap.put("minus", new Character('\u2212')); // minus sign, U+2212 ISOtech
+ entityMap.put("lowast", new Character('\u2217')); // asterisk operator, U+2217 ISOtech
+ entityMap.put("radic", new Character('\u221a')); // square root = radical sign, U+221A ISOtech
+ entityMap.put("prop", new Character('\u221d')); // proportional to, U+221D ISOtech
+ entityMap.put("infin", new Character('\u221e')); // infinity, U+221E ISOtech
+ entityMap.put("ang", new Character('\u2220')); // angle, U+2220 ISOamso
+ entityMap.put("and", new Character('\u2227')); // logical and = wedge, U+2227 ISOtech
+ entityMap.put("or", new Character('\u2228')); // logical or = vee, U+2228 ISOtech
+ entityMap.put("cap", new Character('\u2229')); // intersection = cap, U+2229 ISOtech
+ entityMap.put("cup", new Character('\u222a')); // union = cup, U+222A ISOtech
+ entityMap.put("int", new Character('\u222b')); // integral, U+222B ISOtech
+ entityMap.put("there4", new Character('\u2234')); // therefore, U+2234 ISOtech
+ entityMap.put("sim", new Character('\u223c')); // tilde operator = varies with = similar to, U+223C ISOtech
+ // tilde operator is NOT the same character as the tilde, U+007E,
+ // although the same glyph might be used to represent both
+ entityMap.put("cong", new Character('\u2245')); // approximately equal to, U+2245 ISOtech
+ entityMap.put("asymp", new Character('\u2248')); // almost equal to = asymptotic to, U+2248 ISOamsr
+ entityMap.put("ne", new Character('\u2260')); // not equal to, U+2260 ISOtech
+ entityMap.put("equiv", new Character('\u2261')); // identical to, U+2261 ISOtech
+ entityMap.put("le", new Character('\u2264')); // less-than or equal to, U+2264 ISOtech
+ entityMap.put("ge", new Character('\u2265')); // greater-than or equal to, U+2265 ISOtech
+ entityMap.put("sub", new Character('\u2282')); // subset of, U+2282 ISOtech
+ entityMap.put("sup", new Character('\u2283')); // superset of, U+2283 ISOtech
+ // note that nsup, 'not a superset of, U+2283' is not covered by the Symbol
+ // font encoding and is not included. Should it be, for symmetry?
+ // It is in ISOamsn
+ entityMap.put("nsub", new Character('\u2284')); // not a subset of, U+2284 ISOamsn
+ entityMap.put("sube", new Character('\u2286')); // subset of or equal to, U+2286 ISOtech
+ entityMap.put("supe", new Character('\u2287')); // superset of or equal to, U+2287 ISOtech
+ entityMap.put("oplus", new Character('\u2295')); // circled plus = direct sum, U+2295 ISOamsb
+ entityMap.put("otimes", new Character('\u2297')); // circled times = vector product, U+2297 ISOamsb
+ entityMap.put("perp", new Character('\u22a5')); // up tack = orthogonal to = perpendicular, U+22A5 ISOtech
+ entityMap.put("sdot", new Character('\u22c5')); // dot operator, U+22C5 ISOamsb
+ // dot operator is NOT the same character as U+00B7 middle dot
+ // Miscellaneous Technical
+ entityMap.put("lceil", new Character('\u2308')); // left ceiling = apl upstile, U+2308 ISOamsc
+ entityMap.put("rceil", new Character('\u2309')); // right ceiling, U+2309 ISOamsc
+ entityMap.put("lfloor", new Character('\u230a')); // left floor = apl downstile, U+230A ISOamsc
+ entityMap.put("rfloor", new Character('\u230b')); // right floor, U+230B ISOamsc
+ entityMap.put("lang", new Character('\u2329')); // left-pointing angle bracket = bra, U+2329 ISOtech
+ // lang is NOT the same character as U+003C 'less than'
+ // or U+2039 'single left-pointing angle quotation mark'
+ entityMap.put("rang", new Character('\u232a')); // right-pointing angle bracket = ket, U+232A ISOtech
+ // rang is NOT the same character as U+003E 'greater than'
+ // or U+203A 'single right-pointing angle quotation mark'
+ // Geometric Shapes
+ entityMap.put("loz", new Character('\u25ca')); // lozenge, U+25CA ISOpub
+ // Miscellaneous Symbols
+ entityMap.put("spades", new Character('\u2660')); // black spade suit, U+2660 ISOpub
+ // black here seems to mean filled as opposed to hollow
+ entityMap.put("clubs", new Character('\u2663')); // black club suit = shamrock, U+2663 ISOpub
+ entityMap.put("hearts", new Character('\u2665')); // black heart suit = valentine, U+2665 ISOpub
+ entityMap.put("diams", new Character('\u2666')); // black diamond suit, U+2666 ISOpub
+ // C0 Controls and Basic Latin
+ entityMap.put("quot", new Character('\u0022')); // quotation mark = APL quote, U+0022 ISOnum
+ entityMap.put("amp", new Character('\u0026')); // ampersand, U+0026 ISOnum
+ entityMap.put("apos", new Character('\''));
+ entityMap.put("lt", new Character('\u003c')); // less-than sign, U+003C ISOnum
+ entityMap.put("gt", new Character('\u003e')); // greater-than sign, U+003E ISOnum
+ // Latin Extended-A
+ entityMap.put("OElig", new Character('\u0152')); // latin capital ligature OE, U+0152 ISOlat2
+ entityMap.put("oelig", new Character('\u0153')); // latin small ligature oe, U+0153 ISOlat2
+ // ligature is a misnomer, this is a separate character in some languages
+ entityMap.put("Scaron", new Character('\u0160')); // latin capital letter S with caron, U+0160 ISOlat2
+ entityMap.put("scaron", new Character('\u0161')); // latin small letter s with caron, U+0161 ISOlat2
+ entityMap.put("Yuml", new Character('\u0178')); // latin capital letter Y with diaeresis, U+0178 ISOlat2
+ // Spacing Modifier Letters
+ entityMap.put("circ", new Character('\u02c6')); // modifier letter circumflex accent, U+02C6 ISOpub
+ entityMap.put("tilde", new Character('\u02dc')); // small tilde, U+02DC ISOdia
+ // General Punctuation
+ entityMap.put("ensp", new Character('\u2002')); // en space, U+2002 ISOpub
+ entityMap.put("emsp", new Character('\u2003')); // em space, U+2003 ISOpub
+ entityMap.put("thinsp", new Character('\u2009')); // thin space, U+2009 ISOpub
+ entityMap.put("zwnj", new Character('\u200c')); // zero width non-joiner, U+200C NEW RFC 2070
+ entityMap.put("zwj", new Character('\u200d')); // zero width joiner, U+200D NEW RFC 2070
+ entityMap.put("lrm", new Character('\u200e')); // left-to-right mark, U+200E NEW RFC 2070
+ entityMap.put("rlm", new Character('\u200f')); // right-to-left mark, U+200F NEW RFC 2070
+ entityMap.put("ndash", new Character('\u2013')); // en dash, U+2013 ISOpub
+ entityMap.put("mdash", new Character('\u2014')); // em dash, U+2014 ISOpub
+ entityMap.put("lsquo", new Character('\u2018')); // left single quotation mark, U+2018 ISOnum
+ entityMap.put("rsquo", new Character('\u2019')); // right single quotation mark, U+2019 ISOnum
+ entityMap.put("sbquo", new Character('\u201a')); // single low-9 quotation mark, U+201A NEW
+ entityMap.put("ldquo", new Character('\u201c')); // left double quotation mark, U+201C ISOnum
+ entityMap.put("rdquo", new Character('\u201d')); // right double quotation mark, U+201D ISOnum
+ entityMap.put("bdquo", new Character('\u201e')); // double low-9 quotation mark, U+201E NEW
+ entityMap.put("dagger", new Character('\u2020')); // dagger, U+2020 ISOpub
+ entityMap.put("Dagger", new Character('\u2021')); // double dagger, U+2021 ISOpub
+ entityMap.put("permil", new Character('\u2030')); // per mille sign, U+2030 ISOtech
+ entityMap.put("lsaquo", new Character('\u2039')); // single left-pointing angle quotation mark, U+2039 ISO proposed
+ // lsaquo is proposed but not yet ISO standardized
+ entityMap.put("rsaquo", new Character('\u203a')); // single right-pointing angle quotation mark, U+203A ISO proposed
+ // rsaquo is proposed but not yet ISO standardized
+ entityMap.put("euro", new Character('\u20ac')); // euro sign, U+20AC NEW
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/SpotColor.java b/src/main/java/com/lowagie/text/pdf/SpotColor.java
new file mode 100644
index 0000000..a9da97c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/SpotColor.java
@@ -0,0 +1,90 @@
+/*
+ * $Id: SpotColor.java,v 1.48 2005/12/11 15:31:04 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+/**
+ *
+ * @author psoares
+ */
+public class SpotColor extends ExtendedColor {
+
+ PdfSpotColor spot;
+ float tint;
+
+ public SpotColor(PdfSpotColor spot, float tint) {
+ super(TYPE_SEPARATION,
+ ((float)spot.getAlternativeCS().getRed() / 255f - 1f) * tint + 1,
+ ((float)spot.getAlternativeCS().getGreen() / 255f - 1f) * tint + 1,
+ ((float)spot.getAlternativeCS().getBlue() / 255f - 1f) * tint + 1);
+ this.spot = spot;
+ this.tint = tint;
+ }
+
+ public SpotColor(PdfSpotColor spot) {
+ this(spot, spot.getTint());
+ }
+
+ public PdfSpotColor getPdfSpotColor() {
+ return spot;
+ }
+
+ public float getTint() {
+ return tint;
+ }
+
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ public int hashCode() {
+ return spot.hashCode() ^ Float.floatToIntBits(tint);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/StampContent.java b/src/main/java/com/lowagie/text/pdf/StampContent.java
new file mode 100644
index 0000000..57b85c7
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/StampContent.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2003 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+
+public class StampContent extends PdfContentByte {
+ PdfStamperImp.PageStamp ps;
+ PageResources pageResources;
+
+ /** Creates a new instance of StampContent */
+ StampContent(PdfStamperImp stamper, PdfStamperImp.PageStamp ps) {
+ super(stamper);
+ this.ps = ps;
+ pageResources = ps.pageResources;
+ }
+
+ public void setAction(PdfAction action, float llx, float lly, float urx, float ury) {
+ ((PdfStamperImp)writer).addAnnotation(new PdfAnnotation(writer, llx, lly, urx, ury, action), ps.pageN);
+ }
+
+ /**
+ * Gets a duplicate of this PdfContentByte
. All
+ * the members are copied by reference but the buffer stays different.
+ *
+ * @return a copy of this PdfContentByte
+ */
+ public PdfContentByte getDuplicate() {
+ return new StampContent((PdfStamperImp)writer, ps);
+ }
+
+ PageResources getPageResources() {
+ return pageResources;
+ }
+
+ void addAnnotation(PdfAnnotation annot) {
+ ((PdfStamperImp)writer).addAnnotation(annot, ps.pageN);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/TextField.java b/src/main/java/com/lowagie/text/pdf/TextField.java
new file mode 100644
index 0000000..1535069
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/TextField.java
@@ -0,0 +1,661 @@
+/*
+ * Copyright 2003-2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.awt.Color;
+import com.lowagie.text.Element;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Font;
+import com.lowagie.text.Chunk;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/** Supports text, combo and list fields generating the correct appearances.
+ * All the option in the Acrobat GUI are supported in an easy to use API.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class TextField extends BaseField {
+
+ /** Holds value of property defaultText. */
+ private String defaultText;
+
+ /** Holds value of property choices. */
+ private String[] choices;
+
+ /** Holds value of property choiceExports. */
+ private String[] choiceExports;
+
+ /** Holds value of property choiceSelection. */
+ private int choiceSelection;
+
+ private int topFirst;
+
+ private float extraMarginLeft;
+ private float extraMarginTop;
+
+ /** Creates a new TextField
.
+ * @param writer the document PdfWriter
+ * @param box the field location and dimensions
+ * @param fieldName the field name. If null
only the widget keys
+ * will be included in the field allowing it to be used as a kid field.
+ */
+ public TextField(PdfWriter writer, Rectangle box, String fieldName) {
+ super(writer, box, fieldName);
+ }
+
+ private static boolean checkRTL(String text) {
+ if (text == null || text.length() == 0)
+ return false;
+ char[] cc = text.toCharArray();
+ for (int k = 0; k < cc.length; ++k) {
+ int c = (int)cc[k];
+ if (c >= 0x590 && c < 0x0780)
+ return true;
+ }
+ return false;
+ }
+
+ private static void changeFontSize(Phrase p, float size) {
+ for (int k = 0; k < p.size(); ++k) {
+ ((Chunk)p.get(k)).font().setSize(size);
+ }
+ }
+
+ private Phrase composePhrase(String text, BaseFont ufont, Color color, float fontSize) {
+ Phrase phrase = null;
+ if (extensionFont == null && (substitutionFonts == null || substitutionFonts.size() == 0))
+ phrase = new Phrase(new Chunk(text, new Font(ufont, fontSize, 0, color)));
+ else {
+ FontSelector fs = new FontSelector();
+ fs.addFont(new Font(ufont, fontSize, 0, color));
+ if (extensionFont != null)
+ fs.addFont(new Font(extensionFont, fontSize, 0, color));
+ if (substitutionFonts != null) {
+ for (int k = 0; k < substitutionFonts.size(); ++k) {
+ fs.addFont(new Font((BaseFont)substitutionFonts.get(k), fontSize, 0, color));
+ }
+ }
+ phrase = fs.process(text);
+ }
+ return phrase;
+ }
+
+ private static String removeCRLF(String text) {
+ if (text.indexOf('\n') >= 0 || text.indexOf('\r') >= 0) {
+ char[] p = text.toCharArray();
+ StringBuffer sb = new StringBuffer(p.length);
+ for (int k = 0; k < p.length; ++k) {
+ char c = p[k];
+ if (c == '\n')
+ sb.append(' ');
+ else if (c == '\r') {
+ sb.append(' ');
+ if (k < p.length - 1 && p[k + 1] == '\n')
+ ++k;
+ }
+ else
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+ return text;
+ }
+
+ public PdfAppearance getAppearance() throws IOException, DocumentException {
+ PdfAppearance app = getBorderAppearance();
+ app.beginVariableText();
+ if (text == null || text.length() == 0) {
+ app.endVariableText();
+ return app;
+ }
+ BaseFont ufont = getRealFont();
+ boolean borderExtra = borderStyle == PdfBorderDictionary.STYLE_BEVELED || borderStyle == PdfBorderDictionary.STYLE_INSET;
+ float h = box.height() - borderWidth * 2;
+ float bw2 = borderWidth;
+ if (borderExtra) {
+ h -= borderWidth * 2;
+ bw2 *= 2;
+ }
+ h -= extraMarginTop;
+ float offsetX = (borderExtra ? 2 * borderWidth : borderWidth);
+ offsetX = Math.max(offsetX, 1);
+ float offX = Math.min(bw2, offsetX);
+ app.saveState();
+ app.rectangle(offX, offX, box.width() - 2 * offX, box.height() - 2 * offX);
+ app.clip();
+ app.newPath();
+ Color fcolor = (textColor == null) ? GrayColor.GRAYBLACK : textColor;
+ String ptext = text; //fixed by Kazuya Ujihara (ujihara.jp)
+ if ((options & PASSWORD) != 0) {
+ char[] pchar = new char[text.length()];
+ for (int i = 0; i < text.length(); i++)
+ pchar[i] = '*';
+ ptext = new String(pchar);
+ }
+ int rtl = checkRTL(ptext) ? PdfWriter.RUN_DIRECTION_LTR : PdfWriter.RUN_DIRECTION_NO_BIDI;
+ if ((options & MULTILINE) == 0) {
+ ptext = removeCRLF(text);
+ }
+ Phrase phrase = composePhrase(ptext, ufont, fcolor, fontSize);
+ if ((options & MULTILINE) != 0) {
+ float usize = fontSize;
+ float width = box.width() - 4 * offsetX - extraMarginLeft;
+ float factor = ufont.getFontDescriptor(BaseFont.BBOXURY, 1) - ufont.getFontDescriptor(BaseFont.BBOXLLY, 1);
+ ColumnText ct = new ColumnText(null);
+ if (usize == 0) {
+ usize = h / factor;
+ if (usize > 4) {
+ if (usize > 12)
+ usize = 12;
+ float step = Math.max((usize - 4) / 10, 0.2f);
+ ct.setSimpleColumn(0, -h, width, 0);
+ ct.setAlignment(alignment);
+ ct.setRunDirection(rtl);
+ for (; usize > 4; usize -= step) {
+ ct.setYLine(0);
+ changeFontSize(phrase, usize);
+ ct.setText(phrase);
+ ct.setLeading(factor * usize);
+ int status = ct.go(true);
+ if ((status & ColumnText.NO_MORE_COLUMN) == 0)
+ break;
+ }
+ }
+ if (usize < 4) {
+ usize = 4;
+ }
+ }
+ changeFontSize(phrase, usize);
+ ct.setCanvas(app);
+ float leading = usize * factor;
+ float offsetY = offsetX + h - ufont.getFontDescriptor(BaseFont.BBOXURY, usize);
+ ct.setSimpleColumn(extraMarginLeft + 2 * offsetX, -20000, box.width() - 2 * offsetX, offsetY + leading);
+ ct.setLeading(leading);
+ ct.setAlignment(alignment);
+ ct.setRunDirection(rtl);
+ ct.setText(phrase);
+ ct.go();
+ }
+ else {
+ float usize = fontSize;
+ if (usize == 0) {
+ float maxCalculatedSize = h / (ufont.getFontDescriptor(BaseFont.BBOXURX, 1) - ufont.getFontDescriptor(BaseFont.BBOXLLY, 1));
+ changeFontSize(phrase, 1);
+ float wd = ColumnText.getWidth(phrase, rtl, 0);
+ if (wd == 0)
+ usize = maxCalculatedSize;
+ else
+ usize = (box.width() - extraMarginLeft - 4 * offsetX) / wd;
+ if (usize > maxCalculatedSize)
+ usize = maxCalculatedSize;
+ if (usize < 4)
+ usize = 4;
+ }
+ changeFontSize(phrase, usize);
+ float offsetY = offX + ((box.height() - 2*offX) - ufont.getFontDescriptor(BaseFont.ASCENT, usize)) / 2;
+ if (offsetY < offX)
+ offsetY = offX;
+ if (offsetY - offX < -ufont.getFontDescriptor(BaseFont.DESCENT, usize)) {
+ float ny = -ufont.getFontDescriptor(BaseFont.DESCENT, usize) + offX;
+ float dy = box.height() - offX - ufont.getFontDescriptor(BaseFont.ASCENT, usize);
+ offsetY = Math.min(ny, Math.max(offsetY, dy));
+ }
+ if ((options & COMB) != 0 && maxCharacterLength > 0) {
+ int textLen = Math.min(maxCharacterLength, ptext.length());
+ int position = 0;
+ if (alignment == Element.ALIGN_RIGHT) {
+ position = maxCharacterLength - textLen;
+ }
+ else if (alignment == Element.ALIGN_CENTER) {
+ position = (maxCharacterLength - textLen) / 2;
+ }
+ float step = (box.width() - extraMarginLeft) / maxCharacterLength;
+ float start = step / 2 + position * step;
+ if (textColor == null)
+ app.setGrayFill(0);
+ else
+ app.setColorFill(textColor);
+ app.beginText();
+ for (int k = 0; k < phrase.size(); ++k) {
+ Chunk ck = (Chunk)phrase.get(k);
+ BaseFont bf = ck.font().getBaseFont();
+ app.setFontAndSize(bf, usize);
+ StringBuffer sb = ck.append("");
+ for (int j = 0; j < sb.length(); ++j) {
+ String c = sb.substring(j, j + 1);
+ float wd = bf.getWidthPoint(c, usize);
+ app.setTextMatrix(extraMarginLeft + start - wd / 2, offsetY - extraMarginTop);
+ app.showText(c);
+ start += step;
+ }
+ }
+ app.endText();
+ }
+ else {
+ if (alignment == Element.ALIGN_RIGHT) {
+ ColumnText.showTextAligned(app, Element.ALIGN_RIGHT, phrase, extraMarginLeft + box.width() - 2 * offsetX, offsetY - extraMarginTop, 0, rtl, 0);
+ }
+ else if (alignment == Element.ALIGN_CENTER) {
+ ColumnText.showTextAligned(app, Element.ALIGN_CENTER, phrase, extraMarginLeft + box.width() / 2, offsetY - extraMarginTop, 0, rtl, 0);
+ }
+ else
+ ColumnText.showTextAligned(app, Element.ALIGN_LEFT, phrase, extraMarginLeft + 2 * offsetX, offsetY - extraMarginTop, 0, rtl, 0);
+ }
+ }
+ app.restoreState();
+ app.endVariableText();
+ return app;
+ }
+
+ PdfAppearance getListAppearance() throws IOException, DocumentException {
+ PdfAppearance app = getBorderAppearance();
+ app.beginVariableText();
+ if (choices == null || choices.length == 0) {
+ app.endVariableText();
+ return app;
+ }
+ int topChoice = choiceSelection;
+ if (topChoice >= choices.length) {
+ topChoice = choices.length - 1;
+ }
+ if (topChoice < 0)
+ topChoice = 0;
+ BaseFont ufont = getRealFont();
+ float usize = fontSize;
+ if (usize == 0)
+ usize = 12;
+ boolean borderExtra = borderStyle == PdfBorderDictionary.STYLE_BEVELED || borderStyle == PdfBorderDictionary.STYLE_INSET;
+ float h = box.height() - borderWidth * 2;
+ if (borderExtra)
+ h -= borderWidth * 2;
+ float offsetX = (borderExtra ? 2 * borderWidth : borderWidth);
+ float leading = ufont.getFontDescriptor(BaseFont.BBOXURY, usize) - ufont.getFontDescriptor(BaseFont.BBOXLLY, usize);
+ int maxFit = (int)(h / leading) + 1;
+ int first = 0;
+ int last = 0;
+ last = topChoice + maxFit / 2 + 1;
+ first = last - maxFit;
+ if (first < 0) {
+ last += first;
+ first = 0;
+ }
+// first = topChoice;
+ last = first + maxFit;
+ if (last > choices.length)
+ last = choices.length;
+ topFirst = first;
+ app.saveState();
+ app.rectangle(offsetX, offsetX, box.width() - 2 * offsetX, box.height() - 2 * offsetX);
+ app.clip();
+ app.newPath();
+ Color fcolor = (textColor == null) ? GrayColor.GRAYBLACK : textColor;
+ app.setColorFill(new Color(10, 36, 106));
+ app.rectangle(offsetX, offsetX + h - (topChoice - first + 1) * leading, box.width() - 2 * offsetX, leading);
+ app.fill();
+ float xp = offsetX * 2;
+ float yp = offsetX + h - ufont.getFontDescriptor(BaseFont.BBOXURY, usize);
+ for (int idx = first; idx < last; ++idx, yp -= leading) {
+ String ptext = choices[idx];
+ int rtl = checkRTL(ptext) ? PdfWriter.RUN_DIRECTION_LTR : PdfWriter.RUN_DIRECTION_NO_BIDI;
+ ptext = removeCRLF(ptext);
+ Phrase phrase = composePhrase(ptext, ufont, (idx == topChoice) ? GrayColor.GRAYWHITE : fcolor, usize);
+ ColumnText.showTextAligned(app, Element.ALIGN_LEFT, phrase, xp, yp, 0, rtl, 0);
+ }
+ app.restoreState();
+ app.endVariableText();
+ return app;
+ }
+
+ /** Gets a new text field.
+ * @throws IOException on error
+ * @throws DocumentException on error
+ * @return a new text field
+ */
+ public PdfFormField getTextField() throws IOException, DocumentException {
+ if (maxCharacterLength <= 0)
+ options &= ~COMB;
+ if ((options & COMB) != 0)
+ options &= ~MULTILINE;
+ PdfFormField field = PdfFormField.createTextField(writer, false, false, maxCharacterLength);
+ field.setWidget(box, PdfAnnotation.HIGHLIGHT_INVERT);
+ switch (alignment) {
+ case Element.ALIGN_CENTER:
+ field.setQuadding(PdfFormField.Q_CENTER);
+ break;
+ case Element.ALIGN_RIGHT:
+ field.setQuadding(PdfFormField.Q_RIGHT);
+ break;
+ }
+ if (rotation != 0)
+ field.setMKRotation(rotation);
+ if (fieldName != null) {
+ field.setFieldName(fieldName);
+ field.setValueAsString(text);
+ if (defaultText != null)
+ field.setDefaultValueAsString(defaultText);
+ if ((options & READ_ONLY) != 0)
+ field.setFieldFlags(PdfFormField.FF_READ_ONLY);
+ if ((options & REQUIRED) != 0)
+ field.setFieldFlags(PdfFormField.FF_REQUIRED);
+ if ((options & MULTILINE) != 0)
+ field.setFieldFlags(PdfFormField.FF_MULTILINE);
+ if ((options & DO_NOT_SCROLL) != 0)
+ field.setFieldFlags(PdfFormField.FF_DONOTSCROLL);
+ if ((options & PASSWORD) != 0)
+ field.setFieldFlags(PdfFormField.FF_PASSWORD);
+ if ((options & FILE_SELECTION) != 0)
+ field.setFieldFlags(PdfFormField.FF_FILESELECT);
+ if ((options & DO_NOT_SPELL_CHECK) != 0)
+ field.setFieldFlags(PdfFormField.FF_DONOTSPELLCHECK);
+ if ((options & COMB) != 0)
+ field.setFieldFlags(PdfFormField.FF_COMB);
+ }
+ field.setBorderStyle(new PdfBorderDictionary(borderWidth, borderStyle, new PdfDashPattern(3)));
+ PdfAppearance tp = getAppearance();
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tp);
+ PdfAppearance da = (PdfAppearance)tp.getDuplicate();
+ da.setFontAndSize(getRealFont(), fontSize);
+ if (textColor == null)
+ da.setGrayFill(0);
+ else
+ da.setColorFill(textColor);
+ field.setDefaultAppearanceString(da);
+ if (borderColor != null)
+ field.setMKBorderColor(borderColor);
+ if (backgroundColor != null)
+ field.setMKBackgroundColor(backgroundColor);
+ switch (visibility) {
+ case HIDDEN:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_HIDDEN);
+ break;
+ case VISIBLE_BUT_DOES_NOT_PRINT:
+ break;
+ case HIDDEN_BUT_PRINTABLE:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_NOVIEW);
+ break;
+ default:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT);
+ break;
+ }
+ return field;
+ }
+
+ /** Gets a new combo field.
+ * @throws IOException on error
+ * @throws DocumentException on error
+ * @return a new combo field
+ */
+ public PdfFormField getComboField() throws IOException, DocumentException {
+ return getChoiceField(false);
+ }
+
+ /** Gets a new list field.
+ * @throws IOException on error
+ * @throws DocumentException on error
+ * @return a new list field
+ */
+ public PdfFormField getListField() throws IOException, DocumentException {
+ return getChoiceField(true);
+ }
+
+ protected PdfFormField getChoiceField(boolean isList) throws IOException, DocumentException {
+ options &= (~MULTILINE) & (~COMB);
+ String uchoices[] = choices;
+ if (uchoices == null)
+ uchoices = new String[0];
+ int topChoice = choiceSelection;
+ if (topChoice >= uchoices.length)
+ topChoice = uchoices.length - 1;
+ if (text == null) text = ""; //fixed by Kazuya Ujihara (ujihara.jp)
+ if (topChoice >= 0)
+ text = uchoices[topChoice];
+ if (topChoice < 0)
+ topChoice = 0;
+ PdfFormField field = null;
+ String mix[][] = null;
+ if (choiceExports == null) {
+ if (isList)
+ field = PdfFormField.createList(writer, uchoices, topChoice);
+ else
+ field = PdfFormField.createCombo(writer, (options & EDIT) != 0, uchoices, topChoice);
+ }
+ else {
+ mix = new String[uchoices.length][2];
+ for (int k = 0; k < mix.length; ++k)
+ mix[k][0] = mix[k][1] = uchoices[k];
+ int top = Math.min(uchoices.length, choiceExports.length);
+ for (int k = 0; k < top; ++k) {
+ if (choiceExports[k] != null)
+ mix[k][0] = choiceExports[k];
+ }
+ if (isList)
+ field = PdfFormField.createList(writer, mix, topChoice);
+ else
+ field = PdfFormField.createCombo(writer, (options & EDIT) != 0, mix, topChoice);
+ }
+ field.setWidget(box, PdfAnnotation.HIGHLIGHT_INVERT);
+ if (rotation != 0)
+ field.setMKRotation(rotation);
+ if (fieldName != null) {
+ field.setFieldName(fieldName);
+ if (uchoices.length > 0) {
+ if (mix != null) {
+ field.setValueAsString(mix[topChoice][0]);
+ field.setDefaultValueAsString(mix[topChoice][0]);
+ }
+ else {
+ field.setValueAsString(text);
+ field.setDefaultValueAsString(text);
+ }
+ }
+ if ((options & READ_ONLY) != 0)
+ field.setFieldFlags(PdfFormField.FF_READ_ONLY);
+ if ((options & REQUIRED) != 0)
+ field.setFieldFlags(PdfFormField.FF_REQUIRED);
+ if ((options & DO_NOT_SPELL_CHECK) != 0)
+ field.setFieldFlags(PdfFormField.FF_DONOTSPELLCHECK);
+ }
+ field.setBorderStyle(new PdfBorderDictionary(borderWidth, borderStyle, new PdfDashPattern(3)));
+ PdfAppearance tp;
+ if (isList) {
+ tp = getListAppearance();
+ if (topFirst > 0)
+ field.put(PdfName.TI, new PdfNumber(topFirst));
+ }
+ else
+ tp = getAppearance();
+ field.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, tp);
+ PdfAppearance da = (PdfAppearance)tp.getDuplicate();
+ da.setFontAndSize(getRealFont(), fontSize);
+ if (textColor == null)
+ da.setGrayFill(0);
+ else
+ da.setColorFill(textColor);
+ field.setDefaultAppearanceString(da);
+ if (borderColor != null)
+ field.setMKBorderColor(borderColor);
+ if (backgroundColor != null)
+ field.setMKBackgroundColor(backgroundColor);
+ switch (visibility) {
+ case HIDDEN:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_HIDDEN);
+ break;
+ case VISIBLE_BUT_DOES_NOT_PRINT:
+ break;
+ case HIDDEN_BUT_PRINTABLE:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_NOVIEW);
+ break;
+ default:
+ field.setFlags(PdfAnnotation.FLAGS_PRINT);
+ break;
+ }
+ return field;
+ }
+
+ /** Gets the default text.
+ * @return the default text
+ */
+ public String getDefaultText() {
+ return this.defaultText;
+ }
+
+ /** Sets the default text. It is only meaningful for text fields.
+ * @param defaultText the default text
+ */
+ public void setDefaultText(String defaultText) {
+ this.defaultText = defaultText;
+ }
+
+ /** Gets the choices to be presented to the user in list/combo
+ * fields.
+ * @return the choices to be presented to the user
+ */
+ public String[] getChoices() {
+ return this.choices;
+ }
+
+ /** Sets the choices to be presented to the user in list/combo
+ * fields.
+ * @param choices the choices to be presented to the user
+ */
+ public void setChoices(String[] choices) {
+ this.choices = choices;
+ }
+
+ /** Gets the export values in list/combo fields.
+ * @return the export values in list/combo fields
+ */
+ public String[] getChoiceExports() {
+ return this.choiceExports;
+ }
+
+ /** Sets the export values in list/combo fields. If this array
+ * is null
then the choice values will also be used
+ * as the export values.
+ * @param choiceExports the export values in list/combo fields
+ */
+ public void setChoiceExports(String[] choiceExports) {
+ this.choiceExports = choiceExports;
+ }
+
+ /** Gets the zero based index of the selected item.
+ * @return the zero based index of the selected item
+ */
+ public int getChoiceSelection() {
+ return this.choiceSelection;
+ }
+
+ /** Sets the zero based index of the selected item.
+ * @param choiceSelection the zero based index of the selected item
+ */
+ public void setChoiceSelection(int choiceSelection) {
+ this.choiceSelection = choiceSelection;
+ }
+
+ int getTopFirst() {
+ return topFirst;
+ }
+
+ /**
+ * Sets extra margins in text fields to better mimic the Acrobat layout.
+ * @param extraMarginLeft the extra marging left
+ * @param extraMarginTop the extra margin top
+ */
+ public void setExtraMargin(float extraMarginLeft, float extraMarginTop) {
+ this.extraMarginLeft = extraMarginLeft;
+ this.extraMarginTop = extraMarginTop;
+ }
+
+ /**
+ * Holds value of property substitutionFonts.
+ */
+ private ArrayList substitutionFonts;
+
+ /**
+ * Gets the list of substitution fonts. The list is composed of BaseFont
and can be null
. The fonts in this list will be used if the original
+ * font doesn't contain the needed glyphs.
+ * @return the list
+ */
+ public ArrayList getSubstitutionFonts() {
+ return this.substitutionFonts;
+ }
+
+ /**
+ * Sets a list of substitution fonts. The list is composed of BaseFont
and can also be null
. The fonts in this list will be used if the original
+ * font doesn't contain the needed glyphs.
+ * @param substitutionFonts the list
+ */
+ public void setSubstitutionFonts(ArrayList substitutionFonts) {
+ this.substitutionFonts = substitutionFonts;
+ }
+
+ /**
+ * Holds value of property extensionFont.
+ */
+ private BaseFont extensionFont;
+
+ /**
+ * Gets the extensionFont. This font will be searched before the
+ * substitution fonts. It may be null
.
+ * @return the extensionFont
+ */
+ public BaseFont getExtensionFont() {
+ return this.extensionFont;
+ }
+
+ /**
+ * Sets the extensionFont. This font will be searched before the
+ * substitution fonts. It may be null
.
+ * @param extensionFont New value of property extensionFont.
+ */
+ public void setExtensionFont(BaseFont extensionFont) {
+ this.extensionFont = extensionFont;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/TrueTypeFont.java b/src/main/java/com/lowagie/text/pdf/TrueTypeFont.java
new file mode 100644
index 0000000..8551cfc
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/TrueTypeFont.java
@@ -0,0 +1,1377 @@
+/*
+ * $Id: TrueTypeFont.java,v 1.58 2006/02/23 16:45:48 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.ExceptionConverter;
+/** Reads a Truetype font
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+class TrueTypeFont extends BaseFont {
+
+ /** The code pages possible for a True Type font.
+ */
+ static final String codePages[] = {
+ "1252 Latin 1",
+ "1250 Latin 2: Eastern Europe",
+ "1251 Cyrillic",
+ "1253 Greek",
+ "1254 Turkish",
+ "1255 Hebrew",
+ "1256 Arabic",
+ "1257 Windows Baltic",
+ "1258 Vietnamese",
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ "874 Thai",
+ "932 JIS/Japan",
+ "936 Chinese: Simplified chars--PRC and Singapore",
+ "949 Korean Wansung",
+ "950 Chinese: Traditional chars--Taiwan and Hong Kong",
+ "1361 Korean Johab",
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ "Macintosh Character Set (US Roman)",
+ "OEM Character Set",
+ "Symbol Character Set",
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ "869 IBM Greek",
+ "866 MS-DOS Russian",
+ "865 MS-DOS Nordic",
+ "864 Arabic",
+ "863 MS-DOS Canadian French",
+ "862 Hebrew",
+ "861 MS-DOS Icelandic",
+ "860 MS-DOS Portuguese",
+ "857 IBM Turkish",
+ "855 IBM Cyrillic; primarily Russian",
+ "852 Latin 2",
+ "775 MS-DOS Baltic",
+ "737 Greek; former 437 G",
+ "708 Arabic; ASMO 708",
+ "850 WE/Latin 1",
+ "437 US"};
+
+ protected boolean justNames = false;
+ /** Contains the location of the several tables. The key is the name of
+ * the table and the value is an int[2]
where position 0
+ * is the offset from the start of the file and position 1 is the length
+ * of the table.
+ */
+ protected HashMap tables;
+ /** The file in use.
+ */
+ protected RandomAccessFileOrArray rf;
+ /** The file name.
+ */
+ protected String fileName;
+
+ protected boolean cff = false;
+
+ protected int cffOffset;
+
+ protected int cffLength;
+
+ /** The offset from the start of the file to the table directory.
+ * It is 0 for TTF and may vary for TTC depending on the chosen font.
+ */
+ protected int directoryOffset;
+ /** The index for the TTC font. It is an empty String
for a
+ * TTF file.
+ */
+ protected String ttcIndex;
+ /** The style modifier */
+ protected String style = "";
+ /** The content of table 'head'.
+ */
+ protected FontHeader head = new FontHeader();
+ /** The content of table 'hhea'.
+ */
+ protected HorizontalHeader hhea = new HorizontalHeader();
+ /** The content of table 'OS/2'.
+ */
+ protected WindowsMetrics os_2 = new WindowsMetrics();
+ /** The width of the glyphs. This is essentially the content of table
+ * 'hmtx' normalized to 1000 units.
+ */
+ protected int GlyphWidths[];
+
+ protected int bboxes[][];
+ /** The map containing the code information for the table 'cmap', encoding 1.0.
+ * The key is the code and the value is an int[2]
where position 0
+ * is the glyph number and position 1 is the glyph width normalized to 1000
+ * units.
+ */
+ protected HashMap cmap10;
+ /** The map containing the code information for the table 'cmap', encoding 3.1
+ * in Unicode.
+ * int
[2] where position 0
+ * is the glyph number and position 1 is the glyph width normalized to 1000
+ * units.
+ */
+ protected HashMap cmap31;
+ /** The map containing the kerning information. It represents the content of
+ * table 'kern'. The key is an Integer
where the top 16 bits
+ * are the glyph number for the first character and the lower 16 bits are the
+ * glyph number for the second character. The value is the amount of kerning in
+ * normalized 1000 units as an Integer
. This value is usually negative.
+ */
+ protected IntHashtable kerning = new IntHashtable();
+ /**
+ * The font name.
+ * This name is usually extracted from the table 'name' with
+ * the 'Name ID' 6.
+ */
+ protected String fontName;
+
+ /** The full name of the font
+ */
+ protected String fullName[][];
+
+ /** The family name of the font
+ */
+ protected String familyName[][];
+ /** The italic angle. It is usually extracted from the 'post' table or in it's
+ * absence with the code:
+ *
+ * -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI
+ *
+ */
+ protected double italicAngle;
+ /** true
if all the glyphs have the same width.
+ */
+ protected boolean isFixedPitch = false;
+
+ /** The components of table 'head'.
+ */
+ protected static class FontHeader {
+ /** A variable. */
+ int flags;
+ /** A variable. */
+ int unitsPerEm;
+ /** A variable. */
+ short xMin;
+ /** A variable. */
+ short yMin;
+ /** A variable. */
+ short xMax;
+ /** A variable. */
+ short yMax;
+ /** A variable. */
+ int macStyle;
+ }
+
+ /** The components of table 'hhea'.
+ */
+ protected static class HorizontalHeader {
+ /** A variable. */
+ short Ascender;
+ /** A variable. */
+ short Descender;
+ /** A variable. */
+ short LineGap;
+ /** A variable. */
+ int advanceWidthMax;
+ /** A variable. */
+ short minLeftSideBearing;
+ /** A variable. */
+ short minRightSideBearing;
+ /** A variable. */
+ short xMaxExtent;
+ /** A variable. */
+ short caretSlopeRise;
+ /** A variable. */
+ short caretSlopeRun;
+ /** A variable. */
+ int numberOfHMetrics;
+ }
+
+ /** The components of table 'OS/2'.
+ */
+ protected static class WindowsMetrics {
+ /** A variable. */
+ short xAvgCharWidth;
+ /** A variable. */
+ int usWeightClass;
+ /** A variable. */
+ int usWidthClass;
+ /** A variable. */
+ short fsType;
+ /** A variable. */
+ short ySubscriptXSize;
+ /** A variable. */
+ short ySubscriptYSize;
+ /** A variable. */
+ short ySubscriptXOffset;
+ /** A variable. */
+ short ySubscriptYOffset;
+ /** A variable. */
+ short ySuperscriptXSize;
+ /** A variable. */
+ short ySuperscriptYSize;
+ /** A variable. */
+ short ySuperscriptXOffset;
+ /** A variable. */
+ short ySuperscriptYOffset;
+ /** A variable. */
+ short yStrikeoutSize;
+ /** A variable. */
+ short yStrikeoutPosition;
+ /** A variable. */
+ short sFamilyClass;
+ /** A variable. */
+ byte panose[] = new byte[10];
+ /** A variable. */
+ byte achVendID[] = new byte[4];
+ /** A variable. */
+ int fsSelection;
+ /** A variable. */
+ int usFirstCharIndex;
+ /** A variable. */
+ int usLastCharIndex;
+ /** A variable. */
+ short sTypoAscender;
+ /** A variable. */
+ short sTypoDescender;
+ /** A variable. */
+ short sTypoLineGap;
+ /** A variable. */
+ int usWinAscent;
+ /** A variable. */
+ int usWinDescent;
+ /** A variable. */
+ int ulCodePageRange1;
+ /** A variable. */
+ int ulCodePageRange2;
+ /** A variable. */
+ int sCapHeight;
+ }
+
+ /** This constructor is present to allow extending the class.
+ */
+ protected TrueTypeFont() {
+ }
+
+ TrueTypeFont(String ttFile, String enc, boolean emb, byte ttfAfm[]) throws DocumentException, IOException {
+ this(ttFile, enc, emb, ttfAfm, false);
+ }
+
+ /** Creates a new TrueType font.
+ * @param ttFile the location of the font on file. The file must end in '.ttf' or
+ * '.ttc' but can have modifiers after the name
+ * @param enc the encoding to be applied to this font
+ * @param emb true if the font is to be embedded in the PDF
+ * @param ttfAfm the font as a byte
array
+ * @throws DocumentException the font is invalid
+ * @throws IOException the font file could not be read
+ */
+ TrueTypeFont(String ttFile, String enc, boolean emb, byte ttfAfm[], boolean justNames) throws DocumentException, IOException {
+ this.justNames = justNames;
+ String nameBase = getBaseName(ttFile);
+ String ttcName = getTTCName(nameBase);
+ if (nameBase.length() < ttFile.length()) {
+ style = ttFile.substring(nameBase.length());
+ }
+ encoding = enc;
+ embedded = emb;
+ fileName = ttcName;
+ fontType = FONT_TYPE_TT;
+ ttcIndex = "";
+ if (ttcName.length() < nameBase.length())
+ ttcIndex = nameBase.substring(ttcName.length() + 1);
+ if (fileName.toLowerCase().endsWith(".ttf") || fileName.toLowerCase().endsWith(".otf") || fileName.toLowerCase().endsWith(".ttc")) {
+ process(ttfAfm);
+ if (!justNames && embedded && os_2.fsType == 2)
+ throw new DocumentException(fileName + style + " cannot be embedded due to licensing restrictions.");
+ }
+ else
+ throw new DocumentException(fileName + style + " is not a TTF, OTF or TTC font file.");
+ PdfEncodings.convertToBytes(" ", enc); // check if the encoding exists
+ createEncoding();
+ }
+
+ /** Gets the name from a composed TTC file name.
+ * If I have for input "myfont.ttc,2" the return will
+ * be "myfont.ttc".
+ * @param name the full name
+ * @return the simple file name
+ */
+ protected static String getTTCName(String name) {
+ int idx = name.toLowerCase().indexOf(".ttc,");
+ if (idx < 0)
+ return name;
+ else
+ return name.substring(0, idx + 4);
+ }
+
+
+ /**
+ * Reads the tables 'head', 'hhea', 'OS/2' and 'post' filling several variables.
+ * @throws DocumentException the font is invalid
+ * @throws IOException the font file could not be read
+ */
+ void fillTables() throws DocumentException, IOException {
+ int table_location[];
+ table_location = (int[])tables.get("head");
+ if (table_location == null)
+ throw new DocumentException("Table 'head' does not exist in " + fileName + style);
+ rf.seek(table_location[0] + 16);
+ head.flags = rf.readUnsignedShort();
+ head.unitsPerEm = rf.readUnsignedShort();
+ rf.skipBytes(16);
+ head.xMin = rf.readShort();
+ head.yMin = rf.readShort();
+ head.xMax = rf.readShort();
+ head.yMax = rf.readShort();
+ head.macStyle = rf.readUnsignedShort();
+
+ table_location = (int[])tables.get("hhea");
+ if (table_location == null)
+ throw new DocumentException("Table 'hhea' does not exist " + fileName + style);
+ rf.seek(table_location[0] + 4);
+ hhea.Ascender = rf.readShort();
+ hhea.Descender = rf.readShort();
+ hhea.LineGap = rf.readShort();
+ hhea.advanceWidthMax = rf.readUnsignedShort();
+ hhea.minLeftSideBearing = rf.readShort();
+ hhea.minRightSideBearing = rf.readShort();
+ hhea.xMaxExtent = rf.readShort();
+ hhea.caretSlopeRise = rf.readShort();
+ hhea.caretSlopeRun = rf.readShort();
+ rf.skipBytes(12);
+ hhea.numberOfHMetrics = rf.readUnsignedShort();
+
+ table_location = (int[])tables.get("OS/2");
+ if (table_location == null)
+ throw new DocumentException("Table 'OS/2' does not exist in " + fileName + style);
+ rf.seek(table_location[0]);
+ int version = rf.readUnsignedShort();
+ os_2.xAvgCharWidth = rf.readShort();
+ os_2.usWeightClass = rf.readUnsignedShort();
+ os_2.usWidthClass = rf.readUnsignedShort();
+ os_2.fsType = rf.readShort();
+ os_2.ySubscriptXSize = rf.readShort();
+ os_2.ySubscriptYSize = rf.readShort();
+ os_2.ySubscriptXOffset = rf.readShort();
+ os_2.ySubscriptYOffset = rf.readShort();
+ os_2.ySuperscriptXSize = rf.readShort();
+ os_2.ySuperscriptYSize = rf.readShort();
+ os_2.ySuperscriptXOffset = rf.readShort();
+ os_2.ySuperscriptYOffset = rf.readShort();
+ os_2.yStrikeoutSize = rf.readShort();
+ os_2.yStrikeoutPosition = rf.readShort();
+ os_2.sFamilyClass = rf.readShort();
+ rf.readFully(os_2.panose);
+ rf.skipBytes(16);
+ rf.readFully(os_2.achVendID);
+ os_2.fsSelection = rf.readUnsignedShort();
+ os_2.usFirstCharIndex = rf.readUnsignedShort();
+ os_2.usLastCharIndex = rf.readUnsignedShort();
+ os_2.sTypoAscender = rf.readShort();
+ os_2.sTypoDescender = rf.readShort();
+ if (os_2.sTypoDescender > 0)
+ os_2.sTypoDescender = (short)(-os_2.sTypoDescender);
+ os_2.sTypoLineGap = rf.readShort();
+ os_2.usWinAscent = rf.readUnsignedShort();
+ os_2.usWinDescent = rf.readUnsignedShort();
+ os_2.ulCodePageRange1 = 0;
+ os_2.ulCodePageRange2 = 0;
+ if (version > 0) {
+ os_2.ulCodePageRange1 = rf.readInt();
+ os_2.ulCodePageRange2 = rf.readInt();
+ }
+ if (version > 1) {
+ rf.skipBytes(2);
+ os_2.sCapHeight = rf.readShort();
+ }
+ else
+ os_2.sCapHeight = (int)(0.7 * head.unitsPerEm);
+
+ table_location = (int[])tables.get("post");
+ if (table_location == null) {
+ italicAngle = -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI;
+ return;
+ }
+ rf.seek(table_location[0] + 4);
+ short mantissa = rf.readShort();
+ int fraction = rf.readUnsignedShort();
+ italicAngle = (double)mantissa + (double)fraction / 16384.0;
+ rf.skipBytes(4);
+ isFixedPitch = rf.readInt() != 0;
+ }
+
+ /**
+ * Gets the Postscript font name.
+ * @throws DocumentException the font is invalid
+ * @throws IOException the font file could not be read
+ * @return the Postscript font name
+ */
+ String getBaseFont() throws DocumentException, IOException {
+ int table_location[];
+ table_location = (int[])tables.get("name");
+ if (table_location == null)
+ throw new DocumentException("Table 'name' does not exist in " + fileName + style);
+ rf.seek(table_location[0] + 2);
+ int numRecords = rf.readUnsignedShort();
+ int startOfStorage = rf.readUnsignedShort();
+ for (int k = 0; k < numRecords; ++k) {
+ int platformID = rf.readUnsignedShort();
+ int platformEncodingID = rf.readUnsignedShort();
+ int languageID = rf.readUnsignedShort();
+ int nameID = rf.readUnsignedShort();
+ int length = rf.readUnsignedShort();
+ int offset = rf.readUnsignedShort();
+ if (nameID == 6) {
+ rf.seek(table_location[0] + startOfStorage + offset);
+ if (platformID == 0 || platformID == 3)
+ return readUnicodeString(length);
+ else
+ return readStandardString(length);
+ }
+ }
+ File file = new File(fileName);
+ return file.getName().replace(' ', '-');
+ }
+
+ /** Extracts the names of the font in all the languages available.
+ * @param id the name id to retrieve
+ * @throws DocumentException on error
+ * @throws IOException on error
+ */
+ String[][] getNames(int id) throws DocumentException, IOException {
+ int table_location[];
+ table_location = (int[])tables.get("name");
+ if (table_location == null)
+ throw new DocumentException("Table 'name' does not exist in " + fileName + style);
+ rf.seek(table_location[0] + 2);
+ int numRecords = rf.readUnsignedShort();
+ int startOfStorage = rf.readUnsignedShort();
+ ArrayList names = new ArrayList();
+ for (int k = 0; k < numRecords; ++k) {
+ int platformID = rf.readUnsignedShort();
+ int platformEncodingID = rf.readUnsignedShort();
+ int languageID = rf.readUnsignedShort();
+ int nameID = rf.readUnsignedShort();
+ int length = rf.readUnsignedShort();
+ int offset = rf.readUnsignedShort();
+ if (nameID == id) {
+ int pos = rf.getFilePointer();
+ rf.seek(table_location[0] + startOfStorage + offset);
+ String name;
+ if (platformID == 0 || platformID == 3 || (platformID == 2 && platformEncodingID == 1)){
+ name = readUnicodeString(length);
+ }
+ else {
+ name = readStandardString(length);
+ }
+ names.add(new String[]{String.valueOf(platformID),
+ String.valueOf(platformEncodingID), String.valueOf(languageID), name});
+ rf.seek(pos);
+ }
+ }
+ String thisName[][] = new String[names.size()][];
+ for (int k = 0; k < names.size(); ++k)
+ thisName[k] = (String[])names.get(k);
+ return thisName;
+ }
+
+ void checkCff() throws DocumentException, IOException {
+ int table_location[];
+ table_location = (int[])tables.get("CFF ");
+ if (table_location != null) {
+ cff = true;
+ cffOffset = table_location[0];
+ cffLength = table_location[1];
+ }
+ }
+
+ /** Reads the font data.
+ * @param ttfAfm the font as a byte
array, possibly null
+ * @throws DocumentException the font is invalid
+ * @throws IOException the font file could not be read
+ */
+ void process(byte ttfAfm[]) throws DocumentException, IOException {
+ tables = new HashMap();
+
+ try {
+ if (ttfAfm == null)
+ rf = new RandomAccessFileOrArray(fileName);
+ else
+ rf = new RandomAccessFileOrArray(ttfAfm);
+ if (ttcIndex.length() > 0) {
+ int dirIdx = Integer.parseInt(ttcIndex);
+ if (dirIdx < 0)
+ throw new DocumentException("The font index for " + fileName + " must be positive.");
+ String mainTag = readStandardString(4);
+ if (!mainTag.equals("ttcf"))
+ throw new DocumentException(fileName + " is not a valid TTC file.");
+ rf.skipBytes(4);
+ int dirCount = rf.readInt();
+ if (dirIdx >= dirCount)
+ throw new DocumentException("The font index for " + fileName + " must be between 0 and " + (dirCount - 1) + ". It was " + dirIdx + ".");
+ rf.skipBytes(dirIdx * 4);
+ directoryOffset = rf.readInt();
+ }
+ rf.seek(directoryOffset);
+ int ttId = rf.readInt();
+ if (ttId != 0x00010000 && ttId != 0x4F54544F)
+ throw new DocumentException(fileName + " is not a valid TTF or OTF file.");
+ int num_tables = rf.readUnsignedShort();
+ rf.skipBytes(6);
+ for (int k = 0; k < num_tables; ++k) {
+ String tag = readStandardString(4);
+ rf.skipBytes(4);
+ int table_location[] = new int[2];
+ table_location[0] = rf.readInt();
+ table_location[1] = rf.readInt();
+ tables.put(tag, table_location);
+ }
+ checkCff();
+ fontName = getBaseFont();
+ fullName = getNames(4); //full name
+ familyName = getNames(1); //family name
+ if (!justNames) {
+ fillTables();
+ readGlyphWidths();
+ readCMaps();
+ readKerning();
+ readBbox();
+ GlyphWidths = null;
+ }
+ }
+ finally {
+ if (rf != null) {
+ rf.close();
+ if (!embedded)
+ rf = null;
+ }
+ }
+ }
+
+ /** Reads a String
from the font file as bytes using the Cp1252
+ * encoding.
+ * @param length the length of bytes to read
+ * @return the String
read
+ * @throws IOException the font file could not be read
+ */
+ protected String readStandardString(int length) throws IOException {
+ byte buf[] = new byte[length];
+ rf.readFully(buf);
+ try {
+ return new String(buf, WINANSI);
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /** Reads a Unicode String
from the font file. Each character is
+ * represented by two bytes.
+ * @param length the length of bytes to read. The String
will have length
/2
+ * characters
+ * @return the String
read
+ * @throws IOException the font file could not be read
+ */
+ protected String readUnicodeString(int length) throws IOException {
+ StringBuffer buf = new StringBuffer();
+ length /= 2;
+ for (int k = 0; k < length; ++k) {
+ buf.append(rf.readChar());
+ }
+ return buf.toString();
+ }
+
+ /** Reads the glyphs widths. The widths are extracted from the table 'hmtx'.
+ * The glyphs are normalized to 1000 units.
+ * @throws DocumentException the font is invalid
+ * @throws IOException the font file could not be read
+ */
+ protected void readGlyphWidths() throws DocumentException, IOException {
+ int table_location[];
+ table_location = (int[])tables.get("hmtx");
+ if (table_location == null)
+ throw new DocumentException("Table 'hmtx' does not exist in " + fileName + style);
+ rf.seek(table_location[0]);
+ GlyphWidths = new int[hhea.numberOfHMetrics];
+ for (int k = 0; k < hhea.numberOfHMetrics; ++k) {
+ GlyphWidths[k] = (rf.readUnsignedShort() * 1000) / head.unitsPerEm;
+ rf.readUnsignedShort();
+ }
+ }
+
+ /** Gets a glyph width.
+ * @param glyph the glyph to get the width of
+ * @return the width of the glyph in normalized 1000 units
+ */
+ protected int getGlyphWidth(int glyph) {
+ if (glyph >= GlyphWidths.length)
+ glyph = GlyphWidths.length - 1;
+ return GlyphWidths[glyph];
+ }
+
+ private void readBbox() throws DocumentException, IOException {
+ int tableLocation[];
+ tableLocation = (int[])tables.get("head");
+ if (tableLocation == null)
+ throw new DocumentException("Table 'head' does not exist in " + fileName + style);
+ rf.seek(tableLocation[0] + TrueTypeFontSubSet.HEAD_LOCA_FORMAT_OFFSET);
+ boolean locaShortTable = (rf.readUnsignedShort() == 0);
+ tableLocation = (int[])tables.get("loca");
+ if (tableLocation == null)
+ return;
+ rf.seek(tableLocation[0]);
+ int locaTable[];
+ if (locaShortTable) {
+ int entries = tableLocation[1] / 2;
+ locaTable = new int[entries];
+ for (int k = 0; k < entries; ++k)
+ locaTable[k] = rf.readUnsignedShort() * 2;
+ }
+ else {
+ int entries = tableLocation[1] / 4;
+ locaTable = new int[entries];
+ for (int k = 0; k < entries; ++k)
+ locaTable[k] = rf.readInt();
+ }
+ tableLocation = (int[])tables.get("glyf");
+ if (tableLocation == null)
+ throw new DocumentException("Table 'glyf' does not exist in " + fileName + style);
+ int tableGlyphOffset = tableLocation[0];
+ bboxes = new int[locaTable.length - 1][];
+ for (int glyph = 0; glyph < locaTable.length - 1; ++glyph) {
+ int start = locaTable[glyph];
+ if (start != locaTable[glyph + 1]) {
+ rf.seek(tableGlyphOffset + start + 2);
+ bboxes[glyph] = new int[]{
+ (rf.readShort() * 1000) / head.unitsPerEm,
+ (rf.readShort() * 1000) / head.unitsPerEm,
+ (rf.readShort() * 1000) / head.unitsPerEm,
+ (rf.readShort() * 1000) / head.unitsPerEm};
+ }
+ }
+ }
+
+ /** Reads the several maps from the table 'cmap'. The maps of interest are 1.0 for symbolic
+ * fonts and 3.1 for all others. A symbolic font is defined as having the map 3.0.
+ * @throws DocumentException the font is invalid
+ * @throws IOException the font file could not be read
+ */
+ void readCMaps() throws DocumentException, IOException {
+ int table_location[];
+ table_location = (int[])tables.get("cmap");
+ if (table_location == null)
+ throw new DocumentException("Table 'cmap' does not exist in " + fileName + style);
+ rf.seek(table_location[0]);
+ rf.skipBytes(2);
+ int num_tables = rf.readUnsignedShort();
+ fontSpecific = false;
+ int map10 = 0;
+ int map31 = 0;
+ int map30 = 0;
+ for (int k = 0; k < num_tables; ++k) {
+ int platId = rf.readUnsignedShort();
+ int platSpecId = rf.readUnsignedShort();
+ int offset = rf.readInt();
+ if (platId == 3 && platSpecId == 0) {
+ fontSpecific = true;
+ map30 = offset;
+ }
+ else if (platId == 3 && platSpecId == 1) {
+ map31 = offset;
+ }
+ if (platId == 1 && platSpecId == 0) {
+ map10 = offset;
+ }
+ }
+ if (map10 > 0) {
+ rf.seek(table_location[0] + map10);
+ int format = rf.readUnsignedShort();
+ switch (format) {
+ case 0:
+ cmap10 = readFormat0();
+ break;
+ case 4:
+ cmap10 = readFormat4();
+ break;
+ case 6:
+ cmap10 = readFormat6();
+ break;
+ }
+ }
+ if (map31 > 0) {
+ rf.seek(table_location[0] + map31);
+ int format = rf.readUnsignedShort();
+ if (format == 4) {
+ cmap31 = readFormat4();
+ }
+ }
+ if (map30 > 0) {
+ rf.seek(table_location[0] + map30);
+ int format = rf.readUnsignedShort();
+ if (format == 4) {
+ cmap10 = readFormat4();
+ }
+ }
+ }
+
+ /** The information in the maps of the table 'cmap' is coded in several formats.
+ * Format 0 is the Apple standard character to glyph index mapping table.
+ * @return a HashMap
representing this map
+ * @throws IOException the font file could not be read
+ */
+ HashMap readFormat0() throws IOException {
+ HashMap h = new HashMap();
+ rf.skipBytes(4);
+ for (int k = 0; k < 256; ++k) {
+ int r[] = new int[2];
+ r[0] = rf.readUnsignedByte();
+ r[1] = getGlyphWidth(r[0]);
+ h.put(new Integer(k), r);
+ }
+ return h;
+ }
+
+ /** The information in the maps of the table 'cmap' is coded in several formats.
+ * Format 4 is the Microsoft standard character to glyph index mapping table.
+ * @return a HashMap
representing this map
+ * @throws IOException the font file could not be read
+ */
+ HashMap readFormat4() throws IOException {
+ HashMap h = new HashMap();
+ int table_lenght = rf.readUnsignedShort();
+ rf.skipBytes(2);
+ int segCount = rf.readUnsignedShort() / 2;
+ rf.skipBytes(6);
+ int endCount[] = new int[segCount];
+ for (int k = 0; k < segCount; ++k) {
+ endCount[k] = rf.readUnsignedShort();
+ }
+ rf.skipBytes(2);
+ int startCount[] = new int[segCount];
+ for (int k = 0; k < segCount; ++k) {
+ startCount[k] = rf.readUnsignedShort();
+ }
+ int idDelta[] = new int[segCount];
+ for (int k = 0; k < segCount; ++k) {
+ idDelta[k] = rf.readUnsignedShort();
+ }
+ int idRO[] = new int[segCount];
+ for (int k = 0; k < segCount; ++k) {
+ idRO[k] = rf.readUnsignedShort();
+ }
+ int glyphId[] = new int[table_lenght / 2 - 8 - segCount * 4];
+ for (int k = 0; k < glyphId.length; ++k) {
+ glyphId[k] = rf.readUnsignedShort();
+ }
+ for (int k = 0; k < segCount; ++k) {
+ int glyph;
+ for (int j = startCount[k]; j <= endCount[k] && j != 0xFFFF; ++j) {
+ if (idRO[k] == 0) {
+ glyph = (j + idDelta[k]) & 0xFFFF;
+ }
+ else {
+ int idx = k + idRO[k] / 2 - segCount + j - startCount[k];
+ if (idx >= glyphId.length)
+ continue;
+ glyph = (glyphId[idx] + idDelta[k]) & 0xFFFF;
+ }
+ int r[] = new int[2];
+ r[0] = glyph;
+ r[1] = getGlyphWidth(r[0]);
+ h.put(new Integer(fontSpecific ? ((j & 0xff00) == 0xf000 ? j & 0xff : j) : j), r);
+ }
+ }
+ return h;
+ }
+
+ /** The information in the maps of the table 'cmap' is coded in several formats.
+ * Format 6 is a trimmed table mapping. It is similar to format 0 but can have
+ * less than 256 entries.
+ * @return a HashMap
representing this map
+ * @throws IOException the font file could not be read
+ */
+ HashMap readFormat6() throws IOException {
+ HashMap h = new HashMap();
+ rf.skipBytes(4);
+ int start_code = rf.readUnsignedShort();
+ int code_count = rf.readUnsignedShort();
+ for (int k = 0; k < code_count; ++k) {
+ int r[] = new int[2];
+ r[0] = rf.readUnsignedShort();
+ r[1] = getGlyphWidth(r[0]);
+ h.put(new Integer(k + start_code), r);
+ }
+ return h;
+ }
+
+ /** Reads the kerning information from the 'kern' table.
+ * @throws IOException the font file could not be read
+ */
+ void readKerning() throws IOException {
+ int table_location[];
+ table_location = (int[])tables.get("kern");
+ if (table_location == null)
+ return;
+ rf.seek(table_location[0] + 2);
+ int nTables = rf.readUnsignedShort();
+ int checkpoint = table_location[0] + 4;
+ int length = 0;
+ for (int k = 0; k < nTables; ++k) {
+ checkpoint += length;
+ rf.seek(checkpoint);
+ rf.skipBytes(2);
+ length = rf.readUnsignedShort();
+ int coverage = rf.readUnsignedShort();
+ if ((coverage & 0xfff7) == 0x0001) {
+ int nPairs = rf.readUnsignedShort();
+ rf.skipBytes(6);
+ for (int j = 0; j < nPairs; ++j) {
+ int pair = rf.readInt();
+ int value = ((int)rf.readShort() * 1000) / head.unitsPerEm;
+ kerning.put(pair, value);
+ }
+ }
+ }
+ }
+
+ /** Gets the kerning between two Unicode chars.
+ * @param char1 the first char
+ * @param char2 the second char
+ * @return the kerning to be applied
+ */
+ public int getKerning(char char1, char char2) {
+ int metrics[] = getMetricsTT(char1);
+ if (metrics == null)
+ return 0;
+ int c1 = metrics[0];
+ metrics = getMetricsTT(char2);
+ if (metrics == null)
+ return 0;
+ int c2 = metrics[0];
+ return kerning.get((c1 << 16) + c2);
+ }
+
+ /** Gets the width from the font according to the unicode char c
.
+ * If the name
is null it's a symbolic font.
+ * @param c the unicode char
+ * @param name the glyph name
+ * @return the width of the char
+ */
+ int getRawWidth(int c, String name) {
+ HashMap map = null;
+ if (name == null || cmap31 == null)
+ map = cmap10;
+ else
+ map = cmap31;
+ if (map == null)
+ return 0;
+ int metric[] = (int[])map.get(new Integer(c));
+ if (metric == null)
+ return 0;
+ return metric[1];
+ }
+
+ /** Generates the font descriptor for this font.
+ * @return the PdfDictionary containing the font descriptor or null
+ * @param subsetPrefix the subset prefix
+ * @param fontStream the indirect reference to a PdfStream containing the font or null
+ * @throws DocumentException if there is an error
+ */
+ protected PdfDictionary getFontDescriptor(PdfIndirectReference fontStream, String subsetPrefix) throws DocumentException {
+ PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR);
+ dic.put(PdfName.ASCENT, new PdfNumber((int)os_2.sTypoAscender * 1000 / head.unitsPerEm));
+ dic.put(PdfName.CAPHEIGHT, new PdfNumber((int)os_2.sCapHeight * 1000 / head.unitsPerEm));
+ dic.put(PdfName.DESCENT, new PdfNumber((int)os_2.sTypoDescender * 1000 / head.unitsPerEm));
+ dic.put(PdfName.FONTBBOX, new PdfRectangle(
+ (int)head.xMin * 1000 / head.unitsPerEm,
+ (int)head.yMin * 1000 / head.unitsPerEm,
+ (int)head.xMax * 1000 / head.unitsPerEm,
+ (int)head.yMax * 1000 / head.unitsPerEm));
+ if (cff) {
+ if (encoding.startsWith("Identity-"))
+ dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName+"-"+encoding));
+ else
+ dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style));
+ }
+ else
+ dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style));
+ dic.put(PdfName.ITALICANGLE, new PdfNumber(italicAngle));
+ dic.put(PdfName.STEMV, new PdfNumber(80));
+ if (fontStream != null) {
+ if (cff)
+ dic.put(PdfName.FONTFILE3, fontStream);
+ else
+ dic.put(PdfName.FONTFILE2, fontStream);
+ }
+ int flags = 0;
+ if (isFixedPitch)
+ flags |= 1;
+ flags |= fontSpecific ? 4 : 32;
+ if ((head.macStyle & 2) != 0)
+ flags |= 64;
+ if ((head.macStyle & 1) != 0)
+ flags |= 262144;
+ dic.put(PdfName.FLAGS, new PdfNumber(flags));
+
+ return dic;
+ }
+
+ /** Generates the font dictionary for this font.
+ * @return the PdfDictionary containing the font dictionary
+ * @param subsetPrefix the subset prefx
+ * @param firstChar the first valid character
+ * @param lastChar the last valid character
+ * @param shortTag a 256 bytes long byte
array where each unused byte is represented by 0
+ * @param fontDescriptor the indirect reference to a PdfDictionary containing the font descriptor or null
+ * @throws DocumentException if there is an error
+ */
+ protected PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, String subsetPrefix, int firstChar, int lastChar, byte shortTag[]) throws DocumentException {
+ PdfDictionary dic = new PdfDictionary(PdfName.FONT);
+ if (cff) {
+ dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
+ dic.put(PdfName.BASEFONT, new PdfName(fontName + style));
+ }
+ else {
+ dic.put(PdfName.SUBTYPE, PdfName.TRUETYPE);
+ dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style));
+ }
+ dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style));
+ if (!fontSpecific) {
+ for (int k = firstChar; k <= lastChar; ++k) {
+ if (!differences[k].equals(notdef)) {
+ firstChar = k;
+ break;
+ }
+ }
+ if (encoding.equals("Cp1252") || encoding.equals("MacRoman"))
+ dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING);
+ else {
+ PdfDictionary enc = new PdfDictionary(PdfName.ENCODING);
+ PdfArray dif = new PdfArray();
+ boolean gap = true;
+ for (int k = firstChar; k <= lastChar; ++k) {
+ if (shortTag[k] != 0) {
+ if (gap) {
+ dif.add(new PdfNumber(k));
+ gap = false;
+ }
+ dif.add(new PdfName(differences[k]));
+ }
+ else
+ gap = true;
+ }
+ enc.put(PdfName.DIFFERENCES, dif);
+ dic.put(PdfName.ENCODING, enc);
+ }
+ }
+ dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar));
+ dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar));
+ PdfArray wd = new PdfArray();
+ for (int k = firstChar; k <= lastChar; ++k) {
+ if (shortTag[k] == 0)
+ wd.add(new PdfNumber(0));
+ else
+ wd.add(new PdfNumber(widths[k]));
+ }
+ dic.put(PdfName.WIDTHS, wd);
+ if (fontDescriptor != null)
+ dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor);
+ return dic;
+ }
+
+ protected byte[] getFullFont() throws IOException {
+ RandomAccessFileOrArray rf2 = null;
+ try {
+ rf2 = new RandomAccessFileOrArray(rf);
+ rf2.reOpen();
+ byte b[] = new byte[rf2.length()];
+ rf2.readFully(b);
+ return b;
+ }
+ finally {
+ try {rf2.close();} catch (Exception e) {}
+ }
+ }
+
+ protected static int[] compactRanges(ArrayList ranges) {
+ ArrayList simp = new ArrayList();
+ for (int k = 0; k < ranges.size(); ++k) {
+ int[] r = (int[])ranges.get(k);
+ for (int j = 0; j < r.length; j += 2) {
+ simp.add(new int[]{Math.max(0, Math.min(r[j], r[j + 1])), Math.min(0xffff, Math.max(r[j], r[j + 1]))});
+ }
+ }
+ for (int k1 = 0; k1 < simp.size() - 1; ++k1) {
+ for (int k2 = k1 + 1; k2 < simp.size(); ++k2) {
+ int[] r1 = (int[])simp.get(k1);
+ int[] r2 = (int[])simp.get(k2);
+ if ((r1[0] >= r2[0] && r1[0] <= r2[1]) || (r1[1] >= r2[0] && r1[0] <= r2[1])) {
+ r1[0] = Math.min(r1[0], r2[0]);
+ r1[1] = Math.max(r1[1], r2[1]);
+ simp.remove(k2);
+ --k2;
+ }
+ }
+ }
+ int[] s = new int[simp.size() * 2];
+ for (int k = 0; k < simp.size(); ++k) {
+ int[] r = (int[])simp.get(k);
+ s[k * 2] = r[0];
+ s[k * 2 + 1] = r[1];
+ }
+ return s;
+ }
+
+ protected void addRangeUni(HashMap longTag, boolean includeMetrics, boolean subsetp) {
+ if (!subsetp && (subsetRanges != null || directoryOffset > 0)) {
+ int[] rg = (subsetRanges == null && directoryOffset > 0) ? new int[]{0, 0xffff} : compactRanges(subsetRanges);
+ HashMap usemap;
+ if (!fontSpecific && cmap31 != null)
+ usemap = cmap31;
+ else if (fontSpecific && cmap10 != null)
+ usemap = cmap10;
+ else if (cmap31 != null)
+ usemap = cmap31;
+ else
+ usemap = cmap10;
+ for (Iterator it = usemap.entrySet().iterator(); it.hasNext();) {
+ Map.Entry e = (Map.Entry)it.next();
+ int[] v = (int[])e.getValue();
+ Integer gi = new Integer(v[0]);
+ if (longTag.containsKey(gi))
+ continue;
+ int c = ((Integer)e.getKey()).intValue();
+ boolean skip = true;
+ for (int k = 0; k < rg.length; k += 2) {
+ if (c >= rg[k] && c <= rg[k + 1]) {
+ skip = false;
+ break;
+ }
+ }
+ if (!skip)
+ longTag.put(gi, includeMetrics ? new int[]{v[0], v[1], c} : null);
+ }
+ }
+ }
+
+ /** Outputs to the writer the font dictionaries and streams.
+ * @param writer the writer for this document
+ * @param ref the font indirect reference
+ * @param params several parameters that depend on the font type
+ * @throws IOException on error
+ * @throws DocumentException error in generating the object
+ */
+ void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException {
+ int firstChar = ((Integer)params[0]).intValue();
+ int lastChar = ((Integer)params[1]).intValue();
+ byte shortTag[] = (byte[])params[2];
+ boolean subsetp = ((Boolean)params[3]).booleanValue() && subset;
+
+ if (!subsetp) {
+ firstChar = 0;
+ lastChar = shortTag.length - 1;
+ for (int k = 0; k < shortTag.length; ++k)
+ shortTag[k] = 1;
+ }
+ PdfIndirectReference ind_font = null;
+ PdfObject pobj = null;
+ PdfIndirectObject obj = null;
+ String subsetPrefix = "";
+ if (embedded) {
+ if (cff) {
+ RandomAccessFileOrArray rf2 = new RandomAccessFileOrArray(rf);
+ byte b[] = new byte[cffLength];
+ try {
+ rf2.reOpen();
+ rf2.seek(cffOffset);
+ rf2.readFully(b);
+ }
+ finally {
+ try {
+ rf2.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ pobj = new StreamFont(b, "Type1C");
+ obj = writer.addToBody(pobj);
+ ind_font = obj.getIndirectReference();
+ }
+ else {
+ if (subsetp)
+ subsetPrefix = createSubsetPrefix();
+ HashMap glyphs = new HashMap();
+ for (int k = firstChar; k <= lastChar; ++k) {
+ if (shortTag[k] != 0) {
+ int metrics[];
+ if (fontSpecific)
+ metrics = getMetricsTT(k);
+ else
+ metrics = getMetricsTT(unicodeDifferences[k]);
+ if (metrics != null)
+ glyphs.put(new Integer(metrics[0]), null);
+ }
+ }
+ addRangeUni(glyphs, false, subsetp);
+ byte[] b = null;
+ if (subsetp || directoryOffset != 0 || subsetRanges != null) {
+ TrueTypeFontSubSet sb = new TrueTypeFontSubSet(fileName, new RandomAccessFileOrArray(rf), glyphs, directoryOffset, true, !subsetp);
+ b = sb.process();
+ }
+ else {
+ b = getFullFont();
+ }
+ int lengths[] = new int[]{b.length};
+ pobj = new StreamFont(b, lengths);
+ obj = writer.addToBody(pobj);
+ ind_font = obj.getIndirectReference();
+ }
+ }
+ pobj = getFontDescriptor(ind_font, subsetPrefix);
+ if (pobj != null){
+ obj = writer.addToBody(pobj);
+ ind_font = obj.getIndirectReference();
+ }
+ pobj = getFontBaseType(ind_font, subsetPrefix, firstChar, lastChar, shortTag);
+ writer.addToBody(pobj, ref);
+ }
+
+ /** Gets the font parameter identified by key
. Valid values
+ * for key
are ASCENT
, CAPHEIGHT
, DESCENT
+ * and ITALICANGLE
.
+ * @param key the parameter to be extracted
+ * @param fontSize the font size in points
+ * @return the parameter in points
+ */
+ public float getFontDescriptor(int key, float fontSize) {
+ switch (key) {
+ case ASCENT:
+ return (float)os_2.sTypoAscender * fontSize / (float)head.unitsPerEm;
+ case CAPHEIGHT:
+ return (float)os_2.sCapHeight * fontSize / (float)head.unitsPerEm;
+ case DESCENT:
+ return (float)os_2.sTypoDescender * fontSize / (float)head.unitsPerEm;
+ case ITALICANGLE:
+ return (float)italicAngle;
+ case BBOXLLX:
+ return fontSize * (int)head.xMin / head.unitsPerEm;
+ case BBOXLLY:
+ return fontSize * (int)head.yMin / head.unitsPerEm;
+ case BBOXURX:
+ return fontSize * (int)head.xMax / head.unitsPerEm;
+ case BBOXURY:
+ return fontSize * (int)head.yMax / head.unitsPerEm;
+ case AWT_ASCENT:
+ return fontSize * (int)hhea.Ascender / head.unitsPerEm;
+ case AWT_DESCENT:
+ return fontSize * (int)hhea.Descender / head.unitsPerEm;
+ case AWT_LEADING:
+ return fontSize * (int)hhea.LineGap / head.unitsPerEm;
+ case AWT_MAXADVANCE:
+ return fontSize * (int)hhea.advanceWidthMax / head.unitsPerEm;
+ }
+ return 0;
+ }
+
+ /** Gets the glyph index and metrics for a character.
+ * @param c the character
+ * @return an int
array with {glyph index, width}
+ */
+ public int[] getMetricsTT(int c) {
+ if (!fontSpecific && cmap31 != null)
+ return (int[])cmap31.get(new Integer(c));
+ if (fontSpecific && cmap10 != null)
+ return (int[])cmap10.get(new Integer(c));
+ if (cmap31 != null)
+ return (int[])cmap31.get(new Integer(c));
+ if (cmap10 != null)
+ return (int[])cmap10.get(new Integer(c));
+ return null;
+ }
+
+ /** Gets the postscript font name.
+ * @return the postscript font name
+ */
+ public String getPostscriptFontName() {
+ return fontName;
+ }
+
+ /** Gets the code pages supported by the font.
+ * @return the code pages supported by the font
+ */
+ public String[] getCodePagesSupported() {
+ long cp = (((long)os_2.ulCodePageRange2) << 32) + ((long)os_2.ulCodePageRange1 & 0xffffffffL);
+ int count = 0;
+ long bit = 1;
+ for (int k = 0; k < 64; ++k) {
+ if ((cp & bit) != 0 && codePages[k] != null)
+ ++count;
+ bit <<= 1;
+ }
+ String ret[] = new String[count];
+ count = 0;
+ bit = 1;
+ for (int k = 0; k < 64; ++k) {
+ if ((cp & bit) != 0 && codePages[k] != null)
+ ret[count++] = codePages[k];
+ bit <<= 1;
+ }
+ return ret;
+ }
+
+ /** Gets the full name of the font. If it is a True Type font
+ * each array element will have {Platform ID, Platform Encoding ID,
+ * Language ID, font name}. The interpretation of this values can be
+ * found in the Open Type specification, chapter 2, in the 'name' table.
+ * For the other fonts the array has a single element with {"", "", "",
+ * font name}.
+ * @return the full name of the font
+ */
+ public String[][] getFullFontName() {
+ return fullName;
+ }
+
+ /** Gets the family name of the font. If it is a True Type font
+ * each array element will have {Platform ID, Platform Encoding ID,
+ * Language ID, font name}. The interpretation of this values can be
+ * found in the Open Type specification, chapter 2, in the 'name' table.
+ * For the other fonts the array has a single element with {"", "", "",
+ * font name}.
+ * @return the family name of the font
+ */
+ public String[][] getFamilyFontName() {
+ return familyName;
+ }
+
+ /** Checks if the font has any kerning pairs.
+ * @return true
if the font has any kerning pairs
+ */
+ public boolean hasKernPairs() {
+ return kerning.size() > 0;
+ }
+
+ /**
+ * Sets the font name that will appear in the pdf font dictionary.
+ * Use with care as it can easily make a font unreadable if not embedded.
+ * @param name the new font name
+ */
+ public void setPostscriptFontName(String name) {
+ fontName = name;
+ }
+
+ /**
+ * Sets the kerning between two Unicode chars.
+ * @param char1 the first char
+ * @param char2 the second char
+ * @param kern the kerning to apply in normalized 1000 units
+ * @return true
if the kerning was applied, false
otherwise
+ */
+ public boolean setKerning(char char1, char char2, int kern) {
+ int metrics[] = getMetricsTT(char1);
+ if (metrics == null)
+ return false;
+ int c1 = metrics[0];
+ metrics = getMetricsTT(char2);
+ if (metrics == null)
+ return false;
+ int c2 = metrics[0];
+ kerning.put((c1 << 16) + c2, kern);
+ return true;
+ }
+
+ protected int[] getRawCharBBox(int c, String name) {
+ HashMap map = null;
+ if (name == null || cmap31 == null)
+ map = cmap10;
+ else
+ map = cmap31;
+ if (map == null)
+ return null;
+ int metric[] = (int[])map.get(new Integer(c));
+ if (metric == null || bboxes == null)
+ return null;
+ return bboxes[metric[0]];
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/TrueTypeFontSubSet.java b/src/main/java/com/lowagie/text/pdf/TrueTypeFontSubSet.java
new file mode 100644
index 0000000..5107b75
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/TrueTypeFontSubSet.java
@@ -0,0 +1,428 @@
+/*
+ * $Id: TrueTypeFontSubSet.java,v 1.48 2006/02/23 16:45:49 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.ExceptionConverter;
+
+/** Subsets a True Type font by removing the unneeded glyphs from
+ * the font.
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+class TrueTypeFontSubSet {
+ static final String tableNamesSimple[] = {"cvt ", "fpgm", "glyf", "head",
+ "hhea", "hmtx", "loca", "maxp", "prep"};
+ static final String tableNamesCmap[] = {"cmap", "cvt ", "fpgm", "glyf", "head",
+ "hhea", "hmtx", "loca", "maxp", "prep"};
+ static final String tableNamesExtra[] = {"OS/2", "cmap", "cvt ", "fpgm", "glyf", "head",
+ "hhea", "hmtx", "loca", "maxp", "name, prep"};
+ static final int entrySelectors[] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4};
+ static final int TABLE_CHECKSUM = 0;
+ static final int TABLE_OFFSET = 1;
+ static final int TABLE_LENGTH = 2;
+ static final int HEAD_LOCA_FORMAT_OFFSET = 51;
+
+ static final int ARG_1_AND_2_ARE_WORDS = 1;
+ static final int WE_HAVE_A_SCALE = 8;
+ static final int MORE_COMPONENTS = 32;
+ static final int WE_HAVE_AN_X_AND_Y_SCALE = 64;
+ static final int WE_HAVE_A_TWO_BY_TWO = 128;
+
+
+ /** Contains the location of the several tables. The key is the name of
+ * the table and the value is an int[3]
where position 0
+ * is the checksum, position 1 is the offset from the start of the file
+ * and position 2 is the length of the table.
+ */
+ protected HashMap tableDirectory;
+ /** The file in use.
+ */
+ protected RandomAccessFileOrArray rf;
+ /** The file name.
+ */
+ protected String fileName;
+ protected boolean includeCmap;
+ protected boolean includeExtras;
+ protected boolean locaShortTable;
+ protected int locaTable[];
+ protected HashMap glyphsUsed;
+ protected ArrayList glyphsInList;
+ protected int tableGlyphOffset;
+ protected int newLocaTable[];
+ protected byte newLocaTableOut[];
+ protected byte newGlyfTable[];
+ protected int glyfTableRealSize;
+ protected int locaTableRealSize;
+ protected byte outFont[];
+ protected int fontPtr;
+ protected int directoryOffset;
+
+ /** Creates a new TrueTypeFontSubSet
+ * @param directoryOffset The offset from the start of the file to the table directory
+ * @param fileName the file name of the font
+ * @param glyphsUsed the glyphs used
+ * @param includeCmap true
if the table cmap is to be included in the generated font
+ */
+ TrueTypeFontSubSet(String fileName, RandomAccessFileOrArray rf, HashMap glyphsUsed, int directoryOffset, boolean includeCmap, boolean includeExtras) {
+ this.fileName = fileName;
+ this.rf = rf;
+ this.glyphsUsed = glyphsUsed;
+ this.includeCmap = includeCmap;
+ this.includeExtras = includeExtras;
+ this.directoryOffset = directoryOffset;
+ glyphsInList = new ArrayList(glyphsUsed.keySet());
+ }
+
+ /** Does the actual work of subsetting the font.
+ * @throws IOException on error
+ * @throws DocumentException on error
+ * @return the subset font
+ */
+ byte[] process() throws IOException, DocumentException {
+ try {
+ rf.reOpen();
+ createTableDirectory();
+ readLoca();
+ flatGlyphs();
+ createNewGlyphTables();
+ locaTobytes();
+ assembleFont();
+ return outFont;
+ }
+ finally {
+ try {
+ rf.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ }
+
+ protected void assembleFont() throws IOException, DocumentException {
+ int tableLocation[];
+ int fullFontSize = 0;
+ String tableNames[];
+ if (includeExtras)
+ tableNames = tableNamesExtra;
+ else {
+ if (includeCmap)
+ tableNames = tableNamesCmap;
+ else
+ tableNames = tableNamesSimple;
+ }
+ int tablesUsed = 2;
+ int len = 0;
+ for (int k = 0; k < tableNames.length; ++k) {
+ String name = tableNames[k];
+ if (name.equals("glyf") || name.equals("loca"))
+ continue;
+ tableLocation = (int[])tableDirectory.get(name);
+ if (tableLocation == null)
+ continue;
+ ++tablesUsed;
+ fullFontSize += (tableLocation[TABLE_LENGTH] + 3) & (~3);
+ }
+ fullFontSize += newLocaTableOut.length;
+ fullFontSize += newGlyfTable.length;
+ int ref = 16 * tablesUsed + 12;
+ fullFontSize += ref;
+ outFont = new byte[fullFontSize];
+ fontPtr = 0;
+ writeFontInt(0x00010000);
+ writeFontShort(tablesUsed);
+ int selector = entrySelectors[tablesUsed];
+ writeFontShort((1 << selector) * 16);
+ writeFontShort(selector);
+ writeFontShort((tablesUsed - (1 << selector)) * 16);
+ for (int k = 0; k < tableNames.length; ++k) {
+ String name = tableNames[k];
+ tableLocation = (int[])tableDirectory.get(name);
+ if (tableLocation == null)
+ continue;
+ writeFontString(name);
+ if (name.equals("glyf")) {
+ writeFontInt(calculateChecksum(newGlyfTable));
+ len = glyfTableRealSize;
+ }
+ else if (name.equals("loca")) {
+ writeFontInt(calculateChecksum(newLocaTableOut));
+ len = locaTableRealSize;
+ }
+ else {
+ writeFontInt(tableLocation[TABLE_CHECKSUM]);
+ len = tableLocation[TABLE_LENGTH];
+ }
+ writeFontInt(ref);
+ writeFontInt(len);
+ ref += (len + 3) & (~3);
+ }
+ for (int k = 0; k < tableNames.length; ++k) {
+ String name = tableNames[k];
+ tableLocation = (int[])tableDirectory.get(name);
+ if (tableLocation == null)
+ continue;
+ if (name.equals("glyf")) {
+ System.arraycopy(newGlyfTable, 0, outFont, fontPtr, newGlyfTable.length);
+ fontPtr += newGlyfTable.length;
+ newGlyfTable = null;
+ }
+ else if (name.equals("loca")) {
+ System.arraycopy(newLocaTableOut, 0, outFont, fontPtr, newLocaTableOut.length);
+ fontPtr += newLocaTableOut.length;
+ newLocaTableOut = null;
+ }
+ else {
+ rf.seek(tableLocation[TABLE_OFFSET]);
+ rf.readFully(outFont, fontPtr, tableLocation[TABLE_LENGTH]);
+ fontPtr += (tableLocation[TABLE_LENGTH] + 3) & (~3);
+ }
+ }
+ }
+
+ protected void createTableDirectory() throws IOException, DocumentException {
+ tableDirectory = new HashMap();
+ rf.seek(directoryOffset);
+ int id = rf.readInt();
+ if (id != 0x00010000)
+ throw new DocumentException(fileName + " is not a true type file.");
+ int num_tables = rf.readUnsignedShort();
+ rf.skipBytes(6);
+ for (int k = 0; k < num_tables; ++k) {
+ String tag = readStandardString(4);
+ int tableLocation[] = new int[3];
+ tableLocation[TABLE_CHECKSUM] = rf.readInt();
+ tableLocation[TABLE_OFFSET] = rf.readInt();
+ tableLocation[TABLE_LENGTH] = rf.readInt();
+ tableDirectory.put(tag, tableLocation);
+ }
+ }
+
+ protected void readLoca() throws IOException, DocumentException {
+ int tableLocation[];
+ tableLocation = (int[])tableDirectory.get("head");
+ if (tableLocation == null)
+ throw new DocumentException("Table 'head' does not exist in " + fileName);
+ rf.seek(tableLocation[TABLE_OFFSET] + HEAD_LOCA_FORMAT_OFFSET);
+ locaShortTable = (rf.readUnsignedShort() == 0);
+ tableLocation = (int[])tableDirectory.get("loca");
+ if (tableLocation == null)
+ throw new DocumentException("Table 'loca' does not exist in " + fileName);
+ rf.seek(tableLocation[TABLE_OFFSET]);
+ if (locaShortTable) {
+ int entries = tableLocation[TABLE_LENGTH] / 2;
+ locaTable = new int[entries];
+ for (int k = 0; k < entries; ++k)
+ locaTable[k] = rf.readUnsignedShort() * 2;
+ }
+ else {
+ int entries = tableLocation[TABLE_LENGTH] / 4;
+ locaTable = new int[entries];
+ for (int k = 0; k < entries; ++k)
+ locaTable[k] = rf.readInt();
+ }
+ }
+
+ protected void createNewGlyphTables() throws IOException {
+ newLocaTable = new int[locaTable.length];
+ int activeGlyphs[] = new int[glyphsInList.size()];
+ for (int k = 0; k < activeGlyphs.length; ++k)
+ activeGlyphs[k] = ((Integer)glyphsInList.get(k)).intValue();
+ Arrays.sort(activeGlyphs);
+ int glyfSize = 0;
+ for (int k = 0; k < activeGlyphs.length; ++k) {
+ int glyph = activeGlyphs[k];
+ glyfSize += locaTable[glyph + 1] - locaTable[glyph];
+ }
+ glyfTableRealSize = glyfSize;
+ glyfSize = (glyfSize + 3) & (~3);
+ newGlyfTable = new byte[glyfSize];
+ int glyfPtr = 0;
+ int listGlyf = 0;
+ for (int k = 0; k < newLocaTable.length; ++k) {
+ newLocaTable[k] = glyfPtr;
+ if (listGlyf < activeGlyphs.length && activeGlyphs[listGlyf] == k) {
+ ++listGlyf;
+ newLocaTable[k] = glyfPtr;
+ int start = locaTable[k];
+ int len = locaTable[k + 1] - start;
+ if (len > 0) {
+ rf.seek(tableGlyphOffset + start);
+ rf.readFully(newGlyfTable, glyfPtr, len);
+ glyfPtr += len;
+ }
+ }
+ }
+ }
+
+ protected void locaTobytes() {
+ if (locaShortTable)
+ locaTableRealSize = newLocaTable.length * 2;
+ else
+ locaTableRealSize = newLocaTable.length * 4;
+ newLocaTableOut = new byte[(locaTableRealSize + 3) & (~3)];
+ outFont = newLocaTableOut;
+ fontPtr = 0;
+ for (int k = 0; k < newLocaTable.length; ++k) {
+ if (locaShortTable)
+ writeFontShort(newLocaTable[k] / 2);
+ else
+ writeFontInt(newLocaTable[k]);
+ }
+
+ }
+
+ protected void flatGlyphs() throws IOException, DocumentException {
+ int tableLocation[];
+ tableLocation = (int[])tableDirectory.get("glyf");
+ if (tableLocation == null)
+ throw new DocumentException("Table 'glyf' does not exist in " + fileName);
+ Integer glyph0 = new Integer(0);
+ if (!glyphsUsed.containsKey(glyph0)) {
+ glyphsUsed.put(glyph0, null);
+ glyphsInList.add(glyph0);
+ }
+ tableGlyphOffset = tableLocation[TABLE_OFFSET];
+ for (int k = 0; k < glyphsInList.size(); ++k) {
+ int glyph = ((Integer)glyphsInList.get(k)).intValue();
+ checkGlyphComposite(glyph);
+ }
+ }
+
+ protected void checkGlyphComposite(int glyph) throws IOException {
+ int start = locaTable[glyph];
+ if (start == locaTable[glyph + 1]) // no contour
+ return;
+ rf.seek(tableGlyphOffset + start);
+ int numContours = rf.readShort();
+ if (numContours >= 0)
+ return;
+ rf.skipBytes(8);
+ for(;;) {
+ int flags = rf.readUnsignedShort();
+ Integer cGlyph = new Integer(rf.readUnsignedShort());
+ if (!glyphsUsed.containsKey(cGlyph)) {
+ glyphsUsed.put(cGlyph, null);
+ glyphsInList.add(cGlyph);
+ }
+ if ((flags & MORE_COMPONENTS) == 0)
+ return;
+ int skip;
+ if ((flags & ARG_1_AND_2_ARE_WORDS) != 0)
+ skip = 4;
+ else
+ skip = 2;
+ if ((flags & WE_HAVE_A_SCALE) != 0)
+ skip += 2;
+ else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0)
+ skip += 4;
+ if ((flags & WE_HAVE_A_TWO_BY_TWO) != 0)
+ skip += 8;
+ rf.skipBytes(skip);
+ }
+ }
+
+ /** Reads a String
from the font file as bytes using the Cp1252
+ * encoding.
+ * @param length the length of bytes to read
+ * @return the String
read
+ * @throws IOException the font file could not be read
+ */
+ protected String readStandardString(int length) throws IOException {
+ byte buf[] = new byte[length];
+ rf.readFully(buf);
+ try {
+ return new String(buf, BaseFont.WINANSI);
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ protected void writeFontShort(int n) {
+ outFont[fontPtr++] = (byte)(n >> 8);
+ outFont[fontPtr++] = (byte)(n);
+ }
+
+ protected void writeFontInt(int n) {
+ outFont[fontPtr++] = (byte)(n >> 24);
+ outFont[fontPtr++] = (byte)(n >> 16);
+ outFont[fontPtr++] = (byte)(n >> 8);
+ outFont[fontPtr++] = (byte)(n);
+ }
+
+ protected void writeFontString(String s) {
+ byte b[] = PdfEncodings.convertToBytes(s, BaseFont.WINANSI);
+ System.arraycopy(b, 0, outFont, fontPtr, b.length);
+ fontPtr += b.length;
+ }
+
+ protected int calculateChecksum(byte b[]) {
+ int len = b.length / 4;
+ int v0 = 0;
+ int v1 = 0;
+ int v2 = 0;
+ int v3 = 0;
+ int ptr = 0;
+ for (int k = 0; k < len; ++k) {
+ v3 += (int)b[ptr++] & 0xff;
+ v2 += (int)b[ptr++] & 0xff;
+ v1 += (int)b[ptr++] & 0xff;
+ v0 += (int)b[ptr++] & 0xff;
+ }
+ return v0 + (v1 << 8) + (v2 << 16) + (v3 << 24);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/TrueTypeFontUnicode.java b/src/main/java/com/lowagie/text/pdf/TrueTypeFontUnicode.java
new file mode 100644
index 0000000..a05f744
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/TrueTypeFontUnicode.java
@@ -0,0 +1,456 @@
+/*
+ * $Id: TrueTypeFontUnicode.java,v 1.53 2006/02/23 16:45:48 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.ArrayList;
+
+import com.lowagie.text.DocumentException;
+/** Represents a True Type font with Unicode encoding. All the character
+ * in the font can be used directly by using the encoding Identity-H or
+ * Identity-V. This is the only way to represent some character sets such
+ * as Thai.
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+class TrueTypeFontUnicode extends TrueTypeFont implements Comparator{
+
+ /** true
if the encoding is vertical.
+ */
+ boolean vertical = false;
+
+ /** Creates a new TrueType font addressed by Unicode characters. The font
+ * will always be embedded.
+ * @param ttFile the location of the font on file. The file must end in '.ttf'.
+ * The modifiers after the name are ignored.
+ * @param enc the encoding to be applied to this font
+ * @param emb true if the font is to be embedded in the PDF
+ * @param ttfAfm the font as a byte
array
+ * @throws DocumentException the font is invalid
+ * @throws IOException the font file could not be read
+ */
+ TrueTypeFontUnicode(String ttFile, String enc, boolean emb, byte ttfAfm[]) throws DocumentException, IOException {
+ String nameBase = getBaseName(ttFile);
+ String ttcName = getTTCName(nameBase);
+ if (nameBase.length() < ttFile.length()) {
+ style = ttFile.substring(nameBase.length());
+ }
+ encoding = enc;
+ embedded = emb;
+ fileName = ttcName;
+ ttcIndex = "";
+ if (ttcName.length() < nameBase.length())
+ ttcIndex = nameBase.substring(ttcName.length() + 1);
+ fontType = FONT_TYPE_TTUNI;
+ if ((fileName.toLowerCase().endsWith(".ttf") || fileName.toLowerCase().endsWith(".otf") || fileName.toLowerCase().endsWith(".ttc")) && ((enc.equals(IDENTITY_H) || enc.equals(IDENTITY_V)) && emb)) {
+ process(ttfAfm);
+ if (os_2.fsType == 2)
+ throw new DocumentException(fileName + style + " cannot be embedded due to licensing restrictions.");
+ // Sivan
+ if ((cmap31 == null && !fontSpecific) || (cmap10 == null && fontSpecific))
+ directTextToByte=true;
+ //throw new DocumentException(fileName + " " + style + " does not contain an usable cmap.");
+ if (fontSpecific) {
+ fontSpecific = false;
+ String tempEncoding = encoding;
+ encoding = "";
+ createEncoding();
+ encoding = tempEncoding;
+ fontSpecific = true;
+ }
+ }
+ else
+ throw new DocumentException(fileName + " " + style + " is not a TTF font file.");
+ vertical = enc.endsWith("V");
+ }
+
+/**
+ * Gets the width of a String
in normalized 1000 units.
+ * @param text the String
to get the witdth of
+ * @return the width in normalized 1000 units
+ */
+ public int getWidth(String text)
+ {
+ if (vertical)
+ return text.length() * 1000;
+ int total = 0;
+ if (fontSpecific) {
+ char cc[] = text.toCharArray();
+ int len = cc.length;
+ for (int k = 0; k < len; ++k) {
+ char c = cc[k];
+ if ((c & 0xff00) == 0 || (c & 0xff00) == 0xf000)
+ total += getRawWidth(c & 0xff, null);
+ }
+ }
+ else {
+ int len = text.length();
+ for (int k = 0; k < len; ++k)
+ total += getRawWidth(text.charAt(k), encoding);
+ }
+ return total;
+ }
+
+ /** Creates a ToUnicode CMap to allow copy and paste from Acrobat.
+ * @param metrics metrics[0] contains the glyph index and metrics[2]
+ * contains the Unicode code
+ * @throws DocumentException on error
+ * @return the stream representing this CMap or null
+ */
+ private PdfStream getToUnicode(Object metrics[]) throws DocumentException {
+ if (metrics.length == 0)
+ return null;
+ StringBuffer buf = new StringBuffer(
+ "/CIDInit /ProcSet findresource begin\n" +
+ "12 dict begin\n" +
+ "begincmap\n" +
+ "/CIDSystemInfo\n" +
+ "<< /Registry (Adobe)\n" +
+ "/Ordering (UCS)\n" +
+ "/Supplement 0\n" +
+ ">> def\n" +
+ "/CMapName /Adobe-Identity-UCS def\n" +
+ "/CMapType 2 def\n" +
+ "1 begincodespacerange\n" +
+ "<0000>null
+ */
+ byte[] convertToBytes(String text)
+ {
+ return null;
+ }
+
+ /**
+ * Checks if a character exists in this font.
+ * @param c the character to check
+ * @return true
if the character has a glyph,
+ * false
otherwise
+ */
+ public boolean charExists(char c) {
+ HashMap map = null;
+ if (fontSpecific)
+ map = cmap10;
+ else
+ map = cmap31;
+ if (map == null)
+ return false;
+ if (fontSpecific) {
+ if ((c & 0xff00) == 0 || (c & 0xff00) == 0xf000)
+ return map.get(new Integer(c & 0xff)) != null;
+ else
+ return false;
+ }
+ else
+ return map.get(new Integer(c)) != null;
+ }
+
+ /**
+ * Sets the character advance.
+ * @param c the character
+ * @param advance the character advance normalized to 1000 units
+ * @return true
if the advance was set,
+ * false
otherwise
+ */
+ public boolean setCharAdvance(char c, int advance) {
+ HashMap map = null;
+ if (fontSpecific)
+ map = cmap10;
+ else
+ map = cmap31;
+ if (map == null)
+ return false;
+ int m[] = null;
+ if (fontSpecific) {
+ if ((c & 0xff00) == 0 || (c & 0xff00) == 0xf000)
+ m = (int[])map.get(new Integer(c & 0xff));
+ else
+ return false;
+ }
+ else
+ m = (int[])map.get(new Integer(c));
+ if (m == null)
+ return false;
+ else
+ m[1] = advance;
+ return true;
+ }
+
+ public int[] getCharBBox(char c) {
+ if (bboxes == null)
+ return null;
+ HashMap map = null;
+ if (fontSpecific)
+ map = cmap10;
+ else
+ map = cmap31;
+ if (map == null)
+ return null;
+ int m[] = null;
+ if (fontSpecific) {
+ if ((c & 0xff00) == 0 || (c & 0xff00) == 0xf000)
+ m = (int[])map.get(new Integer(c & 0xff));
+ else
+ return null;
+ }
+ else
+ m = (int[])map.get(new Integer(c));
+ if (m == null)
+ return null;
+ return bboxes[m[0]];
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/Type1Font.java b/src/main/java/com/lowagie/text/pdf/Type1Font.java
new file mode 100644
index 0000000..d73ce61
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/Type1Font.java
@@ -0,0 +1,807 @@
+/*
+ * $Id: Type1Font.java,v 1.65 2006/02/23 16:45:49 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.DocumentException;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import com.lowagie.text.pdf.fonts.FontsResourceAnchor;
+import java.io.*;
+
+/** Reads a Type1 font
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+class Type1Font extends BaseFont
+{
+ private static FontsResourceAnchor resourceAnchor;
+
+ /** The PFB file if the input was made with a byte
array.
+ */
+ protected byte pfb[];
+/** The Postscript font name.
+ */
+ private String FontName;
+/** The full name of the font.
+ */
+ private String FullName;
+/** The family name of the font.
+ */
+ private String FamilyName;
+/** The weight of the font: normal, bold, etc.
+ */
+ private String Weight = "";
+/** The italic angle of the font, usually 0.0 or negative.
+ */
+ private float ItalicAngle = 0.0f;
+/** true
if all the characters have the same
+ * width.
+ */
+ private boolean IsFixedPitch = false;
+/** The character set of the font.
+ */
+ private String CharacterSet;
+/** The llx of the FontBox.
+ */
+ private int llx = -50;
+/** The lly of the FontBox.
+ */
+ private int lly = -200;
+/** The lurx of the FontBox.
+ */
+ private int urx = 1000;
+/** The ury of the FontBox.
+ */
+ private int ury = 900;
+/** The underline position.
+ */
+ private int UnderlinePosition = -100;
+/** The underline thickness.
+ */
+ private int UnderlineThickness = 50;
+/** The font's encoding name. This encoding is 'StandardEncoding' or
+ * 'AdobeStandardEncoding' for a font that can be totally encoded
+ * according to the characters names. For all other names the
+ * font is treated as symbolic.
+ */
+ private String EncodingScheme = "FontSpecific";
+/** A variable.
+ */
+ private int CapHeight = 700;
+/** A variable.
+ */
+ private int XHeight = 480;
+/** A variable.
+ */
+ private int Ascender = 800;
+/** A variable.
+ */
+ private int Descender = -200;
+/** A variable.
+ */
+ private int StdHW;
+/** A variable.
+ */
+ private int StdVW = 80;
+
+/** Represents the section CharMetrics in the AFM file. Each
+ * value of this array contains a Object[4]
with an
+ * Integer, Integer, String and int[]. This is the code, width, name and char bbox.
+ * The key is the name of the char and also an Integer with the char number.
+ */
+ private HashMap CharMetrics = new HashMap();
+/** Represents the section KernPairs in the AFM file. The key is
+ * the name of the first character and the value is a Object[]
+ * with 2 elements for each kern pair. Position 0 is the name of
+ * the second character and position 1 is the kerning distance. This is
+ * repeated for all the pairs.
+ */
+ private HashMap KernPairs = new HashMap();
+/** The file in use.
+ */
+ private String fileName;
+/** true
if this font is one of the 14 built in fonts.
+ */
+ private boolean builtinFont = false;
+/** Types of records in a PFB file. ASCII is 1 and BINARY is 2.
+ * They have to appear in the PFB file in this sequence.
+ */
+ private static final int pfbTypes[] = {1, 2, 1};
+
+ /** Creates a new Type1 font.
+ * @param ttfAfm the AFM file if the input is made with a byte
array
+ * @param pfb the PFB file if the input is made with a byte
array
+ * @param afmFile the name of one of the 14 built-in fonts or the location of an AFM file. The file must end in '.afm'
+ * @param enc the encoding to be applied to this font
+ * @param emb true if the font is to be embedded in the PDF
+ * @throws DocumentException the AFM file is invalid
+ * @throws IOException the AFM file could not be read
+ */
+ Type1Font(String afmFile, String enc, boolean emb, byte ttfAfm[], byte pfb[]) throws DocumentException, IOException
+ {
+ if (emb && ttfAfm != null && pfb == null)
+ throw new DocumentException("Two byte arrays are needed if the Type1 font is embedded.");
+ if (emb && ttfAfm != null)
+ this.pfb = pfb;
+ encoding = enc;
+ embedded = emb;
+ fileName = afmFile;
+ fontType = FONT_TYPE_T1;
+ RandomAccessFileOrArray rf = null;
+ InputStream is = null;
+ if (BuiltinFonts14.containsKey(afmFile)) {
+ embedded = false;
+ builtinFont = true;
+ byte buf[] = new byte[1024];
+ try {
+ if (resourceAnchor == null)
+ resourceAnchor = new FontsResourceAnchor();
+ is = getResourceStream(RESOURCE_PATH + afmFile + ".afm", resourceAnchor.getClass().getClassLoader());
+ if (is == null) {
+ String msg = afmFile + " not found as resource. (The *.afm files must exist as resources in the package com.lowagie.text.pdf.fonts)";
+ System.err.println(msg);
+ throw new DocumentException(msg);
+ }
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ while (true) {
+ int size = is.read(buf);
+ if (size < 0)
+ break;
+ out.write(buf, 0, size);
+ }
+ buf = out.toByteArray();
+ }
+ finally {
+ if (is != null) {
+ try {
+ is.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ }
+ try {
+ rf = new RandomAccessFileOrArray(buf);
+ process(rf);
+ }
+ finally {
+ if (rf != null) {
+ try {
+ rf.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ }
+ }
+ else if (afmFile.toLowerCase().endsWith(".afm")) {
+ try {
+ if (ttfAfm == null)
+ rf = new RandomAccessFileOrArray(afmFile);
+ else
+ rf = new RandomAccessFileOrArray(ttfAfm);
+ process(rf);
+ }
+ finally {
+ if (rf != null) {
+ try {
+ rf.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ }
+ }
+ else if (afmFile.toLowerCase().endsWith(".pfm")) {
+ try {
+ ByteArrayOutputStream ba = new ByteArrayOutputStream();
+ if (ttfAfm == null)
+ rf = new RandomAccessFileOrArray(afmFile);
+ else
+ rf = new RandomAccessFileOrArray(ttfAfm);
+ Pfm2afm.convert(rf, ba);
+ rf.close();
+ rf = new RandomAccessFileOrArray(ba.toByteArray());
+ process(rf);
+ }
+ finally {
+ if (rf != null) {
+ try {
+ rf.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ }
+ }
+ else
+ throw new DocumentException(afmFile + " is not an AFM or PFM font file.");
+ try {
+ EncodingScheme = EncodingScheme.trim();
+ if (EncodingScheme.equals("AdobeStandardEncoding") || EncodingScheme.equals("StandardEncoding")) {
+ fontSpecific = false;
+ }
+ PdfEncodings.convertToBytes(" ", enc); // check if the encoding exists
+ createEncoding();
+ }
+ catch (RuntimeException re) {
+ throw re;
+ }
+ catch (Exception e) {
+ throw new DocumentException(e);
+ }
+ }
+
+/** Gets the width from the font according to the name
or,
+ * if the name
is null, meaning it is a symbolic font,
+ * the char c
.
+ * @param c the char if the font is symbolic
+ * @param name the glyph name
+ * @return the width of the char
+ */
+ int getRawWidth(int c, String name) {
+ Object metrics[];
+ if (name == null) { // font specific
+ metrics = (Object[])CharMetrics.get(new Integer(c));
+ }
+ else {
+ if (name.equals(".notdef"))
+ return 0;
+ metrics = (Object[])CharMetrics.get(name);
+ }
+ if (metrics != null)
+ return ((Integer)(metrics[1])).intValue();
+ return 0;
+ }
+
+/** Gets the kerning between two Unicode characters. The characters
+ * are converted to names and this names are used to find the kerning
+ * pairs in the HashMap
KernPairs
.
+ * @param char1 the first char
+ * @param char2 the second char
+ * @return the kerning to be applied
+ */
+ public int getKerning(char char1, char char2)
+ {
+ String first = GlyphList.unicodeToName((int)char1);
+ if (first == null)
+ return 0;
+ String second = GlyphList.unicodeToName((int)char2);
+ if (second == null)
+ return 0;
+ Object obj[] = (Object[])KernPairs.get(first);
+ if (obj == null)
+ return 0;
+ for (int k = 0; k < obj.length; k += 2) {
+ if (second.equals(obj[k]))
+ return ((Integer)obj[k + 1]).intValue();
+ }
+ return 0;
+ }
+
+
+ /** Reads the font metrics
+ * @param rf the AFM file
+ * @throws DocumentException the AFM file is invalid
+ * @throws IOException the AFM file could not be read
+ */
+ public void process(RandomAccessFileOrArray rf) throws DocumentException, IOException
+ {
+ String line;
+ boolean isMetrics = false;
+ while ((line = rf.readLine()) != null)
+ {
+ StringTokenizer tok = new StringTokenizer(line);
+ if (!tok.hasMoreTokens())
+ continue;
+ String ident = tok.nextToken();
+ if (ident.equals("FontName"))
+ FontName = tok.nextToken("\u00ff").substring(1);
+ else if (ident.equals("FullName"))
+ FullName = tok.nextToken("\u00ff").substring(1);
+ else if (ident.equals("FamilyName"))
+ FamilyName = tok.nextToken("\u00ff").substring(1);
+ else if (ident.equals("Weight"))
+ Weight = tok.nextToken("\u00ff").substring(1);
+ else if (ident.equals("ItalicAngle"))
+ ItalicAngle = Float.valueOf(tok.nextToken()).floatValue();
+ else if (ident.equals("IsFixedPitch"))
+ IsFixedPitch = tok.nextToken().equals("true");
+ else if (ident.equals("CharacterSet"))
+ CharacterSet = tok.nextToken("\u00ff").substring(1);
+ else if (ident.equals("FontBBox"))
+ {
+ llx = (int)Float.valueOf(tok.nextToken()).floatValue();
+ lly = (int)Float.valueOf(tok.nextToken()).floatValue();
+ urx = (int)Float.valueOf(tok.nextToken()).floatValue();
+ ury = (int)Float.valueOf(tok.nextToken()).floatValue();
+ }
+ else if (ident.equals("UnderlinePosition"))
+ UnderlinePosition = (int)Float.valueOf(tok.nextToken()).floatValue();
+ else if (ident.equals("UnderlineThickness"))
+ UnderlineThickness = (int)Float.valueOf(tok.nextToken()).floatValue();
+ else if (ident.equals("EncodingScheme"))
+ EncodingScheme = tok.nextToken("\u00ff").substring(1);
+ else if (ident.equals("CapHeight"))
+ CapHeight = (int)Float.valueOf(tok.nextToken()).floatValue();
+ else if (ident.equals("XHeight"))
+ XHeight = (int)Float.valueOf(tok.nextToken()).floatValue();
+ else if (ident.equals("Ascender"))
+ Ascender = (int)Float.valueOf(tok.nextToken()).floatValue();
+ else if (ident.equals("Descender"))
+ Descender = (int)Float.valueOf(tok.nextToken()).floatValue();
+ else if (ident.equals("StdHW"))
+ StdHW = (int)Float.valueOf(tok.nextToken()).floatValue();
+ else if (ident.equals("StdVW"))
+ StdVW = (int)Float.valueOf(tok.nextToken()).floatValue();
+ else if (ident.equals("StartCharMetrics"))
+ {
+ isMetrics = true;
+ break;
+ }
+ }
+ if (!isMetrics)
+ throw new DocumentException("Missing StartCharMetrics in " + fileName);
+ while ((line = rf.readLine()) != null)
+ {
+ StringTokenizer tok = new StringTokenizer(line);
+ if (!tok.hasMoreTokens())
+ continue;
+ String ident = tok.nextToken();
+ if (ident.equals("EndCharMetrics"))
+ {
+ isMetrics = false;
+ break;
+ }
+ Integer C = new Integer(-1);
+ Integer WX = new Integer(250);
+ String N = "";
+ int B[] = null;
+
+ tok = new StringTokenizer(line, ";");
+ while (tok.hasMoreTokens())
+ {
+ StringTokenizer tokc = new StringTokenizer(tok.nextToken());
+ if (!tokc.hasMoreTokens())
+ continue;
+ ident = tokc.nextToken();
+ if (ident.equals("C"))
+ C = Integer.valueOf(tokc.nextToken());
+ else if (ident.equals("WX"))
+ WX = new Integer(Float.valueOf(tokc.nextToken()).intValue());
+ else if (ident.equals("N"))
+ N = tokc.nextToken();
+ else if (ident.equals("B")) {
+ B = new int[]{Integer.parseInt(tokc.nextToken()),
+ Integer.parseInt(tokc.nextToken()),
+ Integer.parseInt(tokc.nextToken()),
+ Integer.parseInt(tokc.nextToken())};
+ }
+ }
+ Object metrics[] = new Object[]{C, WX, N, B};
+ if (C.intValue() >= 0)
+ CharMetrics.put(C, metrics);
+ CharMetrics.put(N, metrics);
+ }
+ if (isMetrics)
+ throw new DocumentException("Missing EndCharMetrics in " + fileName);
+ while ((line = rf.readLine()) != null)
+ {
+ StringTokenizer tok = new StringTokenizer(line);
+ if (!tok.hasMoreTokens())
+ continue;
+ String ident = tok.nextToken();
+ if (ident.equals("EndFontMetrics"))
+ return;
+ if (ident.equals("StartKernPairs"))
+ {
+ isMetrics = true;
+ break;
+ }
+ }
+ if (!isMetrics)
+ throw new DocumentException("Missing EndFontMetrics in " + fileName);
+ while ((line = rf.readLine()) != null)
+ {
+ StringTokenizer tok = new StringTokenizer(line);
+ if (!tok.hasMoreTokens())
+ continue;
+ String ident = tok.nextToken();
+ if (ident.equals("KPX"))
+ {
+ String first = tok.nextToken();
+ String second = tok.nextToken();
+ Integer width = new Integer(Float.valueOf(tok.nextToken()).intValue());
+ Object relates[] = (Object[])KernPairs.get(first);
+ if (relates == null)
+ KernPairs.put(first, new Object[]{second, width});
+ else
+ {
+ int n = relates.length;
+ Object relates2[] = new Object[n + 2];
+ System.arraycopy(relates, 0, relates2, 0, n);
+ relates2[n] = second;
+ relates2[n + 1] = width;
+ KernPairs.put(first, relates2);
+ }
+ }
+ else if (ident.equals("EndKernPairs"))
+ {
+ isMetrics = false;
+ break;
+ }
+ }
+ if (isMetrics)
+ throw new DocumentException("Missing EndKernPairs in " + fileName);
+ rf.close();
+ }
+
+/** If the embedded flag is false
or if the font is
+ * one of the 14 built in types, it returns null
,
+ * otherwise the font is read and output in a PdfStream object.
+ * @return the PdfStream containing the font or null
+ * @throws DocumentException if there is an error reading the font
+ */
+ private PdfStream getFontStream() throws DocumentException
+ {
+ if (builtinFont || !embedded)
+ return null;
+ RandomAccessFileOrArray rf = null;
+ try {
+ String filePfb = fileName.substring(0, fileName.length() - 3) + "pfb";
+ if (pfb == null)
+ rf = new RandomAccessFileOrArray(filePfb);
+ else
+ rf = new RandomAccessFileOrArray(pfb);
+ int fileLength = rf.length();
+ byte st[] = new byte[fileLength - 18];
+ int lengths[] = new int[3];
+ int bytePtr = 0;
+ for (int k = 0; k < 3; ++k) {
+ if (rf.read() != 0x80)
+ throw new DocumentException("Start marker missing in " + filePfb);
+ if (rf.read() != pfbTypes[k])
+ throw new DocumentException("Incorrect segment type in " + filePfb);
+ int size = rf.read();
+ size += rf.read() << 8;
+ size += rf.read() << 16;
+ size += rf.read() << 24;
+ lengths[k] = size;
+ while (size != 0) {
+ int got = rf.read(st, bytePtr, size);
+ if (got < 0)
+ throw new DocumentException("Premature end in " + filePfb);
+ bytePtr += got;
+ size -= got;
+ }
+ }
+ return new StreamFont(st, lengths);
+ }
+ catch (Exception e) {
+ throw new DocumentException(e);
+ }
+ finally {
+ if (rf != null) {
+ try {
+ rf.close();
+ }
+ catch (Exception e) {
+ // empty on purpose
+ }
+ }
+ }
+ }
+
+/** Generates the font descriptor for this font or null
if it is
+ * one of the 14 built in fonts.
+ * @param fontStream the indirect reference to a PdfStream containing the font or null
+ * @return the PdfDictionary containing the font descriptor or null
+ */
+ private PdfDictionary getFontDescriptor(PdfIndirectReference fontStream)
+ {
+ if (builtinFont)
+ return null;
+ PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR);
+ dic.put(PdfName.ASCENT, new PdfNumber(Ascender));
+ dic.put(PdfName.CAPHEIGHT, new PdfNumber(CapHeight));
+ dic.put(PdfName.DESCENT, new PdfNumber(Descender));
+ dic.put(PdfName.FONTBBOX, new PdfRectangle(llx, lly, urx, ury));
+ dic.put(PdfName.FONTNAME, new PdfName(FontName));
+ dic.put(PdfName.ITALICANGLE, new PdfNumber(ItalicAngle));
+ dic.put(PdfName.STEMV, new PdfNumber(StdVW));
+ if (fontStream != null)
+ dic.put(PdfName.FONTFILE, fontStream);
+ int flags = 0;
+ if (IsFixedPitch)
+ flags |= 1;
+ flags |= fontSpecific ? 4 : 32;
+ if (ItalicAngle < 0)
+ flags |= 64;
+ if (FontName.indexOf("Caps") >= 0 || FontName.endsWith("SC"))
+ flags |= 131072;
+ if (Weight.equals("Bold"))
+ flags |= 262144;
+ dic.put(PdfName.FLAGS, new PdfNumber(flags));
+
+ return dic;
+ }
+
+ /** Generates the font dictionary for this font.
+ * @return the PdfDictionary containing the font dictionary
+ * @param firstChar the first valid character
+ * @param lastChar the last valid character
+ * @param shortTag a 256 bytes long byte
array where each unused byte is represented by 0
+ * @param fontDescriptor the indirect reference to a PdfDictionary containing the font descriptor or null
+ */
+ private PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, int firstChar, int lastChar, byte shortTag[])
+ {
+ PdfDictionary dic = new PdfDictionary(PdfName.FONT);
+ dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
+ dic.put(PdfName.BASEFONT, new PdfName(FontName));
+ boolean stdEncoding = encoding.equals("Cp1252") || encoding.equals("MacRoman");
+ if (!fontSpecific) {
+ for (int k = firstChar; k <= lastChar; ++k) {
+ if (!differences[k].equals(notdef)) {
+ firstChar = k;
+ break;
+ }
+ }
+ if (stdEncoding)
+ dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING);
+ else {
+ PdfDictionary enc = new PdfDictionary(PdfName.ENCODING);
+ PdfArray dif = new PdfArray();
+ boolean gap = true;
+ for (int k = firstChar; k <= lastChar; ++k) {
+ if (shortTag[k] != 0) {
+ if (gap) {
+ dif.add(new PdfNumber(k));
+ gap = false;
+ }
+ dif.add(new PdfName(differences[k]));
+ }
+ else
+ gap = true;
+ }
+ enc.put(PdfName.DIFFERENCES, dif);
+ dic.put(PdfName.ENCODING, enc);
+ }
+ }
+ if (forceWidthsOutput || !(builtinFont && (fontSpecific || stdEncoding))) {
+ dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar));
+ dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar));
+ PdfArray wd = new PdfArray();
+ for (int k = firstChar; k <= lastChar; ++k) {
+ if (shortTag[k] == 0)
+ wd.add(new PdfNumber(0));
+ else
+ wd.add(new PdfNumber(widths[k]));
+ }
+ dic.put(PdfName.WIDTHS, wd);
+ }
+ if (!builtinFont && fontDescriptor != null)
+ dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor);
+ return dic;
+ }
+
+ /** Outputs to the writer the font dictionaries and streams.
+ * @param writer the writer for this document
+ * @param ref the font indirect reference
+ * @param params several parameters that depend on the font type
+ * @throws IOException on error
+ * @throws DocumentException error in generating the object
+ */
+ void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException {
+ int firstChar = ((Integer)params[0]).intValue();
+ int lastChar = ((Integer)params[1]).intValue();
+ byte shortTag[] = (byte[])params[2];
+ boolean subsetp = ((Boolean)params[3]).booleanValue() && subset;
+ if (!subsetp) {
+ firstChar = 0;
+ lastChar = shortTag.length - 1;
+ for (int k = 0; k < shortTag.length; ++k)
+ shortTag[k] = 1;
+ }
+ PdfIndirectReference ind_font = null;
+ PdfObject pobj = null;
+ PdfIndirectObject obj = null;
+ pobj = getFontStream();
+ if (pobj != null){
+ obj = writer.addToBody(pobj);
+ ind_font = obj.getIndirectReference();
+ }
+ pobj = getFontDescriptor(ind_font);
+ if (pobj != null){
+ obj = writer.addToBody(pobj);
+ ind_font = obj.getIndirectReference();
+ }
+ pobj = getFontBaseType(ind_font, firstChar, lastChar, shortTag);
+ writer.addToBody(pobj, ref);
+ }
+
+ /** Gets the font parameter identified by key
. Valid values
+ * for key
are ASCENT
, CAPHEIGHT
, DESCENT
,
+ * ITALICANGLE
, BBOXLLX
, BBOXLLY
, BBOXURX
+ * and BBOXURY
.
+ * @param key the parameter to be extracted
+ * @param fontSize the font size in points
+ * @return the parameter in points
+ */
+ public float getFontDescriptor(int key, float fontSize) {
+ switch (key) {
+ case AWT_ASCENT:
+ case ASCENT:
+ return Ascender * fontSize / 1000;
+ case CAPHEIGHT:
+ return CapHeight * fontSize / 1000;
+ case AWT_DESCENT:
+ case DESCENT:
+ return Descender * fontSize / 1000;
+ case ITALICANGLE:
+ return ItalicAngle;
+ case BBOXLLX:
+ return llx * fontSize / 1000;
+ case BBOXLLY:
+ return lly * fontSize / 1000;
+ case BBOXURX:
+ return urx * fontSize / 1000;
+ case BBOXURY:
+ return ury * fontSize / 1000;
+ case AWT_LEADING:
+ return 0;
+ case AWT_MAXADVANCE:
+ return (urx - llx) * fontSize / 1000;
+ }
+ return 0;
+ }
+
+ /** Gets the postscript font name.
+ * @return the postscript font name
+ */
+ public String getPostscriptFontName() {
+ return FontName;
+ }
+
+ /** Gets the full name of the font. If it is a True Type font
+ * each array element will have {Platform ID, Platform Encoding ID,
+ * Language ID, font name}. The interpretation of this values can be
+ * found in the Open Type specification, chapter 2, in the 'name' table.
+ * For the other fonts the array has a single element with {"", "", "",
+ * font name}.
+ * @return the full name of the font
+ */
+ public String[][] getFullFontName() {
+ return new String[][]{{"", "", "", FullName}};
+ }
+
+ /** Gets the family name of the font. If it is a True Type font
+ * each array element will have {Platform ID, Platform Encoding ID,
+ * Language ID, font name}. The interpretation of this values can be
+ * found in the Open Type specification, chapter 2, in the 'name' table.
+ * For the other fonts the array has a single element with {"", "", "",
+ * font name}.
+ * @return the family name of the font
+ */
+ public String[][] getFamilyFontName() {
+ return new String[][]{{"", "", "", FamilyName}};
+ }
+
+ /** Checks if the font has any kerning pairs.
+ * @return true
if the font has any kerning pairs
+ */
+ public boolean hasKernPairs() {
+ return KernPairs.size() > 0;
+ }
+
+ /**
+ * Sets the font name that will appear in the pdf font dictionary.
+ * Use with care as it can easily make a font unreadable if not embedded.
+ * @param name the new font name
+ */
+ public void setPostscriptFontName(String name) {
+ FontName = name;
+ }
+
+ /**
+ * Sets the kerning between two Unicode chars.
+ * @param char1 the first char
+ * @param char2 the second char
+ * @param kern the kerning to apply in normalized 1000 units
+ * @return true
if the kerning was applied, false
otherwise
+ */
+ public boolean setKerning(char char1, char char2, int kern) {
+ String first = GlyphList.unicodeToName((int)char1);
+ if (first == null)
+ return false;
+ String second = GlyphList.unicodeToName((int)char2);
+ if (second == null)
+ return false;
+ Object obj[] = (Object[])KernPairs.get(first);
+ if (obj == null) {
+ obj = new Object[]{second, new Integer(kern)};
+ KernPairs.put(first, obj);
+ return true;
+ }
+ for (int k = 0; k < obj.length; k += 2) {
+ if (second.equals(obj[k])) {
+ obj[k + 1] = new Integer(kern);
+ return true;
+ }
+ }
+ int size = obj.length;
+ Object obj2[] = new Object[size + 2];
+ System.arraycopy(obj, 0, obj2, 0, size);
+ obj2[size] = second;
+ obj2[size + 1] = new Integer(kern);
+ KernPairs.put(first, obj2);
+ return true;
+ }
+
+ protected int[] getRawCharBBox(int c, String name) {
+ Object metrics[];
+ if (name == null) { // font specific
+ metrics = (Object[])CharMetrics.get(new Integer(c));
+ }
+ else {
+ if (name.equals(".notdef"))
+ return null;
+ metrics = (Object[])CharMetrics.get(name);
+ }
+ if (metrics != null)
+ return ((int[])(metrics[3]));
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/Type3Font.java b/src/main/java/com/lowagie/text/pdf/Type3Font.java
new file mode 100644
index 0000000..f1d4e51
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/Type3Font.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import com.lowagie.text.DocumentException;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * A class to support Type3 fonts.
+ */
+public class Type3Font extends BaseFont {
+
+ private IntHashtable char2byte = new IntHashtable();
+ private IntHashtable widths3 = new IntHashtable();
+ private HashMap char2glyph = new HashMap();
+ private PdfWriter writer;
+ private float llx = Float.NaN, lly, urx, ury;
+ private PageResources pageResources = new PageResources();
+ private boolean colorized;
+
+ /**
+ * Creates a Type3 font. This implementation assumes that the /FontMatrix is
+ * [0.001 0 0 0.001 0 0] or a 1000-unit glyph coordinate system.
+ *
+ * Document document = new Document(PageSize.A4);
+ * PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("type3.pdf"));
+ * document.open();
+ * Type3Font t3 = new Type3Font(writer, new char[]{'a', 'b'}, false);
+ * PdfContentByte g = t3.defineGlyph('a', 1000, 0, 0, 750, 750);
+ * g.rectangle(0, 0, 750, 750);
+ * g.fill();
+ * g = t3.defineGlyph('b', 1000, 0, 0, 750, 750);
+ * g.moveTo(0, 0);
+ * g.lineTo(375, 750);
+ * g.lineTo(750, 0);
+ * g.fill();
+ * Font f = new Font(t3, 12);
+ * document.add(new Paragraph("ababab", f));
+ * document.close();
+ *
+ * @param writer the writer
+ * @param chars an array of chars corresponding to the glyphs used
+ * @param colorized if true
the font may specify color, if false
no color commands are allowed
+ * and only images as masks can be used
+ */
+ public Type3Font(PdfWriter writer, char[] chars, boolean colorized) {
+ this.writer = writer;
+ this.colorized = colorized;
+ fontType = FONT_TYPE_T3;
+ if (chars.length == 0 || chars.length > 256)
+ throw new IllegalArgumentException("char array size must be > 0 and <= 256");
+ int count = 255;
+ boolean[] hits = new boolean[chars.length];
+ for (int k = 0; k < chars.length; ++k) {
+ char c = chars[k];
+ if (c >= 32 && c < 256) {
+ if (char2byte.containsKey(c))
+ throw new IllegalArgumentException("duplicated char - " + (int)c + ", index " + k);
+ char2byte.put(c, c);
+ hits[k] = true;
+ }
+ }
+ for (int k = 0; k < hits.length; ++k) {
+ if (hits[k])
+ continue;
+ while (char2byte.containsKey(count)) {
+ --count;
+ }
+ char c = chars[k];
+ if (char2byte.containsKey(c))
+ throw new IllegalArgumentException("duplicated char - " + (int)c + ", index " + k);
+ char2byte.put(c, count--);
+ }
+ }
+
+ /**
+ * Defines a glyph.
+ * @param c the character to match this glyph. It must be one of those defined in the constructor
+ * @param wx the advance this character will have
+ * @param llx the X lower left corner of the glyph bounding box. If the colorize
option is
+ * true
the value is ignored
+ * @param lly the Y lower left corner of the glyph bounding box. If the colorize
option is
+ * true
the value is ignored
+ * @param urx the X upper right corner of the glyph bounding box. If the colorize
option is
+ * true
the value is ignored
+ * @param ury the Y upper right corner of the glyph bounding box. If the colorize
option is
+ * true
the value is ignored
+ * @return a content where the glyph can be defined
+ */
+ public PdfContentByte defineGlyph(char c, float wx, float llx, float lly, float urx, float ury) {
+ if (!char2byte.containsKey(c))
+ throw new IllegalArgumentException("The char " + (int)c + " doesn't belong in this Type3 font");
+ Integer ck = new Integer((int)c);
+ Type3Glyph glyph = (Type3Glyph)char2glyph.get(ck);
+ if (glyph != null)
+ return glyph;
+ widths3.put(c, (int)wx);
+ if (!colorized) {
+ if (Float.isNaN(this.llx)) {
+ this.llx = llx;
+ this.lly = lly;
+ this.urx = urx;
+ this.ury = ury;
+ }
+ else {
+ this.llx = Math.min(this.llx, llx);
+ this.lly = Math.min(this.lly, lly);
+ this.urx = Math.max(this.urx, urx);
+ this.ury = Math.max(this.ury, ury);
+ }
+ }
+ glyph = new Type3Glyph(writer, pageResources, wx, llx, lly, urx, ury, colorized);
+ char2glyph.put(ck, glyph);
+ return glyph;
+ }
+
+ public String[][] getFamilyFontName() {
+ return new String[0][];
+ }
+
+ public float getFontDescriptor(int key, float fontSize) {
+ return 0;
+ }
+
+ public String[][] getFullFontName() {
+ return new String[0][];
+ }
+
+ public int getKerning(char char1, char char2) {
+ return 0;
+ }
+
+ public String getPostscriptFontName() {
+ return "";
+ }
+
+ protected int[] getRawCharBBox(int c, String name) {
+ return null;
+ }
+
+ int getRawWidth(int c, String name) {
+ return 0;
+ }
+
+ public boolean hasKernPairs() {
+ return false;
+ }
+
+ public boolean setKerning(char char1, char char2, int kern) {
+ return false;
+ }
+
+ public void setPostscriptFontName(String name) {
+ }
+
+ void writeFont(PdfWriter writer, PdfIndirectReference ref, Object[] params) throws com.lowagie.text.DocumentException, java.io.IOException {
+ if (this.writer != writer)
+ throw new IllegalArgumentException("Type3 font used with the wrong PdfWriter");
+ if (char2byte.size() != widths3.size())
+ throw new DocumentException("Not all the glyphs in the Type3 font are defined");
+ IntHashtable inv = new IntHashtable();
+ for (Iterator it = char2byte.getEntryIterator(); it.hasNext();) {
+ IntHashtable.IntHashtableEntry entry = (IntHashtable.IntHashtableEntry)it.next();
+ inv.put(entry.getValue(), entry.getKey());
+ }
+ int[] invOrd = inv.toOrderedKeys();
+ int firstChar = invOrd[0];
+ int lastChar = invOrd[invOrd.length - 1];
+ int[] widths = new int[lastChar - firstChar + 1];
+ for (int k = 0; k < widths.length; ++k) {
+ if (inv.containsKey(k + firstChar))
+ widths[k] = widths3.get(inv.get(k + firstChar));
+ }
+ PdfArray diffs = new PdfArray();
+ PdfDictionary charprocs = new PdfDictionary();
+ int last = -1;
+ for (int k = 0; k < invOrd.length; ++k) {
+ int c = invOrd[k];
+ if (c > last) {
+ last = c;
+ diffs.add(new PdfNumber(last));
+ }
+ ++last;
+ int c2 = inv.get(c);
+ String s = GlyphList.unicodeToName(c2);
+ if (s == null)
+ s = "a" + c2;
+ PdfName n = new PdfName(s);
+ diffs.add(n);
+ Type3Glyph glyph = (Type3Glyph)char2glyph.get(new Integer(c2));
+ PdfStream stream = new PdfStream(glyph.toPdf(null));
+ stream.flateCompress();
+ PdfIndirectReference refp = writer.addToBody(stream).getIndirectReference();
+ charprocs.put(n, refp);
+ }
+ PdfDictionary font = new PdfDictionary(PdfName.FONT);
+ font.put(PdfName.SUBTYPE, PdfName.TYPE3);
+ if (colorized)
+ font.put(PdfName.FONTBBOX, new PdfRectangle(0, 0, 0, 0));
+ else
+ font.put(PdfName.FONTBBOX, new PdfRectangle(llx, lly, urx, ury));
+ font.put(PdfName.FONTMATRIX, new PdfArray(new float[]{0.001f, 0, 0, 0.001f, 0, 0}));
+ font.put(PdfName.CHARPROCS, writer.addToBody(charprocs).getIndirectReference());
+ PdfDictionary encoding = new PdfDictionary();
+ encoding.put(PdfName.DIFFERENCES, diffs);
+ font.put(PdfName.ENCODING, writer.addToBody(encoding).getIndirectReference());
+ font.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar));
+ font.put(PdfName.LASTCHAR, new PdfNumber(lastChar));
+ font.put(PdfName.WIDTHS, writer.addToBody(new PdfArray(widths)).getIndirectReference());
+ if (pageResources.hasResources())
+ font.put(PdfName.RESOURCES, writer.addToBody(pageResources.getResources()).getIndirectReference());
+ writer.addToBody(font, ref);
+ }
+
+ byte[] convertToBytes(String text) {
+ char[] cc = text.toCharArray();
+ byte[] b = new byte[cc.length];
+ int p = 0;
+ for (int k = 0; k < cc.length; ++k) {
+ char c = cc[k];
+ if (char2byte.containsKey(c))
+ b[p++] = (byte)char2byte.get(c);
+ }
+ if (b.length == p)
+ return b;
+ byte[] b2 = new byte[p];
+ System.arraycopy(b, 0, b2, 0, p);
+ return b2;
+ }
+
+ public int getWidth(char char1) {
+ if (!widths3.containsKey(char1))
+ throw new IllegalArgumentException("The char " + (int)char1 + " is not defined in a Type3 font");
+ return widths3.get(char1);
+ }
+
+ public int getWidth(String text) {
+ char[] c = text.toCharArray();
+ int total = 0;
+ for (int k = 0; k < c.length; ++k)
+ total += getWidth(c[k]);
+ return total;
+ }
+
+ public int[] getCharBBox(char c) {
+ return null;
+ }
+
+ public boolean charExists(char c) {
+ return char2byte.containsKey(c);
+ }
+
+ public boolean setCharAdvance(char c, int advance) {
+ return false;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/Type3Glyph.java b/src/main/java/com/lowagie/text/pdf/Type3Glyph.java
new file mode 100644
index 0000000..4c03a5e
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/Type3Glyph.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Image;
+/**
+ * The content where Type3 glyphs are written to.
+ */
+public class Type3Glyph extends PdfContentByte {
+
+ private PageResources pageResources;
+ private boolean colorized;
+
+ private Type3Glyph() {
+ super(null);
+ }
+
+ Type3Glyph(PdfWriter writer, PageResources pageResources, float wx, float llx, float lly, float urx, float ury, boolean colorized) {
+ super(writer);
+ this.pageResources = pageResources;
+ this.colorized = colorized;
+ if (colorized) {
+ content.append(wx).append(" 0 d0\n");
+ }
+ else {
+ content.append(wx).append(" 0 ").append(llx).append(' ').append(lly).append(' ').append(urx).append(' ').append(ury).append(" d1\n");
+ }
+ }
+
+ PageResources getPageResources() {
+ return pageResources;
+ }
+
+ public void addImage(Image image, float a, float b, float c, float d, float e, float f, boolean inlineImage) throws DocumentException {
+ if (!colorized && (!image.isMask() || !(image.bpc() == 1 || image.bpc() > 0xff)))
+ throw new DocumentException("Not colorized Typed3 fonts only accept mask images.");
+ super.addImage(image, a, b, c, d, e, f, inlineImage);
+ }
+
+ public PdfContentByte getDuplicate() {
+ Type3Glyph dup = new Type3Glyph();
+ dup.writer = writer;
+ dup.pdf = pdf;
+ dup.pageResources = pageResources;
+ dup.colorized = colorized;
+ return dup;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/VerticalText.java b/src/main/java/com/lowagie/text/pdf/VerticalText.java
new file mode 100644
index 0000000..8ebc359
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/VerticalText.java
@@ -0,0 +1,349 @@
+/*
+ *
+ * Copyright 2002 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf;
+import java.util.ArrayList;
+import java.util.Iterator;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Chunk;
+import com.lowagie.text.Element;
+import com.lowagie.text.DocumentException;
+import java.awt.Color;
+
+/** Writes text vertically. Note that the naming is done according
+ * to horizontal text although it referrs to vertical text.
+ * A line with the alignment Element.LEFT_ALIGN will actually
+ * be top aligned.
+ */
+public class VerticalText {
+
+/** Signals that there are no more text available. */
+ public static final int NO_MORE_TEXT = 1;
+
+/** Signals that there is no more column. */
+ public static final int NO_MORE_COLUMN = 2;
+
+/** The chunks that form the text. */
+ protected ArrayList chunks = new ArrayList();
+
+ /** The PdfContent
where the text will be written to. */
+ protected PdfContentByte text;
+
+ /** The column alignment. Default is left alignment. */
+ protected int alignment = Element.ALIGN_LEFT;
+
+ /** Marks the chunks to be eliminated when the line is written. */
+ protected int currentChunkMarker = -1;
+
+ /** The chunk created by the splitting. */
+ protected PdfChunk currentStandbyChunk;
+
+ /** The chunk created by the splitting. */
+ protected String splittedChunkText;
+
+ /** The leading
+ */
+ protected float leading;
+
+ /** The X coordinate.
+ */
+ protected float startX;
+
+ /** The Y coordinate.
+ */
+ protected float startY;
+
+ /** The maximum number of vertical lines.
+ */
+ protected int maxLines;
+
+ /** The height of the text.
+ */
+ protected float height;
+
+ /** Creates new VerticalText
+ * @param text the place where the text will be written to. Can
+ * be a template.
+ */
+ public VerticalText(PdfContentByte text) {
+ this.text = text;
+ }
+
+ /**
+ * Adds a Phrase
to the current text array.
+ * @param phrase the text
+ */
+ public void addText(Phrase phrase) {
+ for (Iterator j = phrase.getChunks().iterator(); j.hasNext();) {
+ chunks.add(new PdfChunk((Chunk)j.next(), null));
+ }
+ }
+
+ /**
+ * Adds a Chunk
to the current text array.
+ * @param chunk the text
+ */
+ public void addText(Chunk chunk) {
+ chunks.add(new PdfChunk(chunk, null));
+ }
+
+ /** Sets the layout.
+ * @param startX the top right X line position
+ * @param startY the top right Y line position
+ * @param height the height of the lines
+ * @param maxLines the maximum number of lines
+ * @param leading the separation between the lines
+ */
+ public void setVerticalLayout(float startX, float startY, float height, int maxLines, float leading) {
+ this.startX = startX;
+ this.startY = startY;
+ this.height = height;
+ this.maxLines = maxLines;
+ setLeading(leading);
+ }
+
+ /** Sets the separation between the vertical lines.
+ * @param leading the vertical line separation
+ */
+ public void setLeading(float leading) {
+ this.leading = leading;
+ }
+
+ /** Gets the separation between the vertical lines.
+ * @return the vertical line separation
+ */
+ public float getLeading() {
+ return leading;
+ }
+
+ /**
+ * Creates a line from the chunk array.
+ * @param width the width of the line
+ * @return the line or null if no more chunks
+ */
+ protected PdfLine createLine(float width) {
+ if (chunks.size() == 0)
+ return null;
+ splittedChunkText = null;
+ currentStandbyChunk = null;
+ PdfLine line = new PdfLine(0, width, alignment, 0);
+ String total;
+ for (currentChunkMarker = 0; currentChunkMarker < chunks.size(); ++currentChunkMarker) {
+ PdfChunk original = (PdfChunk)(chunks.get(currentChunkMarker));
+ total = original.toString();
+ currentStandbyChunk = line.add(original);
+ if (currentStandbyChunk != null) {
+ splittedChunkText = original.toString();
+ original.setValue(total);
+ return line;
+ }
+ }
+ return line;
+ }
+
+ /**
+ * Normalizes the list of chunks when the line is accepted.
+ */
+ protected void shortenChunkArray() {
+ if (currentChunkMarker < 0)
+ return;
+ if (currentChunkMarker >= chunks.size()) {
+ chunks.clear();
+ return;
+ }
+ PdfChunk split = (PdfChunk)(chunks.get(currentChunkMarker));
+ split.setValue(splittedChunkText);
+ chunks.set(currentChunkMarker, currentStandbyChunk);
+ for (int j = currentChunkMarker - 1; j >= 0; --j)
+ chunks.remove(j);
+ }
+
+ /**
+ * Outputs the lines to the document. It is equivalent to go(false)
.
+ * @return returns the result of the operation. It can be NO_MORE_TEXT
+ * and/or NO_MORE_COLUMN
+ * @throws DocumentException on error
+ */
+ public int go() throws DocumentException {
+ return go(false);
+ }
+
+ /**
+ * Outputs the lines to the document. The output can be simulated.
+ * @param simulate true
to simulate the writting to the document
+ * @return returns the result of the operation. It can be NO_MORE_TEXT
+ * and/or NO_MORE_COLUMN
+ * @throws DocumentException on error
+ */
+ public int go(boolean simulate) throws DocumentException {
+ boolean dirty = false;
+ PdfContentByte graphics = null;
+ if (text != null) {
+ graphics = text.getDuplicate();
+ }
+ else if (simulate == false)
+ throw new NullPointerException("VerticalText.go with simulate==false and text==null.");
+ int status = 0;
+ for (;;) {
+ if (maxLines <= 0) {
+ status = NO_MORE_COLUMN;
+ if (chunks.size() == 0)
+ status |= NO_MORE_TEXT;
+ break;
+ }
+ if (chunks.size() == 0) {
+ status = NO_MORE_TEXT;
+ break;
+ }
+ PdfLine line = createLine(height);
+ if (!simulate && !dirty) {
+ text.beginText();
+ dirty = true;
+ }
+ shortenChunkArray();
+ if (!simulate) {
+ text.setTextMatrix(startX, startY - line.indentLeft());
+ writeLine(line, text, graphics);
+ }
+ --maxLines;
+ startX -= leading;
+ }
+ if (dirty) {
+ text.endText();
+ text.add(graphics);
+ }
+ return status;
+ }
+
+ void writeLine(PdfLine line, PdfContentByte text, PdfContentByte graphics) throws DocumentException {
+ PdfFont currentFont = null;
+ PdfChunk chunk;
+ for (Iterator j = line.iterator(); j.hasNext(); ) {
+ chunk = (PdfChunk) j.next();
+
+ if (chunk.font().compareTo(currentFont) != 0) {
+ currentFont = chunk.font();
+ text.setFontAndSize(currentFont.getFont(), currentFont.size());
+ }
+ Color color = chunk.color();
+ if (color != null)
+ text.setColorFill(color);
+ text.showText(chunk.toString());
+ if (color != null)
+ text.resetRGBColorFill();
+ }
+ }
+
+ /** Sets the new text origin.
+ * @param startX the X coordinate
+ * @param startY the Y coordinate
+ */
+ public void setOrigin(float startX, float startY) {
+ this.startX = startX;
+ this.startY = startY;
+ }
+
+ /** Gets the X coordinate where the next line will be writen. This value will change
+ * after each call to go()
.
+ * @return the X coordinate
+ */
+ public float getOriginX() {
+ return startX;
+ }
+
+ /** Gets the Y coordinate where the next line will be writen.
+ * @return the Y coordinate
+ */
+ public float getOriginY() {
+ return startY;
+ }
+
+ /** Gets the maximum number of available lines. This value will change
+ * after each call to go()
.
+ * @return Value of property maxLines.
+ */
+ public int getMaxLines() {
+ return maxLines;
+ }
+
+ /** Sets the maximum number of lines.
+ * @param maxLines the maximum number of lines
+ */
+ public void setMaxLines(int maxLines) {
+ this.maxLines = maxLines;
+ }
+
+ /** Gets the height of the line
+ * @return the height
+ */
+ public float getHeight() {
+ return height;
+ }
+
+ /** Sets the height of the line
+ * @param height the new height
+ */
+ public void setHeight(float height) {
+ this.height = height;
+ }
+
+ /**
+ * Sets the alignment.
+ * @param alignment the alignment
+ */
+ public void setAlignment(int alignment) {
+ this.alignment = alignment;
+ }
+
+ /**
+ * Gets the alignment.
+ * @return the alignment
+ */
+ public int getAlignment() {
+ return alignment;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/XfdfReader.java b/src/main/java/com/lowagie/text/pdf/XfdfReader.java
new file mode 100644
index 0000000..e3a7067
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/XfdfReader.java
@@ -0,0 +1,208 @@
+/*
+ *
+ * Copyright 2004 by Leonard Rosenthol.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.util.HashMap;
+import java.util.Stack;
+
+/**
+ * Reads a XFDF.
+ * @author Leonard Rosenthol (leonardr@pdfsages.com)
+ */
+public class XfdfReader implements SimpleXMLDocHandler {
+ // stuff used during parsing to handle state
+ private boolean foundRoot = false;
+ private Stack fieldNames = new Stack();
+ private Stack fieldValues = new Stack();
+
+ // storage for the field list and their values
+ HashMap fields;
+
+ // storage for the path to referenced PDF, if any
+ String fileSpec;
+
+ /** Reads an XFDF form.
+ * @param filename the file name of the form
+ * @throws IOException on error
+ */
+ public XfdfReader(String filename) throws IOException {
+ FileInputStream fin = null;
+ try {
+ fin = new FileInputStream(filename);
+ SimpleXMLParser.parse(this, fin);
+ }
+ finally {
+ try{fin.close();}catch(Exception e){}
+ }
+ }
+
+ /** Reads an XFDF form.
+ * @param xfdfIn the byte array with the form
+ * @throws IOException on error
+ */
+ public XfdfReader(byte xfdfIn[]) throws IOException {
+ SimpleXMLParser.parse( this, new ByteArrayInputStream(xfdfIn));
+ }
+
+ /** Gets all the fields. The map is keyed by the fully qualified
+ * field name and the value is a merged PdfDictionary
+ * with the field content.
+ * @return all the fields
+ */
+ public HashMap getFields() {
+ return fields;
+ }
+
+ /** Gets the field value.
+ * @param name the fully qualified field name
+ * @return the field's value
+ */
+ public String getField(String name) {
+ return (String)fields.get(name);
+ }
+
+ /** Gets the field value or null
if the field does not
+ * exist or has no value defined.
+ * @param name the fully qualified field name
+ * @return the field value or null
+ */
+ public String getFieldValue(String name) {
+ String field = (String)fields.get(name);
+ if (field == null)
+ return null;
+ else
+ return field;
+ }
+
+ /** Gets the PDF file specification contained in the FDF.
+ * @return the PDF file specification contained in the FDF
+ */
+ public String getFileSpec() {
+ return fileSpec;
+ }
+
+ /**
+ * Called when a start tag is found.
+ * @param tag the tag name
+ * @param h the tag's attributes
+ */
+ public void startElement(String tag, HashMap h)
+ {
+ if ( !foundRoot ) {
+ if (!tag.equals("xfdf"))
+ throw new RuntimeException("Root element is not Bookmark.");
+ else
+ foundRoot = true;
+ }
+
+ if ( tag.equals("xfdf") ){
+
+ } else if ( tag.equals("f") ) {
+ fileSpec = (String)h.get( "href" );
+ } else if ( tag.equals("fields") ) {
+ fields = new HashMap(); // init it!
+ } else if ( tag.equals("field") ) {
+ String fName = (String) h.get( "name" );
+ fieldNames.push( fName );
+ } else if ( tag.equals("value") ) {
+ fieldValues.push( "" );
+ }
+ }
+ /**
+ * Called when an end tag is found.
+ * @param tag the tag name
+ */
+ public void endElement(String tag) {
+ if ( tag.equals("value") ) {
+ String fName = "";
+ for (int k = 0; k < fieldNames.size(); ++k) {
+ fName += "." + (String)fieldNames.elementAt(k);
+ }
+ if (fName.startsWith("."))
+ fName = fName.substring(1);
+ String fVal = (String) fieldValues.pop();
+ fields.put( fName, fVal );
+ }
+ else if (tag.equals("field") ) {
+ if (!fieldNames.isEmpty())
+ fieldNames.pop();
+ }
+ }
+
+ /**
+ * Called when the document starts to be parsed.
+ */
+ public void startDocument()
+ {
+ fileSpec = new String(""); // and this too...
+ }
+ /**
+ * Called after the document is parsed.
+ */
+ public void endDocument()
+ {
+
+ }
+ /**
+ * Called when a text element is found.
+ * @param str the text element, probably a fragment.
+ */
+ public void text(String str)
+ {
+ if (fieldNames.isEmpty() || fieldValues.isEmpty())
+ return;
+
+ String val = (String)fieldValues.pop();
+ val += str;
+ fieldValues.push(val);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/codec/BmpImage.java b/src/main/java/com/lowagie/text/pdf/codec/BmpImage.java
new file mode 100644
index 0000000..f9f80be
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/BmpImage.java
@@ -0,0 +1,1282 @@
+/*
+ * Copyright 2003 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ *
+ *
+ * The original JAI codecs have the following license
+ *
+ * Copyright (c) 2001 Sun Microsystems, 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.
+ *
+ * -Redistribution in binary form must reproduct the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
+ * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
+ * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
+ * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
+ * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
+ * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that Software is not designed,licensed or intended for use in
+ * the design, construction, operation or maintenance of any nuclear facility.
+ */
+package com.lowagie.text.pdf.codec;
+
+import com.lowagie.text.pdf.*;
+import com.lowagie.text.*;
+import java.io.*;
+import java.util.HashMap;
+import java.net.URL;
+
+/** Reads a BMP image. All types of BMP can be read.
+ * int
from an InputStream
.
+ *
+ * @param is an InputStream
+ * @return the value of an int
+ */
+
+ public static final int getInt(InputStream is) throws IOException {
+ return (is.read() << 24) + (is.read() << 16) + (is.read() << 8) + is.read();
+ }
+
+/**
+ * Gets a word
from an InputStream
.
+ *
+ * @param is an InputStream
+ * @return the value of an int
+ */
+
+ public static final int getWord(InputStream is) throws IOException {
+ return (is.read() << 8) + is.read();
+ }
+
+/**
+ * Gets a String
from an InputStream
.
+ *
+ * @param is an InputStream
+ * @return the value of an int
+ */
+
+ public static final String getString(InputStream is) throws IOException {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < 4; i++) {
+ buf.append((char)is.read());
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/codec/TIFFConstants.java b/src/main/java/com/lowagie/text/pdf/codec/TIFFConstants.java
new file mode 100644
index 0000000..a4ba5d5
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/TIFFConstants.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2001 Sun Microsystems, 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.
+ *
+ * -Redistribution in binary form must reproduct the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
+ * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
+ * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
+ * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
+ * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
+ * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that Software is not designed,licensed or intended for use in
+ * the design, construction, operation or maintenance of any nuclear facility.
+ */
+package com.lowagie.text.pdf.codec;
+
+/**
+ * A baseline TIFF reader. The reader has some functionality in addition to
+ * the baseline specifications for Bilevel images, for which the group 3 and
+ * group 4 decompression schemes have been implemented. Support for LZW
+ * decompression has also been added. Support for Horizontal differencing
+ * predictor decoding is also included, when used with LZW compression.
+ * However, this support is limited to data with bitsPerSample value of 8.
+ * When reading in RGB images, support for alpha and extraSamples being
+ * present has been added. Support for reading in images with 16 bit samples
+ * has been added. Support for the SampleFormat tag (signed samples as well
+ * as floating-point samples) has also been added. In all other cases, support
+ * is limited to Baseline specifications.
+ *
+ *
+ */
+public class TIFFConstants {
+
+/*
+ * TIFF Tag Definitions (from tifflib).
+ */
+ public static final int TIFFTAG_SUBFILETYPE = 254; /* subfile data descriptor */
+ public static final int FILETYPE_REDUCEDIMAGE = 0x1; /* reduced resolution version */
+ public static final int FILETYPE_PAGE = 0x2; /* one page of many */
+ public static final int FILETYPE_MASK = 0x4; /* transparency mask */
+ public static final int TIFFTAG_OSUBFILETYPE = 255; /* +kind of data in subfile */
+ public static final int OFILETYPE_IMAGE = 1; /* full resolution image data */
+ public static final int OFILETYPE_REDUCEDIMAGE = 2; /* reduced size image data */
+ public static final int OFILETYPE_PAGE = 3; /* one page of many */
+ public static final int TIFFTAG_IMAGEWIDTH = 256; /* image width in pixels */
+ public static final int TIFFTAG_IMAGELENGTH = 257; /* image height in pixels */
+ public static final int TIFFTAG_BITSPERSAMPLE = 258; /* bits per channel (sample) */
+ public static final int TIFFTAG_COMPRESSION = 259; /* data compression technique */
+ public static final int COMPRESSION_NONE = 1; /* dump mode */
+ public static final int COMPRESSION_CCITTRLE = 2; /* CCITT modified Huffman RLE */
+ public static final int COMPRESSION_CCITTFAX3 = 3; /* CCITT Group 3 fax encoding */
+ public static final int COMPRESSION_CCITTFAX4 = 4; /* CCITT Group 4 fax encoding */
+ public static final int COMPRESSION_LZW = 5; /* Lempel-Ziv & Welch */
+ public static final int COMPRESSION_OJPEG = 6; /* !6.0 JPEG */
+ public static final int COMPRESSION_JPEG = 7; /* %JPEG DCT compression */
+ public static final int COMPRESSION_NEXT = 32766; /* NeXT 2-bit RLE */
+ public static final int COMPRESSION_CCITTRLEW = 32771; /* #1 w/ word alignment */
+ public static final int COMPRESSION_PACKBITS = 32773; /* Macintosh RLE */
+ public static final int COMPRESSION_THUNDERSCAN = 32809; /* ThunderScan RLE */
+ /* codes 32895-32898 are reserved for ANSI IT8 TIFF/IT SeekableStream
.
+ */
+ public static int getNumDirectories(RandomAccessFileOrArray stream)
+ throws IOException{
+ long pointer = stream.getFilePointer(); // Save stream pointer
+
+ stream.seek(0L);
+ int endian = stream.readUnsignedShort();
+ if (!isValidEndianTag(endian)) {
+ throw new
+ IllegalArgumentException("Bad endianness tag (not 0x4949 or 0x4d4d).");
+ }
+ boolean isBigEndian = (endian == 0x4d4d);
+ int magic = readUnsignedShort(stream, isBigEndian);
+ if (magic != 42) {
+ throw new
+ IllegalArgumentException("Bad magic number, should be 42.");
+ }
+
+ stream.seek(4L);
+ long offset = readUnsignedInt(stream, isBigEndian);
+
+ int numDirectories = 0;
+ while (offset != 0L) {
+ ++numDirectories;
+
+ // EOFException means IFD was probably not properly terminated.
+ try {
+ stream.seek(offset);
+ int entries = readUnsignedShort(stream, isBigEndian);
+ stream.skip(12*entries);
+ offset = readUnsignedInt(stream, isBigEndian);
+ } catch(EOFException eof) {
+ numDirectories--;
+ break;
+ }
+ }
+
+ stream.seek(pointer); // Reset stream pointer
+ return numDirectories;
+ }
+
+ /**
+ * Returns a boolean indicating whether the byte order used in the
+ * the TIFF file is big-endian (i.e. whether the byte order is from
+ * the most significant to the least significant)
+ */
+ public boolean isBigEndian() {
+ return isBigEndian;
+ }
+
+ /**
+ * Returns the offset of the IFD corresponding to this
+ * TIFFDirectory
.
+ */
+ public long getIFDOffset() {
+ return IFDOffset;
+ }
+
+ /**
+ * Returns the offset of the next IFD after the IFD corresponding to this
+ * TIFFDirectory
.
+ */
+ public long getNextIFDOffset() {
+ return nextIFDOffset;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/codec/TIFFFaxDecoder.java b/src/main/java/com/lowagie/text/pdf/codec/TIFFFaxDecoder.java
new file mode 100644
index 0000000..7a7b61c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/TIFFFaxDecoder.java
@@ -0,0 +1,1477 @@
+/*
+ * Copyright (c) 2001 Sun Microsystems, 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.
+ *
+ * -Redistribution in binary form must reproduct the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
+ * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
+ * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
+ * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
+ * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
+ * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that Software is not designed,licensed or intended for use in
+ * the design, construction, operation or maintenance of any nuclear facility.
+ */
+package com.lowagie.text.pdf.codec;
+
+public class TIFFFaxDecoder {
+
+ private int bitPointer, bytePointer;
+ private byte[] data;
+ private int w, h;
+ private int fillOrder;
+
+ // Data structures needed to store changing elements for the previous
+ // and the current scanline
+ private int changingElemSize = 0;
+ private int prevChangingElems[];
+ private int currChangingElems[];
+
+ // Element at which to start search in getNextChangingElement
+ private int lastChangingElement = 0;
+
+ private int compression = 2;
+
+ // Variables set by T4Options
+ private int uncompressedMode = 0;
+ private int fillBits = 0;
+ private int oneD;
+
+ static int table1[] = {
+ 0x00, // 0 bits are left in first byte - SHOULD NOT HAPPEN
+ 0x01, // 1 bits are left in first byte
+ 0x03, // 2 bits are left in first byte
+ 0x07, // 3 bits are left in first byte
+ 0x0f, // 4 bits are left in first byte
+ 0x1f, // 5 bits are left in first byte
+ 0x3f, // 6 bits are left in first byte
+ 0x7f, // 7 bits are left in first byte
+ 0xff // 8 bits are left in first byte
+ };
+
+ static int table2[] = {
+ 0x00, // 0
+ 0x80, // 1
+ 0xc0, // 2
+ 0xe0, // 3
+ 0xf0, // 4
+ 0xf8, // 5
+ 0xfc, // 6
+ 0xfe, // 7
+ 0xff // 8
+ };
+
+ // Table to be used when fillOrder = 2, for flipping bytes.
+ static byte flipTable[] = {
+ 0, -128, 64, -64, 32, -96, 96, -32,
+ 16, -112, 80, -48, 48, -80, 112, -16,
+ 8, -120, 72, -56, 40, -88, 104, -24,
+ 24, -104, 88, -40, 56, -72, 120, -8,
+ 4, -124, 68, -60, 36, -92, 100, -28,
+ 20, -108, 84, -44, 52, -76, 116, -12,
+ 12, -116, 76, -52, 44, -84, 108, -20,
+ 28, -100, 92, -36, 60, -68, 124, -4,
+ 2, -126, 66, -62, 34, -94, 98, -30,
+ 18, -110, 82, -46, 50, -78, 114, -14,
+ 10, -118, 74, -54, 42, -86, 106, -22,
+ 26, -102, 90, -38, 58, -70, 122, -6,
+ 6, -122, 70, -58, 38, -90, 102, -26,
+ 22, -106, 86, -42, 54, -74, 118, -10,
+ 14, -114, 78, -50, 46, -82, 110, -18,
+ 30, -98, 94, -34, 62, -66, 126, -2,
+ 1, -127, 65, -63, 33, -95, 97, -31,
+ 17, -111, 81, -47, 49, -79, 113, -15,
+ 9, -119, 73, -55, 41, -87, 105, -23,
+ 25, -103, 89, -39, 57, -71, 121, -7,
+ 5, -123, 69, -59, 37, -91, 101, -27,
+ 21, -107, 85, -43, 53, -75, 117, -11,
+ 13, -115, 77, -51, 45, -83, 109, -19,
+ 29, -99, 93, -35, 61, -67, 125, -3,
+ 3, -125, 67, -61, 35, -93, 99, -29,
+ 19, -109, 83, -45, 51, -77, 115, -13,
+ 11, -117, 75, -53, 43, -85, 107, -21,
+ 27, -101, 91, -37, 59, -69, 123, -5,
+ 7, -121, 71, -57, 39, -89, 103, -25,
+ 23, -105, 87, -41, 55, -73, 119, -9,
+ 15, -113, 79, -49, 47, -81, 111, -17,
+ 31, -97, 95, -33, 63, -65, 127, -1,
+ };
+
+ // The main 10 bit white runs lookup table
+ static short white[] = {
+ // 0 - 7
+ 6430, 6400, 6400, 6400, 3225, 3225, 3225, 3225,
+ // 8 - 15
+ 944, 944, 944, 944, 976, 976, 976, 976,
+ // 16 - 23
+ 1456, 1456, 1456, 1456, 1488, 1488, 1488, 1488,
+ // 24 - 31
+ 718, 718, 718, 718, 718, 718, 718, 718,
+ // 32 - 39
+ 750, 750, 750, 750, 750, 750, 750, 750,
+ // 40 - 47
+ 1520, 1520, 1520, 1520, 1552, 1552, 1552, 1552,
+ // 48 - 55
+ 428, 428, 428, 428, 428, 428, 428, 428,
+ // 56 - 63
+ 428, 428, 428, 428, 428, 428, 428, 428,
+ // 64 - 71
+ 654, 654, 654, 654, 654, 654, 654, 654,
+ // 72 - 79
+ 1072, 1072, 1072, 1072, 1104, 1104, 1104, 1104,
+ // 80 - 87
+ 1136, 1136, 1136, 1136, 1168, 1168, 1168, 1168,
+ // 88 - 95
+ 1200, 1200, 1200, 1200, 1232, 1232, 1232, 1232,
+ // 96 - 103
+ 622, 622, 622, 622, 622, 622, 622, 622,
+ // 104 - 111
+ 1008, 1008, 1008, 1008, 1040, 1040, 1040, 1040,
+ // 112 - 119
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ // 120 - 127
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ // 128 - 135
+ 396, 396, 396, 396, 396, 396, 396, 396,
+ // 136 - 143
+ 396, 396, 396, 396, 396, 396, 396, 396,
+ // 144 - 151
+ 1712, 1712, 1712, 1712, 1744, 1744, 1744, 1744,
+ // 152 - 159
+ 846, 846, 846, 846, 846, 846, 846, 846,
+ // 160 - 167
+ 1264, 1264, 1264, 1264, 1296, 1296, 1296, 1296,
+ // 168 - 175
+ 1328, 1328, 1328, 1328, 1360, 1360, 1360, 1360,
+ // 176 - 183
+ 1392, 1392, 1392, 1392, 1424, 1424, 1424, 1424,
+ // 184 - 191
+ 686, 686, 686, 686, 686, 686, 686, 686,
+ // 192 - 199
+ 910, 910, 910, 910, 910, 910, 910, 910,
+ // 200 - 207
+ 1968, 1968, 1968, 1968, 2000, 2000, 2000, 2000,
+ // 208 - 215
+ 2032, 2032, 2032, 2032, 16, 16, 16, 16,
+ // 216 - 223
+ 10257, 10257, 10257, 10257, 12305, 12305, 12305, 12305,
+ // 224 - 231
+ 330, 330, 330, 330, 330, 330, 330, 330,
+ // 232 - 239
+ 330, 330, 330, 330, 330, 330, 330, 330,
+ // 240 - 247
+ 330, 330, 330, 330, 330, 330, 330, 330,
+ // 248 - 255
+ 330, 330, 330, 330, 330, 330, 330, 330,
+ // 256 - 263
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ // 264 - 271
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ // 272 - 279
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ // 280 - 287
+ 362, 362, 362, 362, 362, 362, 362, 362,
+ // 288 - 295
+ 878, 878, 878, 878, 878, 878, 878, 878,
+ // 296 - 303
+ 1904, 1904, 1904, 1904, 1936, 1936, 1936, 1936,
+ // 304 - 311
+ -18413, -18413, -16365, -16365, -14317, -14317, -10221, -10221,
+ // 312 - 319
+ 590, 590, 590, 590, 590, 590, 590, 590,
+ // 320 - 327
+ 782, 782, 782, 782, 782, 782, 782, 782,
+ // 328 - 335
+ 1584, 1584, 1584, 1584, 1616, 1616, 1616, 1616,
+ // 336 - 343
+ 1648, 1648, 1648, 1648, 1680, 1680, 1680, 1680,
+ // 344 - 351
+ 814, 814, 814, 814, 814, 814, 814, 814,
+ // 352 - 359
+ 1776, 1776, 1776, 1776, 1808, 1808, 1808, 1808,
+ // 360 - 367
+ 1840, 1840, 1840, 1840, 1872, 1872, 1872, 1872,
+ // 368 - 375
+ 6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157,
+ // 376 - 383
+ 6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157,
+ // 384 - 391
+ -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275,
+ // 392 - 399
+ -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275,
+ // 400 - 407
+ 14353, 14353, 14353, 14353, 16401, 16401, 16401, 16401,
+ // 408 - 415
+ 22547, 22547, 24595, 24595, 20497, 20497, 20497, 20497,
+ // 416 - 423
+ 18449, 18449, 18449, 18449, 26643, 26643, 28691, 28691,
+ // 424 - 431
+ 30739, 30739, -32749, -32749, -30701, -30701, -28653, -28653,
+ // 432 - 439
+ -26605, -26605, -24557, -24557, -22509, -22509, -20461, -20461,
+ // 440 - 447
+ 8207, 8207, 8207, 8207, 8207, 8207, 8207, 8207,
+ // 448 - 455
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 456 - 463
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 464 - 471
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 472 - 479
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 480 - 487
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 488 - 495
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 496 - 503
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 504 - 511
+ 72, 72, 72, 72, 72, 72, 72, 72,
+ // 512 - 519
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 520 - 527
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 528 - 535
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 536 - 543
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 544 - 551
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 552 - 559
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 560 - 567
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 568 - 575
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ // 576 - 583
+ 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
+ // 584 - 591
+ 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
+ // 592 - 599
+ 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
+ // 600 - 607
+ 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107,
+ // 608 - 615
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ // 616 - 623
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ // 624 - 631
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ // 632 - 639
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ // 640 - 647
+ 298, 298, 298, 298, 298, 298, 298, 298,
+ // 648 - 655
+ 298, 298, 298, 298, 298, 298, 298, 298,
+ // 656 - 663
+ 298, 298, 298, 298, 298, 298, 298, 298,
+ // 664 - 671
+ 298, 298, 298, 298, 298, 298, 298, 298,
+ // 672 - 679
+ 524, 524, 524, 524, 524, 524, 524, 524,
+ // 680 - 687
+ 524, 524, 524, 524, 524, 524, 524, 524,
+ // 688 - 695
+ 556, 556, 556, 556, 556, 556, 556, 556,
+ // 696 - 703
+ 556, 556, 556, 556, 556, 556, 556, 556,
+ // 704 - 711
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 712 - 719
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 720 - 727
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 728 - 735
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 736 - 743
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 744 - 751
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 752 - 759
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 760 - 767
+ 136, 136, 136, 136, 136, 136, 136, 136,
+ // 768 - 775
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 776 - 783
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 784 - 791
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 792 - 799
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 800 - 807
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 808 - 815
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 816 - 823
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 824 - 831
+ 168, 168, 168, 168, 168, 168, 168, 168,
+ // 832 - 839
+ 460, 460, 460, 460, 460, 460, 460, 460,
+ // 840 - 847
+ 460, 460, 460, 460, 460, 460, 460, 460,
+ // 848 - 855
+ 492, 492, 492, 492, 492, 492, 492, 492,
+ // 856 - 863
+ 492, 492, 492, 492, 492, 492, 492, 492,
+ // 864 - 871
+ 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
+ // 872 - 879
+ 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
+ // 880 - 887
+ 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
+ // 888 - 895
+ 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059,
+ // 896 - 903
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 904 - 911
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 912 - 919
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 920 - 927
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 928 - 935
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 936 - 943
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 944 - 951
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 952 - 959
+ 200, 200, 200, 200, 200, 200, 200, 200,
+ // 960 - 967
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 968 - 975
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 976 - 983
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 984 - 991
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 992 - 999
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 1000 - 1007
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 1008 - 1015
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ // 1016 - 1023
+ 232, 232, 232, 232, 232, 232, 232, 232,
+ };
+
+ // Additional make up codes for both White and Black runs
+ static short additionalMakeup[] = {
+ 28679, 28679, 31752, (short)32777,
+ (short)33801, (short)34825, (short)35849, (short)36873,
+ (short)29703, (short)29703, (short)30727, (short)30727,
+ (short)37897, (short)38921, (short)39945, (short)40969
+ };
+
+ // Initial black run look up table, uses the first 4 bits of a code
+ static short initBlack[] = {
+ // 0 - 7
+ 3226, 6412, 200, 168, 38, 38, 134, 134,
+ // 8 - 15
+ 100, 100, 100, 100, 68, 68, 68, 68
+ };
+
+ //
+ static short twoBitBlack[] = {292, 260, 226, 226}; // 0 - 3
+
+ // Main black run table, using the last 9 bits of possible 13 bit code
+ static short black[] = {
+ // 0 - 7
+ 62, 62, 30, 30, 0, 0, 0, 0,
+ // 8 - 15
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ // 16 - 23
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ // 24 - 31
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ // 32 - 39
+ 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
+ // 40 - 47
+ 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
+ // 48 - 55
+ 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
+ // 56 - 63
+ 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225,
+ // 64 - 71
+ 588, 588, 588, 588, 588, 588, 588, 588,
+ // 72 - 79
+ 1680, 1680, 20499, 22547, 24595, 26643, 1776, 1776,
+ // 80 - 87
+ 1808, 1808, -24557, -22509, -20461, -18413, 1904, 1904,
+ // 88 - 95
+ 1936, 1936, -16365, -14317, 782, 782, 782, 782,
+ // 96 - 103
+ 814, 814, 814, 814, -12269, -10221, 10257, 10257,
+ // 104 - 111
+ 12305, 12305, 14353, 14353, 16403, 18451, 1712, 1712,
+ // 112 - 119
+ 1744, 1744, 28691, 30739, -32749, -30701, -28653, -26605,
+ // 120 - 127
+ 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061,
+ // 128 - 135
+ 424, 424, 424, 424, 424, 424, 424, 424,
+ // 136 - 143
+ 424, 424, 424, 424, 424, 424, 424, 424,
+ // 144 - 151
+ 424, 424, 424, 424, 424, 424, 424, 424,
+ // 152 - 159
+ 424, 424, 424, 424, 424, 424, 424, 424,
+ // 160 - 167
+ 750, 750, 750, 750, 1616, 1616, 1648, 1648,
+ // 168 - 175
+ 1424, 1424, 1456, 1456, 1488, 1488, 1520, 1520,
+ // 176 - 183
+ 1840, 1840, 1872, 1872, 1968, 1968, 8209, 8209,
+ // 184 - 191
+ 524, 524, 524, 524, 524, 524, 524, 524,
+ // 192 - 199
+ 556, 556, 556, 556, 556, 556, 556, 556,
+ // 200 - 207
+ 1552, 1552, 1584, 1584, 2000, 2000, 2032, 2032,
+ // 208 - 215
+ 976, 976, 1008, 1008, 1040, 1040, 1072, 1072,
+ // 216 - 223
+ 1296, 1296, 1328, 1328, 718, 718, 718, 718,
+ // 224 - 231
+ 456, 456, 456, 456, 456, 456, 456, 456,
+ // 232 - 239
+ 456, 456, 456, 456, 456, 456, 456, 456,
+ // 240 - 247
+ 456, 456, 456, 456, 456, 456, 456, 456,
+ // 248 - 255
+ 456, 456, 456, 456, 456, 456, 456, 456,
+ // 256 - 263
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 264 - 271
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 272 - 279
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 280 - 287
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 288 - 295
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 296 - 303
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 304 - 311
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 312 - 319
+ 326, 326, 326, 326, 326, 326, 326, 326,
+ // 320 - 327
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 328 - 335
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 336 - 343
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 344 - 351
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 352 - 359
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 360 - 367
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 368 - 375
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 376 - 383
+ 358, 358, 358, 358, 358, 358, 358, 358,
+ // 384 - 391
+ 490, 490, 490, 490, 490, 490, 490, 490,
+ // 392 - 399
+ 490, 490, 490, 490, 490, 490, 490, 490,
+ // 400 - 407
+ 4113, 4113, 6161, 6161, 848, 848, 880, 880,
+ // 408 - 415
+ 912, 912, 944, 944, 622, 622, 622, 622,
+ // 416 - 423
+ 654, 654, 654, 654, 1104, 1104, 1136, 1136,
+ // 424 - 431
+ 1168, 1168, 1200, 1200, 1232, 1232, 1264, 1264,
+ // 432 - 439
+ 686, 686, 686, 686, 1360, 1360, 1392, 1392,
+ // 440 - 447
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ // 448 - 455
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 456 - 463
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 464 - 471
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 472 - 479
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 480 - 487
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 488 - 495
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 496 - 503
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ // 504 - 511
+ 390, 390, 390, 390, 390, 390, 390, 390,
+ };
+
+ static byte twoDCodes[] = {
+ // 0 - 7
+ 80, 88, 23, 71, 30, 30, 62, 62,
+ // 8 - 15
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ // 16 - 23
+ 11, 11, 11, 11, 11, 11, 11, 11,
+ // 24 - 31
+ 11, 11, 11, 11, 11, 11, 11, 11,
+ // 32 - 39
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ // 40 - 47
+ 35, 35, 35, 35, 35, 35, 35, 35,
+ // 48 - 55
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ // 56 - 63
+ 51, 51, 51, 51, 51, 51, 51, 51,
+ // 64 - 71
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 72 - 79
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 80 - 87
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 88 - 95
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 96 - 103
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 104 - 111
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 112 - 119
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ // 120 - 127
+ 41, 41, 41, 41, 41, 41, 41, 41,
+ };
+
+ /**
+ * @param fillOrder The fill order of the compressed data bytes.
+ * @param w
+ * @param h
+ */
+ public TIFFFaxDecoder(int fillOrder, int w, int h) {
+ this.fillOrder = fillOrder;
+ this.w = w;
+ this.h = h;
+
+ this.bitPointer = 0;
+ this.bytePointer = 0;
+ this.prevChangingElems = new int[w];
+ this.currChangingElems = new int[w];
+ }
+
+ // One-dimensional decoding methods
+
+ public void decode1D(byte[] buffer, byte[] compData,
+ int startX, int height) {
+ this.data = compData;
+
+ int lineOffset = 0;
+ int scanlineStride = (w + 7)/8;
+
+ bitPointer = 0;
+ bytePointer = 0;
+
+ for (int i = 0; i < height; i++) {
+ decodeNextScanline(buffer, lineOffset, startX);
+ lineOffset += scanlineStride;
+ }
+ }
+
+ public void decodeNextScanline(byte[] buffer,
+ int lineOffset, int bitOffset) {
+ int bits = 0, code = 0, isT = 0;
+ int current, entry, twoBits;
+ boolean isWhite = true;
+
+ // Initialize starting of the changing elements array
+ changingElemSize = 0;
+
+ // While scanline not complete
+ while (bitOffset < w) {
+ while (isWhite) {
+ // White run
+ current = nextNBits(10);
+ entry = white[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x0f;
+
+ if (bits == 12) { // Additional Make up code
+ // Get the next 2 bits
+ twoBits = nextLesserThan8Bits(2);
+ // Consolidate the 2 new bits and last 2 bits into 4 bits
+ current = ((current << 2) & 0x000c) | twoBits;
+ entry = additionalMakeup[current];
+ bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111
+ code = (entry >>> 4) & 0x0fff; // 12 bits
+ bitOffset += code; // Skip white run
+
+ updatePointer(4 - bits);
+ } else if (bits == 0) { // ERROR
+ throw new RuntimeException("Invalid code encountered.");
+ } else if (bits == 15) { // EOL
+ throw new RuntimeException("EOL code word encountered in White run.");
+ } else {
+ // 11 bits - 0000 0111 1111 1111 = 0x07ff
+ code = (entry >>> 5) & 0x07ff;
+ bitOffset += code;
+
+ updatePointer(10 - bits);
+ if (isT == 0) {
+ isWhite = false;
+ currChangingElems[changingElemSize++] = bitOffset;
+ }
+ }
+ }
+
+ // Check whether this run completed one width, if so
+ // advance to next byte boundary for compression = 2.
+ if (bitOffset == w) {
+ if (compression == 2) {
+ advancePointer();
+ }
+ break;
+ }
+
+ while (isWhite == false) {
+ // Black run
+ current = nextLesserThan8Bits(4);
+ entry = initBlack[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x000f;
+ code = (entry >>> 5) & 0x07ff;
+
+ if (code == 100) {
+ current = nextNBits(9);
+ entry = black[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x000f;
+ code = (entry >>> 5) & 0x07ff;
+
+ if (bits == 12) {
+ // Additional makeup codes
+ updatePointer(5);
+ current = nextLesserThan8Bits(4);
+ entry = additionalMakeup[current];
+ bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111
+ code = (entry >>> 4) & 0x0fff; // 12 bits
+
+ setToBlack(buffer, lineOffset, bitOffset, code);
+ bitOffset += code;
+
+ updatePointer(4 - bits);
+ } else if (bits == 15) {
+ // EOL code
+ throw new RuntimeException("EOL code word encountered in Black run.");
+ } else {
+ setToBlack(buffer, lineOffset, bitOffset, code);
+ bitOffset += code;
+
+ updatePointer(9 - bits);
+ if (isT == 0) {
+ isWhite = true;
+ currChangingElems[changingElemSize++] = bitOffset;
+ }
+ }
+ } else if (code == 200) {
+ // Is a Terminating code
+ current = nextLesserThan8Bits(2);
+ entry = twoBitBlack[current];
+ code = (entry >>> 5) & 0x07ff;
+ bits = (entry >>> 1) & 0x0f;
+
+ setToBlack(buffer, lineOffset, bitOffset, code);
+ bitOffset += code;
+
+ updatePointer(2 - bits);
+ isWhite = true;
+ currChangingElems[changingElemSize++] = bitOffset;
+ } else {
+ // Is a Terminating code
+ setToBlack(buffer, lineOffset, bitOffset, code);
+ bitOffset += code;
+
+ updatePointer(4 - bits);
+ isWhite = true;
+ currChangingElems[changingElemSize++] = bitOffset;
+ }
+ }
+
+ // Check whether this run completed one width
+ if (bitOffset == w) {
+ if (compression == 2) {
+ advancePointer();
+ }
+ break;
+ }
+ }
+
+ currChangingElems[changingElemSize++] = bitOffset;
+ }
+
+ // Two-dimensional decoding methods
+
+ public void decode2D(byte[] buffer,
+ byte compData[],
+ int startX,
+ int height,
+ long tiffT4Options) {
+ this.data = compData;
+ compression = 3;
+
+ bitPointer = 0;
+ bytePointer = 0;
+
+ int scanlineStride = (w + 7)/8;
+
+ int a0, a1, b1, b2;
+ int[] b = new int[2];
+ int entry, code, bits;
+ boolean isWhite;
+ int currIndex = 0;
+ int temp[];
+
+ // fillBits - dealt with this in readEOL
+ // 1D/2D encoding - dealt with this in readEOL
+
+ // uncompressedMode - haven't dealt with this yet.
+
+
+ oneD = (int)(tiffT4Options & 0x01);
+ uncompressedMode = (int)((tiffT4Options & 0x02) >> 1);
+ fillBits = (int)((tiffT4Options & 0x04) >> 2);
+
+ // The data must start with an EOL code
+ if (readEOL(true) != 1) {
+ throw new RuntimeException("First scanline must be 1D encoded.");
+ }
+
+ int lineOffset = 0;
+ int bitOffset;
+
+ // Then the 1D encoded scanline data will occur, changing elements
+ // array gets set.
+ decodeNextScanline(buffer, lineOffset, startX);
+ lineOffset += scanlineStride;
+
+ for (int lines = 1; lines < height; lines++) {
+
+ // Every line must begin with an EOL followed by a bit which
+ // indicates whether the following scanline is 1D or 2D encoded.
+ if (readEOL(false) == 0) {
+ // 2D encoded scanline follows
+
+ // Initialize previous scanlines changing elements, and
+ // initialize current scanline's changing elements array
+ temp = prevChangingElems;
+ prevChangingElems = currChangingElems;
+ currChangingElems = temp;
+ currIndex = 0;
+
+ // a0 has to be set just before the start of this scanline.
+ a0 = -1;
+ isWhite = true;
+ bitOffset = startX;
+
+ lastChangingElement = 0;
+
+ while (bitOffset < w) {
+ // Get the next changing element
+ getNextChangingElement(a0, isWhite, b);
+
+ b1 = b[0];
+ b2 = b[1];
+
+ // Get the next seven bits
+ entry = nextLesserThan8Bits(7);
+
+ // Run these through the 2DCodes table
+ entry = (int)(twoDCodes[entry] & 0xff);
+
+ // Get the code and the number of bits used up
+ code = (entry & 0x78) >>> 3;
+ bits = entry & 0x07;
+
+ if (code == 0) {
+ if (!isWhite) {
+ setToBlack(buffer, lineOffset, bitOffset,
+ b2 - bitOffset);
+ }
+ bitOffset = a0 = b2;
+
+ // Set pointer to consume the correct number of bits.
+ updatePointer(7 - bits);
+ } else if (code == 1) {
+ // Horizontal
+ updatePointer(7 - bits);
+
+ // identify the next 2 codes.
+ int number;
+ if (isWhite) {
+ number = decodeWhiteCodeWord();
+ bitOffset += number;
+ currChangingElems[currIndex++] = bitOffset;
+
+ number = decodeBlackCodeWord();
+ setToBlack(buffer, lineOffset, bitOffset, number);
+ bitOffset += number;
+ currChangingElems[currIndex++] = bitOffset;
+ } else {
+ number = decodeBlackCodeWord();
+ setToBlack(buffer, lineOffset, bitOffset, number);
+ bitOffset += number;
+ currChangingElems[currIndex++] = bitOffset;
+
+ number = decodeWhiteCodeWord();
+ bitOffset += number;
+ currChangingElems[currIndex++] = bitOffset;
+ }
+
+ a0 = bitOffset;
+ } else if (code <= 8) {
+ // Vertical
+ a1 = b1 + (code - 5);
+
+ currChangingElems[currIndex++] = a1;
+
+ // We write the current color till a1 - 1 pos,
+ // since a1 is where the next color starts
+ if (!isWhite) {
+ setToBlack(buffer, lineOffset, bitOffset,
+ a1 - bitOffset);
+ }
+ bitOffset = a0 = a1;
+ isWhite = !isWhite;
+
+ updatePointer(7 - bits);
+ } else {
+ throw new RuntimeException("Invalid code encountered while decoding 2D group 3 compressed data.");
+ }
+ }
+
+ // Add the changing element beyond the current scanline for the
+ // other color too
+ currChangingElems[currIndex++] = bitOffset;
+ changingElemSize = currIndex;
+ } else {
+ // 1D encoded scanline follows
+ decodeNextScanline(buffer, lineOffset, startX);
+ }
+
+ lineOffset += scanlineStride;
+ }
+ }
+
+ public synchronized void decodeT6(byte[] buffer,
+ byte[] compData,
+ int startX,
+ int height,
+ long tiffT6Options) {
+ this.data = compData;
+ compression = 4;
+
+ bitPointer = 0;
+ bytePointer = 0;
+
+ int scanlineStride = (w + 7)/8;
+
+ int a0, a1, b1, b2;
+ int entry, code, bits;
+ boolean isWhite;
+ int currIndex;
+ int temp[];
+
+ // Return values from getNextChangingElement
+ int[] b = new int[2];
+
+ // uncompressedMode - have written some code for this, but this
+ // has not been tested due to lack of test images using this optional
+
+ uncompressedMode = (int)((tiffT6Options & 0x02) >> 1);
+
+ // Local cached reference
+ int[] cce = currChangingElems;
+
+ // Assume invisible preceding row of all white pixels and insert
+ // both black and white changing elements beyond the end of this
+ // imaginary scanline.
+ changingElemSize = 0;
+ cce[changingElemSize++] = w;
+ cce[changingElemSize++] = w;
+
+ int lineOffset = 0;
+ int bitOffset;
+
+ for (int lines = 0; lines < height; lines++) {
+ // a0 has to be set just before the start of the scanline.
+ a0 = -1;
+ isWhite = true;
+
+ // Assign the changing elements of the previous scanline to
+ // prevChangingElems and start putting this new scanline's
+ // changing elements into the currChangingElems.
+ temp = prevChangingElems;
+ prevChangingElems = currChangingElems;
+ cce = currChangingElems = temp;
+ currIndex = 0;
+
+ // Start decoding the scanline at startX in the raster
+ bitOffset = startX;
+
+ // Reset search start position for getNextChangingElement
+ lastChangingElement = 0;
+
+ // Till one whole scanline is decoded
+ while (bitOffset < w) {
+ // Get the next changing element
+ getNextChangingElement(a0, isWhite, b);
+ b1 = b[0];
+ b2 = b[1];
+
+ // Get the next seven bits
+ entry = nextLesserThan8Bits(7);
+ // Run these through the 2DCodes table
+ entry = (int)(twoDCodes[entry] & 0xff);
+
+ // Get the code and the number of bits used up
+ code = (entry & 0x78) >>> 3;
+ bits = entry & 0x07;
+
+ if (code == 0) { // Pass
+ // We always assume WhiteIsZero format for fax.
+ if (!isWhite) {
+ setToBlack(buffer, lineOffset, bitOffset,
+ b2 - bitOffset);
+ }
+ bitOffset = a0 = b2;
+
+ // Set pointer to only consume the correct number of bits.
+ updatePointer(7 - bits);
+ } else if (code == 1) { // Horizontal
+ // Set pointer to only consume the correct number of bits.
+ updatePointer(7 - bits);
+
+ // identify the next 2 alternating color codes.
+ int number;
+ if (isWhite) {
+ // Following are white and black runs
+ number = decodeWhiteCodeWord();
+ bitOffset += number;
+ cce[currIndex++] = bitOffset;
+
+ number = decodeBlackCodeWord();
+ setToBlack(buffer, lineOffset, bitOffset, number);
+ bitOffset += number;
+ cce[currIndex++] = bitOffset;
+ } else {
+ // First a black run and then a white run follows
+ number = decodeBlackCodeWord();
+ setToBlack(buffer, lineOffset, bitOffset, number);
+ bitOffset += number;
+ cce[currIndex++] = bitOffset;
+
+ number = decodeWhiteCodeWord();
+ bitOffset += number;
+ cce[currIndex++] = bitOffset;
+ }
+
+ a0 = bitOffset;
+ } else if (code <= 8) { // Vertical
+ a1 = b1 + (code - 5);
+ cce[currIndex++] = a1;
+
+ // We write the current color till a1 - 1 pos,
+ // since a1 is where the next color starts
+ if (!isWhite) {
+ setToBlack(buffer, lineOffset, bitOffset,
+ a1 - bitOffset);
+ }
+ bitOffset = a0 = a1;
+ isWhite = !isWhite;
+
+ updatePointer(7 - bits);
+ } else if (code == 11) {
+ if (nextLesserThan8Bits(3) != 7) {
+ throw new RuntimeException("Invalid code encountered while decoding 2D group 4 compressed data.");
+ }
+
+ int zeros = 0;
+ boolean exit = false;
+
+ while (!exit) {
+ while (nextLesserThan8Bits(1) != 1) {
+ zeros++;
+ }
+
+ if (zeros > 5) {
+ // Exit code
+
+ // Zeros before exit code
+ zeros = zeros - 6;
+
+ if (!isWhite && (zeros > 0)) {
+ cce[currIndex++] = bitOffset;
+ }
+
+ // Zeros before the exit code
+ bitOffset += zeros;
+ if (zeros > 0) {
+ // Some zeros have been written
+ isWhite = true;
+ }
+
+ // Read in the bit which specifies the color of
+ // the following run
+ if (nextLesserThan8Bits(1) == 0) {
+ if (!isWhite) {
+ cce[currIndex++] = bitOffset;
+ }
+ isWhite = true;
+ } else {
+ if (isWhite) {
+ cce[currIndex++] = bitOffset;
+ }
+ isWhite = false;
+ }
+
+ exit = true;
+ }
+
+ if (zeros == 5) {
+ if (!isWhite) {
+ cce[currIndex++] = bitOffset;
+ }
+ bitOffset += zeros;
+
+ // Last thing written was white
+ isWhite = true;
+ } else {
+ bitOffset += zeros;
+
+ cce[currIndex++] = bitOffset;
+ setToBlack(buffer, lineOffset, bitOffset, 1);
+ ++bitOffset;
+
+ // Last thing written was black
+ isWhite = false;
+ }
+
+ }
+ } else {
+ throw new RuntimeException("Invalid code encountered while decoding 2D group 4 compressed data.");
+ }
+ }
+
+ // Add the changing element beyond the current scanline for the
+ // other color too
+ //make sure that the index does not exceed the bounds of the array
+ if(currIndex < cce.length)
+ cce[currIndex++] = bitOffset;
+
+ // Number of changing elements in this scanline.
+ changingElemSize = currIndex;
+
+ lineOffset += scanlineStride;
+ }
+ }
+
+ private void setToBlack(byte[] buffer,
+ int lineOffset, int bitOffset,
+ int numBits) {
+ int bitNum = 8*lineOffset + bitOffset;
+ int lastBit = bitNum + numBits;
+
+ int byteNum = bitNum >> 3;
+
+ // Handle bits in first byte
+ int shift = bitNum & 0x7;
+ if (shift > 0) {
+ int maskVal = 1 << (7 - shift);
+ byte val = buffer[byteNum];
+ while (maskVal > 0 && bitNum < lastBit) {
+ val |= maskVal;
+ maskVal >>= 1;
+ ++bitNum;
+ }
+ buffer[byteNum] = val;
+ }
+
+ // Fill in 8 bits at a time
+ byteNum = bitNum >> 3;
+ while (bitNum < lastBit - 7) {
+ buffer[byteNum++] = (byte)255;
+ bitNum += 8;
+ }
+
+ // Fill in remaining bits
+ while (bitNum < lastBit) {
+ byteNum = bitNum >> 3;
+ buffer[byteNum] |= 1 << (7 - (bitNum & 0x7));
+ ++bitNum;
+ }
+ }
+
+ // Returns run length
+ private int decodeWhiteCodeWord() {
+ int current, entry, bits, isT, twoBits, code = -1;
+ int runLength = 0;
+ boolean isWhite = true;
+
+ while (isWhite) {
+ current = nextNBits(10);
+ entry = white[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x0f;
+
+ if (bits == 12) { // Additional Make up code
+ // Get the next 2 bits
+ twoBits = nextLesserThan8Bits(2);
+ // Consolidate the 2 new bits and last 2 bits into 4 bits
+ current = ((current << 2) & 0x000c) | twoBits;
+ entry = additionalMakeup[current];
+ bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111
+ code = (entry >>> 4) & 0x0fff; // 12 bits
+ runLength += code;
+ updatePointer(4 - bits);
+ } else if (bits == 0) { // ERROR
+ throw new RuntimeException("Invalid code encountered.");
+ } else if (bits == 15) { // EOL
+ throw new RuntimeException("EOL code word encountered in White run.");
+ } else {
+ // 11 bits - 0000 0111 1111 1111 = 0x07ff
+ code = (entry >>> 5) & 0x07ff;
+ runLength += code;
+ updatePointer(10 - bits);
+ if (isT == 0) {
+ isWhite = false;
+ }
+ }
+ }
+
+ return runLength;
+ }
+
+ // Returns run length
+ private int decodeBlackCodeWord() {
+ int current, entry, bits, isT, code = -1;
+ int runLength = 0;
+ boolean isWhite = false;
+
+ while (!isWhite) {
+ current = nextLesserThan8Bits(4);
+ entry = initBlack[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x000f;
+ code = (entry >>> 5) & 0x07ff;
+
+ if (code == 100) {
+ current = nextNBits(9);
+ entry = black[current];
+
+ // Get the 3 fields from the entry
+ isT = entry & 0x0001;
+ bits = (entry >>> 1) & 0x000f;
+ code = (entry >>> 5) & 0x07ff;
+
+ if (bits == 12) {
+ // Additional makeup codes
+ updatePointer(5);
+ current = nextLesserThan8Bits(4);
+ entry = additionalMakeup[current];
+ bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111
+ code = (entry >>> 4) & 0x0fff; // 12 bits
+ runLength += code;
+
+ updatePointer(4 - bits);
+ } else if (bits == 15) {
+ // EOL code
+ throw new RuntimeException("EOL code word encountered in Black run.");
+ } else {
+ runLength += code;
+ updatePointer(9 - bits);
+ if (isT == 0) {
+ isWhite = true;
+ }
+ }
+ } else if (code == 200) {
+ // Is a Terminating code
+ current = nextLesserThan8Bits(2);
+ entry = twoBitBlack[current];
+ code = (entry >>> 5) & 0x07ff;
+ runLength += code;
+ bits = (entry >>> 1) & 0x0f;
+ updatePointer(2 - bits);
+ isWhite = true;
+ } else {
+ // Is a Terminating code
+ runLength += code;
+ updatePointer(4 - bits);
+ isWhite = true;
+ }
+ }
+
+ return runLength;
+ }
+
+ private int readEOL(boolean isFirstEOL) {
+ if (fillBits == 0) {
+ int next12Bits = nextNBits(12);
+ if (isFirstEOL && next12Bits == 0) {
+
+ // Might have the case of EOL padding being used even
+ // though it was not flagged in the T4Options field.
+ // This was observed to be the case in TIFFs produced
+ // by a well known vendor who shall remain nameless.
+
+ if(nextNBits(4) == 1) {
+
+ // EOL must be padded: reset the fillBits flag.
+
+ fillBits = 1;
+ return 1;
+ }
+ }
+ if(next12Bits != 1) {
+ throw new RuntimeException("Scanline must begin with EOL code word.");
+ }
+ } else if (fillBits == 1) {
+
+ // First EOL code word xxxx 0000 0000 0001 will occur
+ // As many fill bits will be present as required to make
+ // the EOL code of 12 bits end on a byte boundary.
+
+ int bitsLeft = 8 - bitPointer;
+
+ if (nextNBits(bitsLeft) != 0) {
+ throw new RuntimeException("All fill bits preceding EOL code must be 0.");
+ }
+
+ // If the number of bitsLeft is less than 8, then to have a 12
+ // bit EOL sequence, two more bytes are certainly going to be
+ // required. The first of them has to be all zeros, so ensure
+ // that.
+ if (bitsLeft < 4) {
+ if (nextNBits(8) != 0) {
+ throw new RuntimeException("All fill bits preceding EOL code must be 0.");
+ }
+ }
+
+ // There might be a random number of fill bytes with 0s, so
+ // loop till the EOL of 0000 0001 is found, as long as all
+ // the bytes preceding it are 0's.
+ int n;
+ while ((n = nextNBits(8)) != 1) {
+
+ // If not all zeros
+ if (n != 0) {
+ throw new RuntimeException("All fill bits preceding EOL code must be 0.");
+ }
+ }
+ }
+
+ // If one dimensional encoding mode, then always return 1
+ if (oneD == 0) {
+ return 1;
+ } else {
+ // Otherwise for 2D encoding mode,
+ // The next one bit signifies 1D/2D encoding of next line.
+ return nextLesserThan8Bits(1);
+ }
+ }
+
+ private void getNextChangingElement(int a0, boolean isWhite, int[] ret) {
+ // Local copies of instance variables
+ int[] pce = this.prevChangingElems;
+ int ces = this.changingElemSize;
+
+ // If the previous match was at an odd element, we still
+ // have to search the preceeding element.
+ // int start = lastChangingElement & ~0x1;
+ int start = lastChangingElement > 0 ? lastChangingElement - 1 : 0;
+ if (isWhite) {
+ start &= ~0x1; // Search even numbered elements
+ } else {
+ start |= 0x1; // Search odd numbered elements
+ }
+
+ int i = start;
+ for (; i < ces; i += 2) {
+ int temp = pce[i];
+ if (temp > a0) {
+ lastChangingElement = i;
+ ret[0] = temp;
+ break;
+ }
+ }
+
+ if (i + 1 < ces) {
+ ret[1] = pce[i + 1];
+ }
+ }
+
+ private int nextNBits(int bitsToGet) {
+ byte b, next, next2next;
+ int l = data.length - 1;
+ int bp = this.bytePointer;
+
+ if (fillOrder == 1) {
+ b = data[bp];
+
+ if (bp == l) {
+ next = 0x00;
+ next2next = 0x00;
+ } else if ((bp + 1) == l) {
+ next = data[bp + 1];
+ next2next = 0x00;
+ } else {
+ next = data[bp + 1];
+ next2next = data[bp + 2];
+ }
+ } else if (fillOrder == 2) {
+ b = flipTable[data[bp] & 0xff];
+
+ if (bp == l) {
+ next = 0x00;
+ next2next = 0x00;
+ } else if ((bp + 1) == l) {
+ next = flipTable[data[bp + 1] & 0xff];
+ next2next = 0x00;
+ } else {
+ next = flipTable[data[bp + 1] & 0xff];
+ next2next = flipTable[data[bp + 2] & 0xff];
+ }
+ } else {
+ throw new RuntimeException("TIFF_FILL_ORDER tag must be either 1 or 2.");
+ }
+
+ int bitsLeft = 8 - bitPointer;
+ int bitsFromNextByte = bitsToGet - bitsLeft;
+ int bitsFromNext2NextByte = 0;
+ if (bitsFromNextByte > 8) {
+ bitsFromNext2NextByte = bitsFromNextByte - 8;
+ bitsFromNextByte = 8;
+ }
+
+ bytePointer++;
+
+ int i1 = (b & table1[bitsLeft]) << (bitsToGet - bitsLeft);
+ int i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte);
+
+ int i3 = 0;
+ if (bitsFromNext2NextByte != 0) {
+ i2 <<= bitsFromNext2NextByte;
+ i3 = (next2next & table2[bitsFromNext2NextByte]) >>>
+ (8 - bitsFromNext2NextByte);
+ i2 |= i3;
+ bytePointer++;
+ bitPointer = bitsFromNext2NextByte;
+ } else {
+ if (bitsFromNextByte == 8) {
+ bitPointer = 0;
+ bytePointer++;
+ } else {
+ bitPointer = bitsFromNextByte;
+ }
+ }
+
+ int i = i1 | i2;
+ return i;
+ }
+
+ private int nextLesserThan8Bits(int bitsToGet) {
+ byte b, next;
+ int l = data.length - 1;
+ int bp = this.bytePointer;
+
+ if (fillOrder == 1) {
+ b = data[bp];
+ if (bp == l) {
+ next = 0x00;
+ } else {
+ next = data[bp + 1];
+ }
+ } else if (fillOrder == 2) {
+ b = flipTable[data[bp] & 0xff];
+ if (bp == l) {
+ next = 0x00;
+ } else {
+ next = flipTable[data[bp + 1] & 0xff];
+ }
+ } else {
+ throw new RuntimeException("TIFF_FILL_ORDER tag must be either 1 or 2.");
+ }
+
+ int bitsLeft = 8 - bitPointer;
+ int bitsFromNextByte = bitsToGet - bitsLeft;
+
+ int shift = bitsLeft - bitsToGet;
+ int i1, i2;
+ if (shift >= 0) {
+ i1 = (b & table1[bitsLeft]) >>> shift;
+ bitPointer += bitsToGet;
+ if (bitPointer == 8) {
+ bitPointer = 0;
+ bytePointer++;
+ }
+ } else {
+ i1 = (b & table1[bitsLeft]) << (-shift);
+ i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte);
+
+ i1 |= i2;
+ bytePointer++;
+ bitPointer = bitsFromNextByte;
+ }
+
+ return i1;
+ }
+
+ // Move pointer backwards by given amount of bits
+ private void updatePointer(int bitsToMoveBack) {
+ int i = bitPointer - bitsToMoveBack;
+
+ if (i < 0) {
+ bytePointer--;
+ bitPointer = 8 + i;
+ } else {
+ bitPointer = i;
+ }
+ }
+
+ // Move to the next byte boundary
+ private boolean advancePointer() {
+ if (bitPointer != 0) {
+ bytePointer++;
+ bitPointer = 0;
+ }
+
+ return true;
+ }
+}
+
diff --git a/src/main/java/com/lowagie/text/pdf/codec/TIFFField.java b/src/main/java/com/lowagie/text/pdf/codec/TIFFField.java
new file mode 100644
index 0000000..ac3b068
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/TIFFField.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 2001 Sun Microsystems, 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.
+ *
+ * -Redistribution in binary form must reproduct the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
+ * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
+ * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
+ * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
+ * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
+ * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that Software is not designed,licensed or intended for use in
+ * the design, construction, operation or maintenance of any nuclear facility.
+ */
+package com.lowagie.text.pdf.codec;
+
+import java.io.Serializable;
+
+/**
+ * A class representing a field in a TIFF 6.0 Image File Directory.
+ *
+ *
+ *
+ */
+ public TIFFField(int tag, int type, int count, Object data) {
+ this.tag = tag;
+ this.type = type;
+ this.count = count;
+ this.data = data;
+ }
+
+ /**
+ * Returns the tag number, between 0 and 65535.
+ */
+ public int getTag() {
+ return tag;
+ }
+
+ /**
+ * Returns the type of the data stored in the IFD.
+ * For a TIFF6.0 file, the value will equal one of the
+ * TIFF_ constants defined in this class. For future
+ * revisions of TIFF, higher values are possible.
+ *
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Returns the number of elements in the IFD.
+ */
+ public int getCount() {
+ return count;
+ }
+
+ /**
+ * Returns the data as an uninterpreted array of bytes.
+ * The type of the field must be one of TIFF_BYTE, TIFF_SBYTE,
+ * or TIFF_UNDEFINED;
+ *
+ *
+ * TIFF type Java type
+ *
+ * TIFF_BYTE byte
+ *
+ * TIFF_ASCII String
+ *
+ * TIFF_SHORT char
+ *
+ * TIFF_LONG long
+ *
+ * TIFF_RATIONAL long[2]
+ *
+ * TIFF_SBYTE byte
+ *
+ * TIFF_UNDEFINED byte
+ *
+ * TIFF_SSHORT short
+ *
+ * TIFF_SLONG int
+ *
+ * TIFF_SRATIONAL int[2]
+ *
+ * TIFF_FLOAT float
+ *
+ * TIFF_DOUBLE double
+ * TIFFField
with another
+ * TIFFField
by comparing the tags.
+ *
+ * equals()
.
+ *
+ * @throws IllegalArgumentException if the parameter is null
.
+ * @throws ClassCastException if the parameter is not a
+ * TIFFField
.
+ */
+ public int compareTo(Object o) {
+ if(o == null) {
+ throw new IllegalArgumentException();
+ }
+
+ int oTag = ((TIFFField)o).getTag();
+
+ if(tag < oTag) {
+ return -1;
+ } else if(tag > oTag) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/codec/TIFFLZWDecoder.java b/src/main/java/com/lowagie/text/pdf/codec/TIFFLZWDecoder.java
new file mode 100644
index 0000000..ca0f91a
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/TIFFLZWDecoder.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2001 Sun Microsystems, 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.
+ *
+ * -Redistribution in binary form must reproduct the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
+ * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
+ * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
+ * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
+ * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
+ * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
+ * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that Software is not designed,licensed or intended for use in
+ * the design, construction, operation or maintenance of any nuclear facility.
+ */
+package com.lowagie.text.pdf.codec;
+
+/**
+ * A class for performing LZW decoding.
+ *
+ *
+ */
+public class TIFFLZWDecoder {
+
+ byte stringTable[][];
+ byte data[] = null, uncompData[];
+ int tableIndex, bitsToGet = 9;
+ int bytePointer, bitPointer;
+ int dstIndex;
+ int w, h;
+ int predictor, samplesPerPixel;
+ int nextData = 0;
+ int nextBits = 0;
+
+ int andTable[] = {
+ 511,
+ 1023,
+ 2047,
+ 4095
+ };
+
+ public TIFFLZWDecoder(int w, int predictor, int samplesPerPixel) {
+ this.w = w;
+ this.predictor = predictor;
+ this.samplesPerPixel = samplesPerPixel;
+ }
+
+ /**
+ * Method to decode LZW compressed data.
+ *
+ * @param data The compressed data.
+ * @param uncompData Array to return the uncompressed data in.
+ * @param h The number of rows the compressed data contains.
+ */
+ public byte[] decode(byte data[], byte uncompData[], int h) {
+
+ if(data[0] == (byte)0x00 && data[1] == (byte)0x01) {
+ throw new UnsupportedOperationException("TIFF 5.0-style LZW codes are not supported.");
+ }
+
+ initializeStringTable();
+
+ this.data = data;
+ this.h = h;
+ this.uncompData = uncompData;
+
+ // Initialize pointers
+ bytePointer = 0;
+ bitPointer = 0;
+ dstIndex = 0;
+
+
+ nextData = 0;
+ nextBits = 0;
+
+ int code, oldCode = 0;
+ byte string[];
+
+ while ( ((code = getNextCode()) != 257) &&
+ dstIndex < uncompData.length) {
+
+ if (code == 256) {
+
+ initializeStringTable();
+ code = getNextCode();
+
+ if (code == 257) {
+ break;
+ }
+
+ writeString(stringTable[code]);
+ oldCode = code;
+
+ } else {
+
+ if (code < tableIndex) {
+
+ string = stringTable[code];
+
+ writeString(string);
+ addStringToTable(stringTable[oldCode], string[0]);
+ oldCode = code;
+
+ } else {
+
+ string = stringTable[oldCode];
+ string = composeString(string, string[0]);
+ writeString(string);
+ addStringToTable(string);
+ oldCode = code;
+ }
+
+ }
+
+ }
+
+ // Horizontal Differencing Predictor
+ if (predictor == 2) {
+
+ int count;
+ for (int j = 0; j < h; j++) {
+
+ count = samplesPerPixel * (j * w + 1);
+
+ for (int i = samplesPerPixel; i < w * samplesPerPixel; i++) {
+
+ uncompData[count] += uncompData[count - samplesPerPixel];
+ count++;
+ }
+ }
+ }
+
+ return uncompData;
+ }
+
+
+ /**
+ * Initialize the string table.
+ */
+ public void initializeStringTable() {
+
+ stringTable = new byte[4096][];
+
+ for (int i=0; i<256; i++) {
+ stringTable[i] = new byte[1];
+ stringTable[i][0] = (byte)i;
+ }
+
+ tableIndex = 258;
+ bitsToGet = 9;
+ }
+
+ /**
+ * Write out the string just uncompressed.
+ */
+ public void writeString(byte string[]) {
+
+ for (int i=0; ioldString
.
+ */
+ public byte[] composeString(byte oldString[], byte newString) {
+ int length = oldString.length;
+ byte string[] = new byte[length + 1];
+ System.arraycopy(oldString, 0, string, 0, length);
+ string[length] = newString;
+
+ return string;
+ }
+
+ // Returns the next 9, 10, 11 or 12 bits
+ public int getNextCode() {
+ // Attempt to get the next code. The exception is caught to make
+ // this robust to cases wherein the EndOfInformation code has been
+ // omitted from a strip. Examples of such cases have been observed
+ // in practice.
+ try {
+ nextData = (nextData << 8) | (data[bytePointer++] & 0xff);
+ nextBits += 8;
+
+ if (nextBits < bitsToGet) {
+ nextData = (nextData << 8) | (data[bytePointer++] & 0xff);
+ nextBits += 8;
+ }
+
+ int code =
+ (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet-9];
+ nextBits -= bitsToGet;
+
+ return code;
+ } catch(ArrayIndexOutOfBoundsException e) {
+ // Strip not terminated as expected: return EndOfInformation code.
+ return 257;
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/pdf/codec/TiffImage.java b/src/main/java/com/lowagie/text/pdf/codec/TiffImage.java
new file mode 100644
index 0000000..cc812d7
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/TiffImage.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright 2003-2005 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf.codec;
+import com.lowagie.text.pdf.*;
+import com.lowagie.text.Image;
+import com.lowagie.text.Jpeg;
+import com.lowagie.text.ExceptionConverter;
+import java.io.*;
+import java.util.zip.*;
+import java.awt.color.ICC_Profile;
+
+/** Reads TIFF images
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class TiffImage {
+
+ /** Gets the number of pages the TIFF document has.
+ * @param s the file source
+ * @return the number of pages
+ */
+ public static int getNumberOfPages(RandomAccessFileOrArray s) {
+ try {
+ return TIFFDirectory.getNumDirectories(s);
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ static int getDpi(TIFFField fd, int resolutionUnit) {
+ if (fd == null)
+ return 0;
+ long res[] = fd.getAsRational(0);
+ float frac = (float)res[0] / (float)res[1];
+ int dpi = 0;
+ switch (resolutionUnit) {
+ case TIFFConstants.RESUNIT_INCH:
+ case TIFFConstants.RESUNIT_NONE:
+ dpi = (int)frac;
+ break;
+ case TIFFConstants.RESUNIT_CENTIMETER:
+ dpi = (int)(frac * 2.54);
+ break;
+ }
+ return dpi;
+ }
+
+ /** Reads a page from a TIFF image. Direct mode is not used.
+ * @param s the file source
+ * @param page the page to get. The first page is 1
+ * @return the Image
+ */
+ public static Image getTiffImage(RandomAccessFileOrArray s, int page) {
+ return getTiffImage(s, page, false);
+ }
+
+ /** Reads a page from a TIFF image.
+ * @param s the file source
+ * @param page the page to get. The first page is 1
+ * @param direct for single strip, CCITT images, generate the image
+ * by direct byte copying. It's faster but may not work
+ * every time
+ * @return the Image
+ */
+ public static Image getTiffImage(RandomAccessFileOrArray s, int page, boolean direct) {
+ if (page < 1)
+ throw new IllegalArgumentException("The page number must be >= 1.");
+ try {
+ TIFFDirectory dir = new TIFFDirectory(s, page - 1);
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_TILEWIDTH))
+ throw new IllegalArgumentException("Tiles are not supported.");
+ int compression = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_COMPRESSION);
+ switch (compression) {
+ case TIFFConstants.COMPRESSION_CCITTRLEW:
+ case TIFFConstants.COMPRESSION_CCITTRLE:
+ case TIFFConstants.COMPRESSION_CCITTFAX3:
+ case TIFFConstants.COMPRESSION_CCITTFAX4:
+ break;
+ default:
+ return getTiffImageColor(dir, s);
+ }
+ float rotation = 0;
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_ORIENTATION)) {
+ int rot = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_ORIENTATION);
+ if (rot == TIFFConstants.ORIENTATION_BOTRIGHT || rot == TIFFConstants.ORIENTATION_BOTLEFT)
+ rotation = (float)Math.PI;
+ else if (rot == TIFFConstants.ORIENTATION_LEFTTOP || rot == TIFFConstants.ORIENTATION_LEFTBOT)
+ rotation = (float)(Math.PI / 2.0);
+ else if (rot == TIFFConstants.ORIENTATION_RIGHTTOP || rot == TIFFConstants.ORIENTATION_RIGHTBOT)
+ rotation = -(float)(Math.PI / 2.0);
+ }
+
+ Image img = null;
+ long tiffT4Options = 0;
+ long tiffT6Options = 0;
+ int fillOrder = 1;
+ int h = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGELENGTH);
+ int w = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGEWIDTH);
+ int dpiX = 0;
+ int dpiY = 0;
+ float XYRatio = 0;
+ int resolutionUnit = TIFFConstants.RESUNIT_INCH;
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_RESOLUTIONUNIT))
+ resolutionUnit = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_RESOLUTIONUNIT);
+ dpiX = getDpi(dir.getField(TIFFConstants.TIFFTAG_XRESOLUTION), resolutionUnit);
+ dpiY = getDpi(dir.getField(TIFFConstants.TIFFTAG_YRESOLUTION), resolutionUnit);
+ if (resolutionUnit == TIFFConstants.RESUNIT_NONE) {
+ if (dpiY != 0)
+ XYRatio = (float)dpiX / (float)dpiY;
+ dpiX = 0;
+ dpiY = 0;
+ }
+ long tstrip = 0xFFFFFFFFL;
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_ROWSPERSTRIP))
+ tstrip = dir.getFieldAsLong(TIFFConstants.TIFFTAG_ROWSPERSTRIP);
+ int rowsStrip = (int)Math.min(h, tstrip);
+ long offset[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPOFFSETS);
+ long size[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPBYTECOUNTS);
+ if (size == null && h == rowsStrip) { // some TIFF producers are really lousy, so...
+ size = new long[]{s.length() - (int)offset[0]};
+ }
+ boolean reverse = false;
+ TIFFField fillOrderField = dir.getField(TIFFConstants.TIFFTAG_FILLORDER);
+ if (fillOrderField != null)
+ fillOrder = fillOrderField.getAsInt(0);
+ reverse = (fillOrder == TIFFConstants.FILLORDER_LSB2MSB);
+ int params = 0;
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_PHOTOMETRIC)) {
+ long photo = dir.getFieldAsLong(TIFFConstants.TIFFTAG_PHOTOMETRIC);
+ if (photo == TIFFConstants.PHOTOMETRIC_MINISBLACK)
+ params |= Image.CCITT_BLACKIS1;
+ }
+ int imagecomp = 0;
+ switch (compression) {
+ case TIFFConstants.COMPRESSION_CCITTRLEW:
+ case TIFFConstants.COMPRESSION_CCITTRLE:
+ imagecomp = Image.CCITTG3_1D;
+ params |= Image.CCITT_ENCODEDBYTEALIGN | Image.CCITT_ENDOFBLOCK;
+ break;
+ case TIFFConstants.COMPRESSION_CCITTFAX3:
+ imagecomp = Image.CCITTG3_1D;
+ params |= Image.CCITT_ENDOFLINE | Image.CCITT_ENDOFBLOCK;
+ TIFFField t4OptionsField = dir.getField(TIFFConstants.TIFFTAG_GROUP3OPTIONS);
+ if (t4OptionsField != null) {
+ tiffT4Options = t4OptionsField.getAsLong(0);
+ if ((tiffT4Options & TIFFConstants.GROUP3OPT_2DENCODING) != 0)
+ imagecomp = Image.CCITTG3_2D;
+ if ((tiffT4Options & TIFFConstants.GROUP3OPT_FILLBITS) != 0)
+ params |= Image.CCITT_ENCODEDBYTEALIGN;
+ }
+ break;
+ case TIFFConstants.COMPRESSION_CCITTFAX4:
+ imagecomp = Image.CCITTG4;
+ TIFFField t6OptionsField = dir.getField(TIFFConstants.TIFFTAG_GROUP4OPTIONS);
+ if (t6OptionsField != null)
+ tiffT6Options = t6OptionsField.getAsLong(0);
+ break;
+ }
+ if (direct && rowsStrip == h) { //single strip, direct
+ byte im[] = new byte[(int)size[0]];
+ s.seek(offset[0]);
+ s.readFully(im);
+ img = Image.getInstance(w, h, reverse, imagecomp, params, im);
+ img.setInverted(true);
+ }
+ else {
+ int rowsLeft = h;
+ CCITTG4Encoder g4 = new CCITTG4Encoder(w);
+ for (int k = 0; k < offset.length; ++k) {
+ byte im[] = new byte[(int)size[k]];
+ s.seek(offset[k]);
+ s.readFully(im);
+ int height = Math.min(rowsStrip, rowsLeft);
+ TIFFFaxDecoder decoder = new TIFFFaxDecoder(fillOrder, w, height);
+ byte outBuf[] = new byte[(w + 7) / 8 * height];
+ switch (compression) {
+ case TIFFConstants.COMPRESSION_CCITTRLEW:
+ case TIFFConstants.COMPRESSION_CCITTRLE:
+ decoder.decode1D(outBuf, im, 0, height);
+ g4.fax4Encode(outBuf,height);
+ break;
+ case TIFFConstants.COMPRESSION_CCITTFAX3:
+ try {
+ decoder.decode2D(outBuf, im, 0, height, tiffT4Options);
+ }
+ catch (Exception e) {
+ // let's flip the fill bits and try again...
+ tiffT4Options ^= TIFFConstants.GROUP3OPT_FILLBITS;
+ try {
+ decoder.decode2D(outBuf, im, 0, height, tiffT4Options);
+ }
+ catch (Exception e2) {
+ throw e;
+ }
+ }
+ g4.fax4Encode(outBuf, height);
+ break;
+ case TIFFConstants.COMPRESSION_CCITTFAX4:
+ decoder.decodeT6(outBuf, im, 0, height, tiffT6Options);
+ g4.fax4Encode(outBuf, height);
+ break;
+ }
+ rowsLeft -= rowsStrip;
+ }
+ byte g4pic[] = g4.close();
+ img = Image.getInstance(w, h, false, Image.CCITTG4, params & Image.CCITT_BLACKIS1, g4pic);
+ }
+ img.setDpi(dpiX, dpiY);
+ img.setXYRatio(XYRatio);
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_ICCPROFILE)) {
+ try {
+ TIFFField fd = dir.getField(TIFFConstants.TIFFTAG_ICCPROFILE);
+ ICC_Profile icc_prof = ICC_Profile.getInstance(fd.getAsBytes());
+ if (icc_prof.getNumComponents() == 1)
+ img.tagICC(icc_prof);
+ }
+ catch (Exception e) {
+ //empty
+ }
+ }
+ img.setOriginalType(Image.ORIGINAL_TIFF);
+ if (rotation != 0)
+ img.setInitialRotation(rotation);
+ return img;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ protected static Image getTiffImageColor(TIFFDirectory dir, RandomAccessFileOrArray s) {
+ try {
+ int compression = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_COMPRESSION);
+ int predictor = 1;
+ TIFFLZWDecoder lzwDecoder = null;
+ switch (compression) {
+ case TIFFConstants.COMPRESSION_NONE:
+ case TIFFConstants.COMPRESSION_LZW:
+ case TIFFConstants.COMPRESSION_PACKBITS:
+ case TIFFConstants.COMPRESSION_DEFLATE:
+ case TIFFConstants.COMPRESSION_OJPEG:
+ break;
+ default:
+ throw new IllegalArgumentException("The compression " + compression + " is not supported.");
+ }
+ int photometric = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_PHOTOMETRIC);
+ switch (photometric) {
+ case TIFFConstants.PHOTOMETRIC_MINISWHITE:
+ case TIFFConstants.PHOTOMETRIC_MINISBLACK:
+ case TIFFConstants.PHOTOMETRIC_RGB:
+ case TIFFConstants.PHOTOMETRIC_SEPARATED:
+ case TIFFConstants.PHOTOMETRIC_PALETTE:
+ break;
+ default:
+ throw new IllegalArgumentException("The photometric " + photometric + " is not supported.");
+ }
+ float rotation = 0;
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_ORIENTATION)) {
+ int rot = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_ORIENTATION);
+ if (rot == TIFFConstants.ORIENTATION_BOTRIGHT || rot == TIFFConstants.ORIENTATION_BOTLEFT)
+ rotation = (float)Math.PI;
+ else if (rot == TIFFConstants.ORIENTATION_LEFTTOP || rot == TIFFConstants.ORIENTATION_LEFTBOT)
+ rotation = (float)(Math.PI / 2.0);
+ else if (rot == TIFFConstants.ORIENTATION_RIGHTTOP || rot == TIFFConstants.ORIENTATION_RIGHTBOT)
+ rotation = -(float)(Math.PI / 2.0);
+ }
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_PLANARCONFIG)
+ && dir.getFieldAsLong(TIFFConstants.TIFFTAG_PLANARCONFIG) == TIFFConstants.PLANARCONFIG_SEPARATE)
+ throw new IllegalArgumentException("Planar images are not supported.");
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_EXTRASAMPLES))
+ throw new IllegalArgumentException("Extra samples are not supported.");
+ int samplePerPixel = 1;
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_SAMPLESPERPIXEL)) // 1,3,4
+ samplePerPixel = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_SAMPLESPERPIXEL);
+ int bitsPerSample = 1;
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_BITSPERSAMPLE))
+ bitsPerSample = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_BITSPERSAMPLE);
+ switch (bitsPerSample) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ throw new IllegalArgumentException("Bits per sample " + bitsPerSample + " is not supported.");
+ }
+ Image img = null;
+
+ int h = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGELENGTH);
+ int w = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_IMAGEWIDTH);
+ int dpiX = 0;
+ int dpiY = 0;
+ int resolutionUnit = TIFFConstants.RESUNIT_INCH;
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_RESOLUTIONUNIT))
+ resolutionUnit = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_RESOLUTIONUNIT);
+ dpiX = getDpi(dir.getField(TIFFConstants.TIFFTAG_XRESOLUTION), resolutionUnit);
+ dpiY = getDpi(dir.getField(TIFFConstants.TIFFTAG_YRESOLUTION), resolutionUnit);
+ int rowsStrip = h;
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_ROWSPERSTRIP)) //another hack for broken tiffs
+ rowsStrip = (int)dir.getFieldAsLong(TIFFConstants.TIFFTAG_ROWSPERSTRIP);
+ long offset[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPOFFSETS);
+ long size[] = getArrayLongShort(dir, TIFFConstants.TIFFTAG_STRIPBYTECOUNTS);
+ if (size == null && h == rowsStrip) { // some TIFF producers are really lousy, so...
+ size = new long[]{s.length() - (int)offset[0]};
+ }
+ if (compression == TIFFConstants.COMPRESSION_LZW) {
+ TIFFField predictorField = dir.getField(TIFFConstants.TIFFTAG_PREDICTOR);
+ if (predictorField != null) {
+ predictor = predictorField.getAsInt(0);
+ if (predictor != 1 && predictor != 2) {
+ throw new RuntimeException("Illegal value for Predictor in TIFF file.");
+ }
+ if (predictor == 2 && bitsPerSample != 8) {
+ throw new RuntimeException(bitsPerSample + "-bit samples are not supported for Horizontal differencing Predictor.");
+ }
+ }
+ lzwDecoder = new TIFFLZWDecoder(w, predictor,
+ samplePerPixel);
+ }
+ int rowsLeft = h;
+ ByteArrayOutputStream stream = null;
+ DeflaterOutputStream zip = null;
+ CCITTG4Encoder g4 = null;
+ if (bitsPerSample == 1 && samplePerPixel == 1) {
+ g4 = new CCITTG4Encoder(w);
+ }
+ else {
+ stream = new ByteArrayOutputStream();
+ if (compression != TIFFConstants.COMPRESSION_OJPEG)
+ zip = new DeflaterOutputStream(stream);
+ }
+ for (int k = 0; k < offset.length; ++k) {
+ byte im[] = new byte[(int)size[k]];
+ s.seek(offset[k]);
+ s.readFully(im);
+ int height = Math.min(rowsStrip, rowsLeft);
+ byte outBuf[] = null;
+ if (compression != TIFFConstants.COMPRESSION_NONE)
+ outBuf = new byte[(w * bitsPerSample * samplePerPixel + 7) / 8 * height];
+ switch (compression) {
+ case TIFFConstants.COMPRESSION_DEFLATE:
+ inflate(im, outBuf);
+ break;
+ case TIFFConstants.COMPRESSION_NONE:
+ outBuf = im;
+ break;
+ case TIFFConstants.COMPRESSION_PACKBITS:
+ decodePackbits(im, outBuf);
+ break;
+ case TIFFConstants.COMPRESSION_LZW:
+ lzwDecoder.decode(im, outBuf, height);
+ break;
+ case TIFFConstants.COMPRESSION_OJPEG:
+ stream.write(im);
+ break;
+ }
+ if (bitsPerSample == 1 && samplePerPixel == 1) {
+ g4.fax4Encode(outBuf, height);
+ }
+ else if (compression != TIFFConstants.COMPRESSION_OJPEG) {
+ zip.write(outBuf);
+ }
+ rowsLeft -= rowsStrip;
+ }
+ if (bitsPerSample == 1 && samplePerPixel == 1) {
+ img = Image.getInstance(w, h, false, Image.CCITTG4,
+ photometric == TIFFConstants.PHOTOMETRIC_MINISBLACK ? Image.CCITT_BLACKIS1 : 0, g4.close());
+ }
+ else {
+ if (compression == TIFFConstants.COMPRESSION_OJPEG) {
+ img = new Jpeg(stream.toByteArray());
+ }
+ else {
+ zip.close();
+ img = Image.getInstance(w, h, samplePerPixel, bitsPerSample, stream.toByteArray());
+ img.setDeflated(true);
+ }
+ }
+ img.setDpi(dpiX, dpiY);
+ if (compression != TIFFConstants.COMPRESSION_OJPEG) {
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_ICCPROFILE)) {
+ try {
+ TIFFField fd = dir.getField(TIFFConstants.TIFFTAG_ICCPROFILE);
+ ICC_Profile icc_prof = ICC_Profile.getInstance(fd.getAsBytes());
+ if (samplePerPixel == icc_prof.getNumComponents())
+ img.tagICC(icc_prof);
+ }
+ catch (Exception e) {
+ //empty
+ }
+ }
+ if (dir.isTagPresent(TIFFConstants.TIFFTAG_COLORMAP)) {
+ TIFFField fd = dir.getField(TIFFConstants.TIFFTAG_COLORMAP);
+ char rgb[] = fd.getAsChars();
+ byte palette[] = new byte[rgb.length];
+ int gColor = rgb.length / 3;
+ int bColor = gColor * 2;
+ for (int k = 0; k < gColor; ++k) {
+ palette[k * 3] = (byte)(rgb[k] >>> 8);
+ palette[k * 3 + 1] = (byte)(rgb[k + gColor] >>> 8);
+ palette[k * 3 + 2] = (byte)(rgb[k + bColor] >>> 8);
+ }
+ PdfArray indexed = new PdfArray();
+ indexed.add(PdfName.INDEXED);
+ indexed.add(PdfName.DEVICERGB);
+ indexed.add(new PdfNumber(gColor - 1));
+ indexed.add(new PdfString(palette));
+ PdfDictionary additional = new PdfDictionary();
+ additional.put(PdfName.COLORSPACE, indexed);
+ img.setAdditional(additional);
+ }
+ img.setOriginalType(Image.ORIGINAL_TIFF);
+ }
+ if (photometric == TIFFConstants.PHOTOMETRIC_MINISWHITE)
+ img.setInverted(true);
+ if (rotation != 0)
+ img.setInitialRotation(rotation);
+ return img;
+ }
+ catch (Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ static long[] getArrayLongShort(TIFFDirectory dir, int tag) {
+ TIFFField field = dir.getField(tag);
+ if (field == null)
+ return null;
+ long offset[];
+ if (field.getType() == TIFFField.TIFF_LONG)
+ offset = field.getAsLongs();
+ else { // must be short
+ char temp[] = field.getAsChars();
+ offset = new long[temp.length];
+ for (int k = 0; k < temp.length; ++k)
+ offset[k] = temp[k];
+ }
+ return offset;
+ }
+
+ // Uncompress packbits compressed image data.
+ public static void decodePackbits(byte data[], byte[] dst) {
+ int srcCount = 0, dstCount = 0;
+ byte repeat, b;
+
+ while (dstCount < dst.length) {
+ b = data[srcCount++];
+ if (b >= 0 && b <= 127) {
+ // literal run packet
+ for (int i=0; i<(b + 1); i++) {
+ dst[dstCount++] = data[srcCount++];
+ }
+
+ } else if (b <= -1 && b >= -127) {
+ // 2 byte encoded run packet
+ repeat = data[srcCount++];
+ for (int i=0; i<(-b + 1); i++) {
+ dst[dstCount++] = repeat;
+ }
+ } else {
+ // no-op packet. Do nothing
+ srcCount++;
+ }
+ }
+ }
+
+ public static void inflate(byte[] deflated, byte[] inflated) {
+ Inflater inflater = new Inflater();
+ inflater.setInput(deflated);
+ try {
+ inflater.inflate(inflated);
+ }
+ catch(DataFormatException dfe) {
+ throw new ExceptionConverter(dfe);
+ }
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/JavaCharStream.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/JavaCharStream.java
new file mode 100644
index 0000000..da52458
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/JavaCharStream.java
@@ -0,0 +1,547 @@
+/* Generated By:JavaCC: Do not edit this line. JavaCharStream.java Version 2.1 */
+package com.lowagie.text.pdf.codec.postscript;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to
+ * contain only ASCII characters (with java-like unicode escape processing).
+ */
+
+public final class JavaCharStream
+{
+ public static final boolean staticFlag = false;
+ static final int hexval(char c) throws java.io.IOException {
+ switch(c)
+ {
+ case '0' :
+ return 0;
+ case '1' :
+ return 1;
+ case '2' :
+ return 2;
+ case '3' :
+ return 3;
+ case '4' :
+ return 4;
+ case '5' :
+ return 5;
+ case '6' :
+ return 6;
+ case '7' :
+ return 7;
+ case '8' :
+ return 8;
+ case '9' :
+ return 9;
+
+ case 'a' :
+ case 'A' :
+ return 10;
+ case 'b' :
+ case 'B' :
+ return 11;
+ case 'c' :
+ case 'C' :
+ return 12;
+ case 'd' :
+ case 'D' :
+ return 13;
+ case 'e' :
+ case 'E' :
+ return 14;
+ case 'f' :
+ case 'F' :
+ return 15;
+ }
+
+ throw new java.io.IOException(); // Should never come here
+ }
+
+ public int bufpos = -1;
+ int bufsize;
+ int available;
+ int tokenBegin;
+ private int bufline[];
+ private int bufcolumn[];
+
+ private int column = 0;
+ private int line = 1;
+
+ private boolean prevCharIsCR = false;
+ private boolean prevCharIsLF = false;
+
+ private java.io.Reader inputStream;
+
+ private char[] nextCharBuf;
+ private char[] buffer;
+ private int maxNextCharInd = 0;
+ private int nextCharInd = -1;
+ private int inBuf = 0;
+
+ private final void ExpandBuff(boolean wrapAround)
+ {
+ char[] newbuffer = new char[bufsize + 2048];
+ int newbufline[] = new int[bufsize + 2048];
+ int newbufcolumn[] = new int[bufsize + 2048];
+
+ try
+ {
+ if (wrapAround)
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ System.arraycopy(buffer, 0, newbuffer,
+ bufsize - tokenBegin, bufpos);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+ bufcolumn = newbufcolumn;
+
+ bufpos += (bufsize - tokenBegin);
+ }
+ else
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ bufcolumn = newbufcolumn;
+
+ bufpos -= tokenBegin;
+ }
+ }
+ catch (Throwable t)
+ {
+ throw new Error(t.getMessage());
+ }
+
+ available = (bufsize += 2048);
+ tokenBegin = 0;
+ }
+
+ private final void FillBuff() throws java.io.IOException
+ {
+ int i;
+ if (maxNextCharInd == 4096)
+ maxNextCharInd = nextCharInd = 0;
+
+ try {
+ if ((i = inputStream.read(nextCharBuf, maxNextCharInd,
+ 4096 - maxNextCharInd)) == -1)
+ {
+ inputStream.close();
+ throw new java.io.IOException();
+ }
+ else
+ maxNextCharInd += i;
+ return;
+ }
+ catch(java.io.IOException e) {
+ if (bufpos != 0)
+ {
+ --bufpos;
+ backup(0);
+ }
+ else
+ {
+ bufline[bufpos] = line;
+ bufcolumn[bufpos] = column;
+ }
+ throw e;
+ }
+ }
+
+ private final char ReadByte() throws java.io.IOException
+ {
+ if (++nextCharInd >= maxNextCharInd)
+ FillBuff();
+
+ return nextCharBuf[nextCharInd];
+ }
+
+ public final char BeginToken() throws java.io.IOException
+ {
+ if (inBuf > 0)
+ {
+ --inBuf;
+
+ if (++bufpos == bufsize)
+ bufpos = 0;
+
+ tokenBegin = bufpos;
+ return buffer[bufpos];
+ }
+
+ tokenBegin = 0;
+ bufpos = -1;
+
+ return readChar();
+ }
+
+ private final void AdjustBuffSize()
+ {
+ if (available == bufsize)
+ {
+ if (tokenBegin > 2048)
+ {
+ bufpos = 0;
+ available = tokenBegin;
+ }
+ else
+ ExpandBuff(false);
+ }
+ else if (available > tokenBegin)
+ available = bufsize;
+ else if ((tokenBegin - available) < 2048)
+ ExpandBuff(true);
+ else
+ available = tokenBegin;
+ }
+
+ private final void UpdateLineColumn(char c)
+ {
+ column++;
+
+ if (prevCharIsLF)
+ {
+ prevCharIsLF = false;
+ line += (column = 1);
+ }
+ else if (prevCharIsCR)
+ {
+ prevCharIsCR = false;
+ if (c == '\n')
+ {
+ prevCharIsLF = true;
+ }
+ else
+ line += (column = 1);
+ }
+
+ switch (c)
+ {
+ case '\r' :
+ prevCharIsCR = true;
+ break;
+ case '\n' :
+ prevCharIsLF = true;
+ break;
+ case '\t' :
+ column--;
+ column += (8 - (column & 07));
+ break;
+ default :
+ break;
+ }
+
+ bufline[bufpos] = line;
+ bufcolumn[bufpos] = column;
+ }
+
+ public final char readChar() throws java.io.IOException
+ {
+ if (inBuf > 0)
+ {
+ --inBuf;
+
+ if (++bufpos == bufsize)
+ bufpos = 0;
+
+ return buffer[bufpos];
+ }
+
+ char c;
+
+ if (++bufpos == available)
+ AdjustBuffSize();
+
+ if ((buffer[bufpos] = c = ReadByte()) == '\\')
+ {
+ UpdateLineColumn(c);
+
+ int backSlashCnt = 1;
+
+ for (;;) // Read all the backslashes
+ {
+ if (++bufpos == available)
+ AdjustBuffSize();
+
+ try
+ {
+ if ((buffer[bufpos] = c = ReadByte()) != '\\')
+ {
+ UpdateLineColumn(c);
+ // found a non-backslash char.
+ if ((c == 'u') && ((backSlashCnt & 1) == 1))
+ {
+ if (--bufpos < 0)
+ bufpos = bufsize - 1;
+
+ break;
+ }
+
+ backup(backSlashCnt);
+ return '\\';
+ }
+ }
+ catch(java.io.IOException e)
+ {
+ if (backSlashCnt > 1)
+ backup(backSlashCnt);
+
+ return '\\';
+ }
+
+ UpdateLineColumn(c);
+ backSlashCnt++;
+ }
+
+ // Here, we have seen an odd number of backslash's followed by a 'u'
+ try
+ {
+ while ((c = ReadByte()) == 'u')
+ ++column;
+
+ buffer[bufpos] = c = (char)(hexval(c) << 12 |
+ hexval(ReadByte()) << 8 |
+ hexval(ReadByte()) << 4 |
+ hexval(ReadByte()));
+
+ column += 4;
+ }
+ catch(java.io.IOException e)
+ {
+ throw new Error("Invalid escape character at line " + line +
+ " column " + column + ".");
+ }
+
+ if (backSlashCnt == 1)
+ return c;
+ else
+ {
+ backup(backSlashCnt - 1);
+ return '\\';
+ }
+ }
+ else
+ {
+ UpdateLineColumn(c);
+ return (c);
+ }
+ }
+
+ /**
+ * @deprecated
+ * @see #getEndColumn
+ */
+
+ public final int getColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ /**
+ * @deprecated
+ * @see #getEndLine
+ */
+
+ public final int getLine() {
+ return bufline[bufpos];
+ }
+
+ public final int getEndColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ public final int getEndLine() {
+ return bufline[bufpos];
+ }
+
+ public final int getBeginColumn() {
+ return bufcolumn[tokenBegin];
+ }
+
+ public final int getBeginLine() {
+ return bufline[tokenBegin];
+ }
+
+ public final void backup(int amount) {
+
+ inBuf += amount;
+ if ((bufpos -= amount) < 0)
+ bufpos += bufsize;
+ }
+
+ public JavaCharStream(java.io.Reader dstream,
+ int startline, int startcolumn, int buffersize)
+ {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ nextCharBuf = new char[4096];
+ }
+
+ public JavaCharStream(java.io.Reader dstream,
+ int startline, int startcolumn)
+ {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ public JavaCharStream(java.io.Reader dstream)
+ {
+ this(dstream, 1, 1, 4096);
+ }
+ public void ReInit(java.io.Reader dstream,
+ int startline, int startcolumn, int buffersize)
+ {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ if (buffer == null || buffersize != buffer.length)
+ {
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ nextCharBuf = new char[4096];
+ }
+ prevCharIsLF = prevCharIsCR = false;
+ tokenBegin = inBuf = maxNextCharInd = 0;
+ nextCharInd = bufpos = -1;
+ }
+
+ public void ReInit(java.io.Reader dstream,
+ int startline, int startcolumn)
+ {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+
+ public void ReInit(java.io.Reader dstream)
+ {
+ ReInit(dstream, 1, 1, 4096);
+ }
+ public JavaCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ this(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096);
+ }
+
+ public JavaCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn)
+ {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ public JavaCharStream(java.io.InputStream dstream)
+ {
+ this(dstream, 1, 1, 4096);
+ }
+
+ public void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096);
+ }
+ public void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn)
+ {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+ public void ReInit(java.io.InputStream dstream)
+ {
+ ReInit(dstream, 1, 1, 4096);
+ }
+
+ public final String GetImage()
+ {
+ if (bufpos >= tokenBegin)
+ return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+ else
+ return new String(buffer, tokenBegin, bufsize - tokenBegin) +
+ new String(buffer, 0, bufpos + 1);
+ }
+
+ public final char[] GetSuffix(int len)
+ {
+ char[] ret = new char[len];
+
+ if ((bufpos + 1) >= len)
+ System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+ else
+ {
+ System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0,
+ len - bufpos - 1);
+ System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+ }
+
+ return ret;
+ }
+
+ public void Done()
+ {
+ nextCharBuf = null;
+ buffer = null;
+ bufline = null;
+ bufcolumn = null;
+ }
+
+ /**
+ * Method to adjust line and column numbers for the start of a token.
+ */
+ public void adjustBeginLineColumn(int newLine, int newCol)
+ {
+ int start = tokenBegin;
+ int len;
+
+ if (bufpos >= tokenBegin)
+ {
+ len = bufpos - tokenBegin + inBuf + 1;
+ }
+ else
+ {
+ len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+ }
+
+ int i = 0, j = 0, k = 0;
+ int nextColDiff = 0, columnDiff = 0;
+
+ while (i < len &&
+ bufline[j = start % bufsize] == bufline[k = ++start % bufsize])
+ {
+ bufline[j] = newLine;
+ nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+ bufcolumn[j] = newCol + columnDiff;
+ columnDiff = nextColDiff;
+ i++;
+ }
+
+ if (i < len)
+ {
+ bufline[j] = newLine++;
+ bufcolumn[j] = newCol + columnDiff;
+
+ while (i++ < len)
+ {
+ if (bufline[j = start % bufsize] != bufline[++start % bufsize])
+ bufline[j] = newLine++;
+ else
+ bufline[j] = newLine;
+ }
+ }
+
+ line = bufline[j];
+ column = bufcolumn[j];
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/MetaDoPS.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/MetaDoPS.java
new file mode 100644
index 0000000..a160e6b
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/MetaDoPS.java
@@ -0,0 +1,98 @@
+/*
+ * $Id: MetaDoPS.java,v 1.4 2006/04/22 16:56:39 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 Paulo Soares
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.pdf.codec.postscript;
+
+import java.io.*;
+import java.awt.*;
+import com.lowagie.text.*;
+import com.lowagie.text.pdf.*;
+
+public class MetaDoPS {
+
+ public PdfContentByte cb;
+ InputStream in;
+ int left;
+ int top;
+ int right;
+ int bottom;
+ int inch;
+
+ public MetaDoPS(InputStream in, PdfContentByte cb) {
+ this.cb = cb;
+ this.in = in;
+ }
+
+ public void readAll() throws IOException, DocumentException {
+
+ cb.saveState();
+ java.awt.Graphics2D g2 = cb.createGraphicsShapes(PageSize.A4.
+ width(), PageSize.A4.height());
+ try {
+ PAContext context = new PAContext( (Graphics2D) g2,
+ new Dimension( (int) PageSize.A4.width(),
+ (int) PageSize.A4.height()));
+ context.draw(in);
+// context.draw(new BufferedInputStream(in));
+ // ( (Graphics2D) backBuffer.getGraphics()).dispose();
+ in.close();
+ }
+ catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ catch (PainterException ex) {
+ ex.printStackTrace();
+ }
+ g2.dispose();
+ cb.restoreState();
+
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/PACommand.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/PACommand.java
new file mode 100644
index 0000000..91b2e18
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/PACommand.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 1998 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information"). You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
+ */
+
+package com.lowagie.text.pdf.codec.postscript;
+
+public interface PACommand {
+
+ public void execute(PAContext context) throws PainterException;
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/codec/postscript/PAContext.java b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAContext.java
new file mode 100644
index 0000000..863d82a
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/codec/postscript/PAContext.java
@@ -0,0 +1,2772 @@
+/*
+ * Copyright 1998 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information"). You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
+ */
+
+package com.lowagie.text.pdf.codec.postscript;
+
+import java.util.*;
+import java.io.*;
+import java.awt.*;
+import java.awt.geom.*;
+import com.lowagie.text.pdf.PdfGraphics2D;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfName;
+import com.lowagie.text.*;
+import com.lowagie.text.pdf.RandomAccessFileOrArray;
+
+public class PAContext {
+
+ public PAPencil pencil;
+ public Stack dictionaries;
+ public Stack operands;
+ public PAEngine engine;
+ PAParser poorscript = null;
+ protected Random randomNumberGenerator;
+ InputStream is=null;
+
+ protected Object lastUnknownIdentifier;
+ public static boolean IgnoreUnknownCommands = false;
+ public static boolean DebugExecution = false;
+
+ public PAContext(Component component) {
+ this(new PAPencil(component));
+ }
+
+ public PAContext(Graphics2D g, Dimension size) {
+ this(new PAPencil(g, size));
+ }
+
+ public PAContext(PAPencil pencil) {
+ super();
+ this.pencil = pencil;
+ this.dictionaries = new Stack();
+ this.operands = new Stack();
+ this.engine = new PAEngine(this);
+ HashMap systemDict = this.constructSystemDict();
+ this.dictionaries.push(systemDict);
+ HashMap globalDict = this.constructGlobalDict();
+ this.dictionaries.push(globalDict);
+ HashMap userDict = this.constructUserDict();
+ systemDict.put("userdict", userDict);
+ this.dictionaries.push(userDict);
+ this.randomNumberGenerator = new Random();
+ this.lastUnknownIdentifier = null;
+ }
+
+ /**
+ * draw
+ *
+ * @param inputStream InputStream
+ * @throws PainterException
+ */
+ public void draw(InputStream inputStream) throws PainterException {
+ try {
+ String filename="init.ps";
+// poorscript = new PAParser(new NamedInputStream(PAContext.class.getResourceAsStream(filename),filename));
+ InputStream inpstr=PAContext.class.getResourceAsStream(filename);
+ poorscript = new PAParser(inpstr);
+ poorscript.parse(this);
+ try {
+ inpstr.close();
+ }
+ catch (IOException ex) {
+ ex.printStackTrace();
+ }
+// poorscript.enable_tracing();
+// poorscript.token_source.setDebugStream(System.err);
+// byte[] b=null;
+// try {
+// b = RandomAccessFileOrArray.InputStreamToArray(inputStream);
+// }
+// catch (IOException ex) {
+// ex.printStackTrace();
+// }
+// ByteArrayInputStream bar=new ByteArrayInputStream(b);
+// is = bar;
+ poorscript.ReInit(inputStream);
+ poorscript.parse(this);
+ // pencil.graphics.dispose();
+ }
+ catch (ParseException e) {
+ e.printStackTrace();
+ throw new PainterException(e.toString());
+ }
+ }
+
+
+
+ public Object getLastUnknownIdentifier() {
+ return this.lastUnknownIdentifier;
+ }
+
+ public double[] popNumberOperands(int n) throws PainterException {
+ double[] result = new double[n];
+ Object objectValue;
+ double doubleValue;
+
+ for (int i = n - 1; i >= 0; i--) {
+ try {
+ objectValue = this.operands.pop();
+ }
+ catch (EmptyStackException e) {
+ throw new PainterException("Operand stack is empty poping " + n +
+ " number operands");
+ }
+ if (objectValue instanceof Number) {
+ doubleValue = ( (Number) objectValue).doubleValue();
+ }
+ else {
+ throw new PainterException("Number expected on operand stack poping " +
+ n + " number operands, found " +
+ objectValue.getClass().getName());
+ }
+ result[i] = doubleValue;
+ }
+ return result;
+ }
+
+ public Object[] popOperands(int n) throws PainterException {
+ Object[] result = new Object[n];
+ Object objectValue;
+
+ for (int i = n - 1; i >= 0; i--) {
+ try {
+ objectValue = this.operands.pop();
+ }
+ catch (EmptyStackException e) {
+ throw new PainterException("Operand stack is empty poping " + n +
+ " operands");
+ }
+ result[i] = objectValue;
+ }
+ return result;
+ }
+
+ public Object peekOperand() throws PainterException {
+ Object objectValue;
+
+ try {
+ objectValue = this.operands.peek();
+ }
+ catch (EmptyStackException e) {
+ throw new PainterException("Operand stack is empty peeking operand");
+ }
+ return objectValue;
+ }
+
+ public Object findIdentifier(Object identifier) {
+ Object result = null;
+ int i, n;
+
+ n = this.dictionaries.size();
+ i = n - 1;
+ while (i >= 0 && result == null) {
+ HashMap dictionary = (HashMap)this.dictionaries.elementAt(i);
+ result = dictionary.get(identifier);
+ i--;
+ }
+ if (result == null) {
+ this.lastUnknownIdentifier = identifier;
+ }
+ return result;
+ }
+
+ public Object findDictionary(Object identifier) {
+ Object result = null;
+ HashMap dictionary = null;
+ int i, n;
+
+ n = this.dictionaries.size();
+ i = n - 1;
+ while (i >= 0 && result == null) {
+ dictionary = (HashMap)this.dictionaries.elementAt(i);
+ result = dictionary.get(identifier);
+ i--;
+ }
+ if (result == null) {
+ return result;
+ }
+ else {
+ return dictionary;
+ }
+ }
+
+ public void collectArray() throws PainterException {
+ ArrayList result;
+ Object objectValue;
+ int i, n;
+ boolean found = false;
+
+ n = this.operands.size();
+ for (i = n - 1; i >= 0; i--) {
+ objectValue = this.operands.elementAt(i);
+ if (objectValue instanceof PAToken &&
+ ( (PAToken) objectValue).type == PAToken.START_ARRAY) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new PainterException("No array was started");
+ }
+ result = new ArrayList(n - i - 1);
+ for (int j = 0; j < n - i - 1; j++) {
+ result.add(null);
+ }
+ for (int j = n - 1; j > i; j--) {
+ try {
+ objectValue = this.operands.pop();
+ }
+ catch (EmptyStackException e) {
+ throw new PainterException(
+ "Operand stack is empty collecting array elements");
+ }
+ result.set(j - i - 1, objectValue);
+ }
+ try {
+ this.operands.pop(); // the start array mark itself
+ }
+ catch (EmptyStackException e) {
+ throw new PainterException(
+ "Operand stack is empty removing begin array mark");
+ }
+ this.operands.push(result);
+ }
+
+ public void collectDict() throws PainterException {
+ HashMap result; // = new HashMap();
+ Object objectValue;
+ int i, n;
+ boolean found = false;
+
+ n = this.operands.size();
+ for (i = n - 1; i >= 0; i--) {
+ objectValue = this.operands.elementAt(i);
+ if (objectValue instanceof PAToken &&
+ ( (PAToken) objectValue).type == PAToken.START_DICT) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new PainterException("No dict was started");
+ }
+// result = new ArrayList(n - i - 1);
+ result = new HashMap();
+// for (int j = 0; j < n - i - 1; j++) {
+// result.add(null);
+// }
+ for (int j = n - 1; j > i; j -= 2) {
+ Object targetValue;
+ try {
+ targetValue = this.operands.pop();
+ objectValue = this.operands.pop();
+ }
+ catch (EmptyStackException e) {
+ throw new PainterException(
+ "Operand stack is empty collecting hashmap elements");
+ }
+ result.put(objectValue, targetValue);
+ }
+ try {
+ this.operands.pop(); // the start array mark itself
+ }
+ catch (EmptyStackException e) {
+ throw new PainterException(
+ "Operand stack is empty removing begin array mark");
+ }
+ this.operands.push(result);
+ }
+
+ protected HashMap constructGlobalDict() {
+ HashMap globalDict = new HashMap();
+ return globalDict;
+ }
+
+ protected HashMap constructUserDict() {
+ HashMap userDict = new HashMap();
+
+ return userDict;
+ }
+
+ public static void main(String[] args) {
+ javax.swing.JFrame jf = new javax.swing.JFrame();
+ jf.setVisible(true);
+ jf.setDefaultCloseOperation(jf.DISPOSE_ON_CLOSE);
+ PAContext pac = new PAContext(new PAPencil(jf));
+ HashMap hm = (HashMap) pac.findDictionary("systemdict");
+ Iterator it = new TreeSet(hm.keySet()).iterator();
+ while (it.hasNext()) {
+
+ String obname = it.next().toString();
+ Object ob = hm.get(obname);
+ String typname = ob.getClass().getName();
+ System.out.println(obname + ":" + typname);
+ }
+ System.exit(0);
+ }
+
+ protected HashMap constructSystemDict() {
+ HashMap systemDict = new HashMap();
+
+ // newpath
+ systemDict.put("newpath", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.pencil.newpath();
+ }
+ });
+
+ // moveto
+ systemDict.put("moveto", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ data = context.popNumberOperands(2);
+ context.pencil.moveto(data[0], data[1]);
+ }
+ });
+
+ // rmoveto
+ systemDict.put("rmoveto", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(2);
+ context.pencil.rmoveto(data[0], data[1]);
+ }
+ });
+
+ // lineto
+ systemDict.put("lineto", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(2);
+ context.pencil.lineto(data[0], data[1]);
+ }
+ });
+
+ // rlineto
+ systemDict.put("rlineto", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(2);
+ context.pencil.rlineto(data[0], data[1]);
+ }
+ });
+
+ // arc
+ systemDict.put("arc", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(5);
+ context.pencil.arc(data[0], data[1], data[2], data[3], data[4]);
+ }
+ });
+
+ // arcn
+ systemDict.put("arcn", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(5);
+ context.pencil.arcn(data[0], data[1], data[2], data[3], data[4]);
+ }
+ });
+
+ // curveto
+ systemDict.put("curveto", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(6);
+ context.pencil.curveto(data[0], data[1], data[2], data[3], data[4],
+ data[5]);
+ }
+ });
+
+ // rcurveto
+ systemDict.put("rcurveto", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(6);
+ context.pencil.rcurveto(data[0], data[1], data[2], data[3], data[4],
+ data[5]);
+ }
+ });
+
+ // closepath
+ systemDict.put("closepath", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.pencil.closepath();
+ }
+ });
+
+ // gsave
+ systemDict.put("gsave", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.pencil.gsave();
+ }
+ });
+
+ // grestore
+ systemDict.put("grestore", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.pencil.grestore();
+ }
+ });
+
+ // translate
+ systemDict.put("translate", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ if (context.peekOperand() instanceof Number) {
+ double data[];
+ AffineTransform at = new AffineTransform();
+ AffineTransform ctm = context.pencil.graphics.getTransform();
+
+ data = context.popNumberOperands(2);
+ at.translate(data[0], data[1]);
+ ctm.concatenate(at);
+ context.pencil.graphics.setTransform(ctm);
+ }
+ else {
+ Object data[];
+
+ data = context.popOperands(3);
+ if (! (data[0] instanceof Number)) {
+ throw new PainterException("translate: wrong arguments");
+ }
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("translate: wrong arguments");
+ }
+ if (! (data[2] instanceof ArrayList)) {
+ throw new PainterException("translate: wrong arguments");
+ }
+
+ ArrayList array = (ArrayList) data[2];
+
+ if (! (array.size() == 6)) {
+ throw new PainterException("translate: wrong arguments");
+ }
+
+ AffineTransform at = new AffineTransform();
+
+ at.translate( ( (Number) data[0]).doubleValue(),
+ ( (Number) data[1]).doubleValue());
+
+ double[] entries = new double[6];
+
+ at.getMatrix(entries);
+
+ for (int i = 0; i < 6; i++) {
+ array.set(i, new Double(entries[i]));
+ }
+ context.operands.push(array);
+ }
+ }
+ });
+
+ // rotate
+ systemDict.put("rotate", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ if (context.peekOperand() instanceof Number) {
+ double data[];
+ AffineTransform at = new AffineTransform();
+ AffineTransform ctm = context.pencil.graphics.getTransform();
+
+ data = context.popNumberOperands(1);
+ at.rotate(data[0] * Math.PI / 180.0d);
+ ctm.concatenate(at);
+ context.pencil.graphics.setTransform(ctm);
+ }
+ else {
+ Object data[];
+ AffineTransform at = new AffineTransform();
+
+ data = context.popOperands(2);
+ if (! (data[0] instanceof Number)) {
+ throw new PainterException("rotate: wrong arguments");
+ }
+ if (! (data[1] instanceof ArrayList)) {
+ throw new PainterException("rotate: wrong arguments");
+ }
+
+ ArrayList array = (ArrayList) data[1];
+
+ if (! (array.size() == 6)) {
+ throw new PainterException("rotate: wrong arguments");
+ }
+
+ at.rotate( ( (Number) data[0]).doubleValue());
+
+ double[] entries = new double[6];
+
+ at.getMatrix(entries);
+
+ for (int i = 0; i < 6; i++) {
+ array.set(i, new Double(entries[i]));
+ }
+ context.operands.push(array);
+ }
+ }
+ });
+
+ // scale
+ systemDict.put("scale", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ if (context.peekOperand() instanceof Number) {
+ double data[];
+ AffineTransform at = new AffineTransform();
+ AffineTransform ctm = context.pencil.graphics.getTransform();
+
+ data = context.popNumberOperands(2);
+ at.scale(data[0], data[1]);
+ ctm.concatenate(at);
+ context.pencil.graphics.setTransform(ctm);
+ }
+ else {
+ Object data[];
+
+ data = context.popOperands(3);
+ if (! (data[0] instanceof Number)) {
+ throw new PainterException("scale: wrong arguments");
+ }
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("scale: wrong arguments");
+ }
+ if (! (data[2] instanceof ArrayList)) {
+ throw new PainterException("scale: wrong arguments");
+ }
+
+ ArrayList array = (ArrayList) data[2];
+
+ double[] entries = new double[6];
+
+ if (! (array.size() == 6)) {
+ throw new PainterException("scale: wrong arguments");
+ }
+
+ entries[0] = ( (Number) data[0]).doubleValue();
+ entries[1] = 0.0d;
+ entries[2] = 0.0d;
+ entries[3] = ( (Number) data[1]).doubleValue();
+ entries[4] = 0.0d;
+ entries[5] = 0.0d;
+
+ for (int i = 0; i < 6; i++) {
+ array.set(i, new Double(entries[i]));
+ }
+ context.operands.push(array);
+ }
+ }
+ });
+ // currentmatrix
+ systemDict.put("currentmatrix", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(1);
+ if (! (data[0] instanceof ArrayList)) {
+ throw new PainterException("currentmatrix: wrong argument");
+ }
+ ArrayList array = (ArrayList) data[0];
+
+ double[] entries = new double[6];
+
+ if (! (array.size() == 6)) {
+ throw new PainterException("currentmatrix: wrong arguments");
+ }
+
+
+ AffineTransform ctm = context.pencil.graphics.getTransform();
+ ctm.getMatrix(entries);
+
+ for (int i = 0; i < 6; i++) {
+ array.set(i, new Double(entries[i]));
+ }
+ context.operands.push(array);
+ }
+ });
+
+ // setmatrix
+ systemDict.put("setmatrix", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(1);
+ if (! (data[0] instanceof ArrayList)) {
+ throw new PainterException("setmatrix: wrong argument");
+ }
+ ArrayList array = (ArrayList) data[0];
+
+ double[] entries = new double[6];
+
+ if (! (array.size() == 6)) {
+ throw new PainterException("setmatrix: wrong arguments");
+ }
+ entries[0] = ((Number)array.get(0)).doubleValue();
+ entries[1] = ((Number)array.get(1)).doubleValue();
+ entries[2] = ((Number)array.get(2)).doubleValue();
+ entries[3] = ((Number)array.get(3)).doubleValue();
+ entries[4] = ((Number)array.get(4)).doubleValue();
+ entries[5] = ((Number)array.get(5)).doubleValue();
+
+ AffineTransform at = new AffineTransform(entries);
+ context.pencil.graphics.setTransform(at);
+ }
+ });
+
+ // stroke
+ systemDict.put("stroke", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.pencil.stroke();
+ }
+ });
+
+ // fill
+ systemDict.put("fill", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.pencil.fill();
+ }
+ });
+
+ // eofill
+ systemDict.put("eofill", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.pencil.eofill();
+ }
+ });
+
+ // show
+ systemDict.put("show", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+
+ data = context.popOperands(1);
+ if (! (data[0] instanceof String)) {
+ throw new PainterException("show: wrong arguments");
+ }
+ context.pencil.show( (String) data[0]);
+ }
+ });
+
+ // stringwidth
+ systemDict.put("stringwidth", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ float[] result;
+ java.awt.Font font;
+
+ data = context.popOperands(1);
+ if (! (data[0] instanceof String)) {
+ throw new PainterException("stringwidth: wrong arguments");
+ }
+ font = context.pencil.graphics.getFont();
+ Rectangle2D rect = font.getStringBounds( (String) data[0],
+ context.pencil.graphics.
+ getFontRenderContext());
+ context.operands.push(new Float(rect.getWidth()));
+ context.operands.push(new Float(rect.getHeight()));
+ }
+ });
+
+ // showpage
+ systemDict.put("showpage", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.pencil.showpage();
+ }
+ });
+
+ // findfont
+ systemDict.put("findfont", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ data = context.popOperands(1);
+ if (! (data[0] instanceof PAToken)) {
+ throw new PainterException("findfont: wrong arguments");
+ }
+ patoken = (PAToken) data[0];
+ if (! (patoken.type == PAToken.KEY)) {
+ throw new PainterException("findfont: wrong arguments");
+ }
+ context.operands.push(context.pencil.findFont( (String) patoken.value));
+ }
+ });
+
+ // makefont
+ systemDict.put("makefont", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(2);
+ if (! (data[0] instanceof java.awt.Font)) {
+ throw new PainterException("makefont: wrong arguments");
+ }
+ if (! (data[1] instanceof ArrayList)) {
+ throw new PainterException("makefont: wrong arguments");
+ }
+ // @TODO implement!!!
+ context.operands.push(data[0]);
+ }
+ });
+
+ // scalefont
+ systemDict.put("scalefont", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(2);
+ if (! (data[0] instanceof java.awt.Font)) {
+ throw new PainterException("scalefont: wrong arguments");
+ }
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("scalefont: wrong arguments");
+ }
+ java.awt.Font fn=( (java.awt.Font) data[0]).deriveFont( ( (Number)
+ data[1]).
+ floatValue());
+ System.out.println("Fonthoehe:"+fn.getSize2D());
+ context.operands.push(fn );
+ }
+ });
+
+ // setfont
+ systemDict.put("setfont", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(1);
+ if (! (data[0] instanceof java.awt.Font)) {
+ throw new PainterException("setfont: wrong arguments");
+ }
+ java.awt.Font fn=(java.awt.Font)data[0];
+ System.out.println("Fonthoehe:"+fn.getSize2D());
+ /**
+ * @todo two times the same?
+ */
+ context.pencil.graphics.setFont( fn);
+ context.pencil.state.font=fn;
+ }
+ });
+
+ // def
+ systemDict.put("def", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ data = context.popOperands(2);
+ if (! (data[0] instanceof PAToken)) {
+ throw new PainterException("def: wrong arguments");
+ }
+ patoken = (PAToken) data[0];
+ if (! (patoken.type == PAToken.KEY)) {
+ throw new PainterException("def: wrong arguments");
+ }
+ try {
+ ( (HashMap) context.dictionaries.peek()).put(patoken.value, data[1]);
+ }
+ catch (EmptyStackException e) {
+ throw new PainterException(e.toString());
+ }
+ }
+ });
+
+ // bind
+ systemDict.put("bind", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ data = context.popOperands(1);
+ if (! (data[0] instanceof PAToken)) {
+ throw new PainterException("bind: wrong arguments, not PAToken");
+ }
+ patoken = (PAToken) data[0];
+ if (! (patoken.type == PAToken.PROCEDURE)) {
+ throw new PainterException("bind: wrong arguments, not Procedure " +
+ patoken.value);
+ }
+ context.engine.bindProcedure(patoken);
+ context.operands.push(patoken);
+ }
+ });
+
+ // mul
+ systemDict.put("mul", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(2);
+ context.operands.push(new Double(data[0] * data[1]));
+ }
+ });
+
+ // div
+ systemDict.put("div", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(2);
+ context.operands.push(new Double(data[0] / data[1]));
+ }
+ });
+
+ // mod
+ systemDict.put("mod", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(2);
+ int a, b, m;
+ a = (int) data[0];
+ b = (int) data[1];
+ m = a % b;
+ context.operands.push(new Integer(m));
+ }
+ });
+
+ // add
+ systemDict.put("add", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(2);
+ context.operands.push(new Double(data[0] + data[1]));
+ }
+ });
+
+ // neg
+ systemDict.put("neg", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ data = context.popNumberOperands(1);
+ context.operands.push(new Double( -data[0]));
+ }
+ });
+ // ceiling
+ systemDict.put("ceiling", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ data = context.popNumberOperands(1);
+ context.operands.push(new Double(Math.ceil(data[0])));
+ }
+ });
+ // sub
+ systemDict.put("sub", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ data = context.popNumberOperands(2);
+ context.operands.push(new Double(data[0] - data[1]));
+ }
+ });
+
+ // atan
+ systemDict.put("atan", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(2);
+ context.operands.push(new Double(Math.atan2(data[0], data[1])));
+ }
+ });
+
+ // sin
+ systemDict.put("sin", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(1);
+ context.operands.push(new Double(Math.sin(data[0] * Math.PI / 180.0)));
+ }
+ });
+
+ // cos
+ systemDict.put("cos", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(1);
+ context.operands.push(new Double(Math.cos(data[0] * Math.PI / 180.0)));
+ }
+ });
+
+ // sqrt
+ systemDict.put("sqrt", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ data = context.popNumberOperands(1);
+ context.operands.push(new Double(Math.sqrt(data[0])));
+ }
+ });
+ // ln
+ systemDict.put("log", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ data = context.popNumberOperands(1);
+ context.operands.push(new Double(Math.log(data[0])));
+ }
+ });
+ // exp
+ systemDict.put("exp", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ data = context.popNumberOperands(2);
+ context.operands.push(new Double(Math.pow(data[0], data[1])));
+ }
+ });
+
+ // exch
+ systemDict.put("exch", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+
+ data = context.popOperands(2);
+ context.operands.push(data[1]);
+ context.operands.push(data[0]);
+ }
+ });
+
+ // dup
+ systemDict.put("dup", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+
+ data = context.popOperands(1);
+ context.operands.push(data[0]);
+ context.operands.push(data[0]);
+ }
+ });
+
+ // roll
+ systemDict.put("roll", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ Object rollData[];
+
+ data = context.popOperands(2);
+ if (! (data[0] instanceof Number)) {
+ throw new PainterException("roll: wrong arguments");
+ }
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("roll: wrong arguments");
+ }
+ int numberOfElements, numberOfPositions, i;
+
+ numberOfElements = ( (Number) data[0]).intValue();
+ numberOfPositions = ( (Number) data[1]).intValue();
+
+ if (numberOfPositions == 0 || numberOfElements <= 0) {
+ return;
+ }
+
+ rollData = context.popOperands(numberOfElements);
+
+ if (numberOfPositions < 0) {
+ numberOfPositions = -numberOfPositions;
+ numberOfPositions = numberOfPositions % numberOfElements;
+
+ // downward roll
+ for (i = numberOfPositions; i < numberOfElements; i++) {
+ context.operands.push(rollData[i]);
+ }
+ for (i = 0; i < numberOfPositions; i++) {
+ context.operands.push(rollData[i]);
+ }
+ }
+ else {
+ numberOfPositions = numberOfPositions % numberOfElements;
+
+ // upward roll
+ for (i = numberOfElements - numberOfPositions; i < numberOfElements;
+ i++) {
+ context.operands.push(rollData[i]);
+ }
+ for (i = 0; i < numberOfElements - numberOfPositions; i++) {
+ context.operands.push(rollData[i]);
+ }
+ }
+ }
+ });
+
+ // pop
+ systemDict.put("pop", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.popOperands(1);
+ }
+ });
+
+ // index
+ systemDict.put("index", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(1);
+ if (! (data[0] instanceof Number)) {
+ throw new PainterException("index: wrong arguments");
+ }
+ int index = ( (Number) data[0]).intValue();
+ try {
+ context.operands.push(context.operands.elementAt(index));
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ throw new PainterException(e.toString());
+ }
+ }
+ });
+
+ // mark
+ systemDict.put("mark", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.operands.push(new PAToken(null, PAToken.MARK));
+ }
+ });
+
+ // cvx
+ systemDict.put("cvx", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data;
+ data = context.operands.pop();
+ ArrayList ar = (ArrayList) data;
+ Stack stack = new Stack();
+ for (int i = ar.size() - 1; i >= 0; i--) {
+ stack.add(ar.get(i));
+ }
+ PAToken patoken = new PAToken(stack, PAToken.PROCEDURE);
+// patoken.type=PAToken.PROCEDURE;
+ context.operands.push(patoken);
+ }
+ });
+ // cleartomark
+ systemDict.put("cleartomark", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data;
+ boolean finished = false;
+
+ while (!finished) {
+ try {
+ data = context.operands.pop();
+ if (data instanceof PAToken) {
+ if ( ( (PAToken) data).type == PAToken.MARK) {
+ finished = true;
+ }
+ }
+ }
+ catch (EmptyStackException e) {
+ throw new PainterException(e.toString());
+ }
+ }
+ }
+ });
+
+ // copy
+ systemDict.put("copy", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(2);
+
+ // decide if it's a simple copy or a composite object copy
+ if ( (data[0] instanceof PAToken) && (data[1] instanceof PAToken)) {
+ // composite object copy
+ if ( ( (PAToken) data[0]).type == ( (PAToken) data[1]).type) {
+ // our tokens are immutable so a copy is easy
+ context.operands.push(data[0]);
+ context.operands.push(data[0]);
+ }
+ else {
+ throw new PainterException(
+ "copy operation failed because composite objects on stack are not of same type");
+ }
+ }
+ else {
+ // restore first arg, we're not interested in it in this simple case
+ context.operands.push(data[0]);
+
+ if (data[1] instanceof Number) {
+ int index = ( (Number) data[1]).intValue();
+ int i, n;
+ n = context.operands.size();
+ Object[] copyData = new Object[index];
+ for (i = n - index; i < n; i++) {
+ try {
+ copyData[i - n + index] = context.operands.elementAt(i);
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ throw new PainterException(e.toString());
+ }
+ }
+ for (i = 0; i < index; i++) {
+ context.operands.push(copyData[i]);
+ }
+ }
+ else {
+ throw new PainterException("I expect a number on stack, dude");
+ }
+ }
+ }
+ });
+
+ // setgray
+ systemDict.put("setgray", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(1);
+ context.pencil.graphics.setPaint(new Color( (float) data[0],
+ (float) data[0], (float) data[0]));
+ }
+ });
+
+ // setrgbcolor
+ systemDict.put("setrgbcolor", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(3);
+ float[] fv = new float[3];
+ fv[0] = (float) Math.max(Math.min(data[0], 1.0d), 0.0d);
+ fv[1] = (float) Math.max(Math.min(data[1], 1.0d), 0.0d);
+ fv[2] = (float) Math.max(Math.min(data[2], 1.0d), 0.0d);
+ context.pencil.graphics.setPaint(new Color(fv[0], fv[1], fv[2]));
+ }
+ });
+
+ // currentrgbcolor
+systemDict.put("currentrgbcolor", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Color cl=context.pencil.graphics.getColor();
+ float[] fv = cl.getRGBComponents(null);
+ context.operands.push(new Float(fv[0]));
+ context.operands.push(new Float(fv[1]));
+ context.operands.push(new Float(fv[2]));
+ }
+});
+
+
+ // PENDING(uweh): color stuff still shaky
+ // sethsbcolor
+ systemDict.put("sethsbcolor", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ data = context.popNumberOperands(3);
+ float[] fv = new float[3];
+ fv[0] = (float) Math.max(Math.min(data[0], 1.0d), 0.0d);
+ fv[1] = (float) Math.max(Math.min(data[1], 1.0d), 0.0d);
+ fv[2] = (float) Math.max(Math.min(data[2], 1.0d), 0.0d);
+ context.pencil.graphics.setPaint(new Color(fv[0], fv[1], fv[2]));
+ }
+ });
+
+ // PENDING(uweh): I have to convert these puppies myself to rgb ?
+ // setcmykcolor
+ systemDict.put("setcmykcolor", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ int rd, gr, bl;
+
+ data = context.popNumberOperands(4);
+ float[] fv = new float[4];
+ fv[0] = (float) data[0];
+ fv[1] = (float) data[1];
+ fv[2] = (float) data[2];
+ fv[3] = (float) data[3];
+ rd = (int) (255 * Math.max(0, 1 - fv[0] - fv[3]));
+ gr = (int) (255 * Math.max(0, 1 - fv[1] - fv[3]));
+ bl = (int) (255 * Math.max(0, 1 - fv[2] - fv[3]));
+ context.pencil.graphics.setPaint(new Color(rd, gr, bl));
+ }
+ });
+
+ // setlinewidth
+ systemDict.put("setlinewidth", new PACommand() {
+ private double minLineWidth(double w, AffineTransform at) {
+ double matrix[] = new double[4];
+ at.getMatrix(matrix);
+ double scale = matrix[0] * matrix[3] - matrix[1] * matrix[2];
+ double minlw = .25 / Math.sqrt(Math.abs(scale));
+ if (w < minlw) {
+ w = minlw;
+ }
+ return w;
+ }
+
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ BasicStroke newStroke;
+ Stroke oldStroke = context.pencil.graphics.getStroke();
+ data = context.popNumberOperands(1);
+ data[0] = this.minLineWidth(data[0],
+ context.pencil.graphics.getTransform());
+ if (oldStroke instanceof BasicStroke) {
+ newStroke = new BasicStroke( (float) data[0],
+ ( (BasicStroke) oldStroke).getEndCap(),
+ ( (BasicStroke) oldStroke).getLineJoin(),
+ ( (BasicStroke) oldStroke).getMiterLimit(),
+ ( (BasicStroke) oldStroke).getDashArray(),
+ ( (BasicStroke) oldStroke).getDashPhase());
+ }
+ else {
+ newStroke = new BasicStroke( (float) data[0], BasicStroke.CAP_ROUND,
+ BasicStroke.JOIN_ROUND);
+ }
+ /**
+ * @todo two times the same?
+ */
+ context.pencil.graphics.setStroke(newStroke);
+// context.pencil.state.stroke=newStroke;
+ }
+ });
+
+ // setlinecap
+ systemDict.put("setlinecap", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ BasicStroke newStroke;
+ Stroke oldStroke = context.pencil.graphics.getStroke();
+ data = context.popNumberOperands(1);
+ if (oldStroke instanceof BasicStroke) {
+ newStroke = new BasicStroke( ( (BasicStroke) oldStroke).getLineWidth(),
+ (int) data[0],
+ ( (BasicStroke) oldStroke).getLineJoin(),
+ ( (BasicStroke) oldStroke).getMiterLimit(),
+ ( (BasicStroke) oldStroke).getDashArray(),
+ ( (BasicStroke) oldStroke).getDashPhase());
+ }
+ else {
+ newStroke = new BasicStroke(1.0f, (int) data[0],
+ BasicStroke.JOIN_ROUND);
+ }
+ context.pencil.graphics.setStroke(newStroke);
+ }
+ });
+
+ // setmiterlimit
+ systemDict.put("setmiterlimit", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ BasicStroke newStroke;
+ Stroke oldStroke = context.pencil.graphics.getStroke();
+ data = context.popNumberOperands(1);
+ if (oldStroke instanceof BasicStroke) {
+ newStroke = new BasicStroke( ( (BasicStroke) oldStroke).getLineWidth(),
+ ( (BasicStroke) oldStroke).getEndCap(),
+ ( (BasicStroke) oldStroke).getLineJoin(),
+ (float) data[0],
+ ( (BasicStroke) oldStroke).getDashArray(),
+ ( (BasicStroke) oldStroke).getDashPhase());
+ }
+ else {
+ newStroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND,
+ BasicStroke.JOIN_ROUND, (float) data[0]);
+ }
+ context.pencil.graphics.setStroke(newStroke);
+ }
+ });
+
+ // setdash
+ systemDict.put("setdash", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ BasicStroke newStroke;
+ Stroke oldStroke = context.pencil.graphics.getStroke();
+ data = context.popOperands(2);
+ if (! (data[0] instanceof ArrayList)) {
+ throw new PainterException("setdash: wrong arguments");
+ }
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("setdash: wrong arguments");
+ }
+
+ ArrayList list = (ArrayList) data[0];
+
+ if (list.size() == 0) {
+ return;
+ }
+ float[] dashpattern = new float[list.size()];
+ for (int i = 0; i < dashpattern.length; i++) {
+ dashpattern[i] = ( (Number) list.get(i)).floatValue();
+ }
+ float dashoffset = ( (Number) data[1]).floatValue();
+ if (oldStroke instanceof BasicStroke) {
+ newStroke = new BasicStroke( ( (BasicStroke) oldStroke).getLineWidth(),
+ ( (BasicStroke) oldStroke).getEndCap(),
+ ( (BasicStroke) oldStroke).getLineJoin(),
+ ( (BasicStroke) oldStroke).getMiterLimit(),
+ dashpattern,
+ dashoffset);
+ }
+ else {
+ newStroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND,
+ BasicStroke.JOIN_ROUND, 1.0f, dashpattern,
+ dashoffset);
+ }
+ context.pencil.graphics.setStroke(newStroke);
+ }
+ });
+
+ // setlinejoin
+ systemDict.put("setlinejoin", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ BasicStroke newStroke;
+ Stroke oldStroke = context.pencil.graphics.getStroke();
+ data = context.popNumberOperands(1);
+ if (oldStroke instanceof BasicStroke) {
+ newStroke = new BasicStroke( ( (BasicStroke) oldStroke).getLineWidth(),
+ ( (BasicStroke) oldStroke).getEndCap(),
+ (int) data[0],
+ ( (BasicStroke) oldStroke).getMiterLimit(),
+ ( (BasicStroke) oldStroke).getDashArray(),
+ ( (BasicStroke) oldStroke).getDashPhase());
+ }
+ else {
+ newStroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND, (int) data[0]);
+ }
+ context.pencil.graphics.setStroke(newStroke);
+ }
+ });
+
+ // dumpstack
+ systemDict.put("dumpstack", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Enumeration enumx = context.operands.elements();
+ System.out.println("-------------Stack--------------");
+ while (enumx.hasMoreElements()) {
+ System.out.println(enumx.nextElement());
+ }
+ System.out.println("--------------------------------");
+ }
+ });
+
+ // for
+ systemDict.put("for", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+
+ data = context.popOperands(4);
+ if (! (data[3] instanceof PAToken)) {
+ throw new PainterException("for: wrong arguments");
+ }
+ if (! (data[0] instanceof Number)) {
+ throw new PainterException("for: wrong arguments");
+ }
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("for: wrong arguments");
+ }
+ if (! (data[2] instanceof Number)) {
+ throw new PainterException("for: wrong arguments");
+ }
+ patoken = (PAToken) data[3];
+ if (! (patoken.type == PAToken.PROCEDURE)) {
+ throw new PainterException("for: wrong arguments");
+ }
+ int i0, i1, i2;
+ i0 = ( (Number) data[0]).intValue();
+ i1 = ( (Number) data[1]).intValue();
+ i2 = ( (Number) data[2]).intValue();
+
+ if (i1 > 0) {
+ for (int i = i0; i <= i2; i += i1) {
+ context.operands.push(new Integer(i));
+ context.engine.process(patoken);
+ }
+ }
+ else {
+ for (int i = i0; i >= i2; i -= i1) {
+ context.operands.push(new Integer(i));
+ context.engine.process(patoken);
+ }
+ }
+ }
+ });
+
+ // repeat
+ systemDict.put("repeat", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ data = context.popOperands(2);
+ if (! (data[1] instanceof PAToken)) {
+ throw new PainterException("repeat: wrong arguments");
+ }
+ if (! (data[0] instanceof Number)) {
+ throw new PainterException("repeat: wrong arguments");
+ }
+ patoken = (PAToken) data[1];
+ if (! (patoken.type == PAToken.PROCEDURE)) {
+ throw new PainterException("repeat: wrong arguments");
+ }
+ int n = ( (Number) data[0]).intValue();
+ for (int i = 0; i < n; i++) {
+ context.engine.process(patoken);
+ }
+ }
+ });
+
+ // true
+ systemDict.put("true", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.operands.push(new Boolean(true));
+ }
+ });
+
+ // false
+ systemDict.put("false", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.operands.push(new Boolean(false));
+ }
+ });
+
+ // lt
+ systemDict.put("lt", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+
+ data = context.popOperands(2);
+ if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) {
+ throw new PainterException("lt: wrong arguments");
+ }
+ if (data[0] instanceof Number) {
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("lt: wrong arguments");
+ }
+ double d0, d1;
+ d0 = ( (Number) data[0]).doubleValue();
+ d1 = ( (Number) data[1]).doubleValue();
+ if (d0 < d1) {
+ context.operands.push(new Boolean(true));
+ }
+ else {
+ context.operands.push(new Boolean(false));
+ }
+ }
+ else {
+ if (! (data[1] instanceof String)) {
+ throw new PainterException("lt: wrong arguments");
+ }
+ String s0, s1;
+ s0 = (String) data[0];
+ s1 = (String) data[1];
+ if (s0.compareTo(s1) < 0) {
+ context.operands.push(new Boolean(true));
+ }
+ else {
+ context.operands.push(new Boolean(false));
+ }
+ }
+ }
+ });
+
+ // gt
+ systemDict.put("gt", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+
+ data = context.popOperands(2);
+ if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) {
+ throw new PainterException("gt: wrong arguments");
+ }
+ if (data[0] instanceof Number) {
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("gt: wrong arguments");
+ }
+ double d0, d1;
+ d0 = ( (Number) data[0]).doubleValue();
+ d1 = ( (Number) data[1]).doubleValue();
+ if (d0 > d1) {
+ context.operands.push(new Boolean(true));
+ }
+ else {
+ context.operands.push(new Boolean(false));
+ }
+ }
+ else {
+ if (! (data[1] instanceof String)) {
+ throw new PainterException("gt: wrong arguments");
+ }
+ String s0, s1;
+ s0 = (String) data[0];
+ s1 = (String) data[1];
+ if (s0.compareTo(s1) > 0) {
+ context.operands.push(new Boolean(true));
+ }
+ else {
+ context.operands.push(new Boolean(false));
+ }
+ }
+ }
+ });
+ // ge
+ systemDict.put("ge", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+
+ data = context.popOperands(2);
+ if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) {
+ throw new PainterException("ge: wrong arguments");
+ }
+ if (data[0] instanceof Number) {
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("ge: wrong arguments");
+ }
+ double d0, d1;
+ d0 = ( (Number) data[0]).doubleValue();
+ d1 = ( (Number) data[1]).doubleValue();
+ if (d0 >= d1) {
+ context.operands.push(new Boolean(true));
+ }
+ else {
+ context.operands.push(new Boolean(false));
+ }
+ }
+ else {
+ if (! (data[1] instanceof String)) {
+ throw new PainterException("ge: wrong arguments");
+ }
+ String s0, s1;
+ s0 = (String) data[0];
+ s1 = (String) data[1];
+ if (s0.compareTo(s1) >= 0) {
+ context.operands.push(new Boolean(true));
+ }
+ else {
+ context.operands.push(new Boolean(false));
+ }
+ }
+ }
+ });
+ // ne
+ systemDict.put("ne", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+
+ data = context.popOperands(2);
+ if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) {
+ throw new PainterException("ne: wrong arguments");
+ }
+ if (data[0] instanceof Number) {
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("ne: wrong arguments");
+ }
+ double d0, d1;
+ d0 = ( (Number) data[0]).doubleValue();
+ d1 = ( (Number) data[1]).doubleValue();
+ if (d0 != d1) {
+ context.operands.push(new Boolean(true));
+ }
+ else {
+ context.operands.push(new Boolean(false));
+ }
+ }
+ else {
+ if (! (data[1] instanceof String)) {
+ throw new PainterException("ne: wrong arguments");
+ }
+ String s0, s1;
+ s0 = (String) data[0];
+ s1 = (String) data[1];
+ if (s0.equals(s1)) {
+ context.operands.push(new Boolean(false));
+ }
+ else {
+ context.operands.push(new Boolean(true));
+ }
+ }
+ }
+ });
+
+ // eq
+ systemDict.put("eq", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+
+ data = context.popOperands(2);
+ if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) {
+ throw new PainterException("eq: wrong arguments");
+ }
+ if (data[0] instanceof Number) {
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("eq: wrong arguments");
+ }
+ double d0, d1;
+ d0 = ( (Number) data[0]).doubleValue();
+ d1 = ( (Number) data[1]).doubleValue();
+ if (d0 == d1) {
+ context.operands.push(new Boolean(true));
+ }
+ else {
+ context.operands.push(new Boolean(false));
+ }
+ }
+ else {
+ if (! (data[1] instanceof String)) {
+ throw new PainterException("eq: wrong arguments");
+ }
+ String s0, s1;
+ s0 = (String) data[0];
+ s1 = (String) data[1];
+ if (s0.compareTo(s1) == 0) {
+ context.operands.push(new Boolean(true));
+ }
+ else {
+ context.operands.push(new Boolean(false));
+ }
+ }
+ }
+ });
+
+ // if
+ systemDict.put("if", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ data = context.popOperands(2);
+ if (! (data[0] instanceof Boolean)) {
+ throw new PainterException("if: wrong arguments");
+ }
+ if (! (data[1] instanceof PAToken)) {
+ throw new PainterException("if: wrong arguments");
+ }
+ patoken = (PAToken) data[1];
+ if (! (patoken.type == PAToken.PROCEDURE)) {
+ throw new PainterException("if: wrong arguments");
+ }
+ if ( ( (Boolean) data[0]).booleanValue()) {
+ context.engine.process(patoken);
+ }
+ }
+ });
+
+ // ifelse
+ systemDict.put("ifelse", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken1, patoken2;
+ data = context.popOperands(3);
+ if (! (data[0] instanceof Boolean)) {
+ throw new PainterException("ifelse: wrong arguments");
+ }
+ if (! (data[1] instanceof PAToken)) {
+ throw new PainterException("ifelse: wrong arguments");
+ }
+ if (! (data[2] instanceof PAToken)) {
+ throw new PainterException("ifelse: wrong arguments");
+ }
+ patoken1 = (PAToken) data[1];
+ patoken2 = (PAToken) data[2];
+ if (! (patoken1.type == PAToken.PROCEDURE)) {
+ throw new PainterException("ifelse: wrong arguments");
+ }
+ if (! (patoken2.type == PAToken.PROCEDURE)) {
+ throw new PainterException("ifelse: wrong arguments");
+ }
+ if ( ( (Boolean) data[0]).booleanValue()) {
+ context.engine.process(patoken1);
+ }
+ else {
+ context.engine.process(patoken2);
+ }
+ }
+ });
+
+ // dict
+ systemDict.put("dict", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ data = context.popNumberOperands(1);
+ context.operands.push(new HashMap( (int) data[0]));
+ }
+ });
+
+ // put
+ systemDict.put("put", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ data = context.popOperands(3);
+ if ( (data[0] instanceof HashMap) && (data[1] instanceof PAToken)) {
+ patoken = (PAToken) data[1];
+ if (! (patoken.type == PAToken.KEY)) {
+ throw new PainterException("put: wrong arguments");
+ }
+ ( (HashMap) data[0]).put(patoken.value, data[2]);
+ }
+ else
+ if ( (data[0] instanceof ArrayList) && (data[1] instanceof Number)) {
+ ArrayList ar = (ArrayList) data[0];
+ Number nr = (Number) data[1];
+ ar.set(nr.intValue(), data[2]);
+ }
+ else
+ if ( (data[0] instanceof StringBuffer) && (data[1] instanceof Number) &&
+ (data[2] instanceof Number)) {
+ StringBuffer text = (StringBuffer) data[0];
+ Number nr = (Number) data[1];
+ Number ch = (Number) data[2];
+ text.setCharAt(nr.intValue(), (char) (ch.intValue()));
+ }
+ else {
+ throw new PainterException("put: wrong arguments");
+ }
+ }
+ });
+
+ // get
+ systemDict.put("get", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ data = context.popOperands(2);
+ if (! (data[0] instanceof HashMap) && ! (data[0] instanceof ArrayList)) {
+ throw new PainterException("get: wrong arguments");
+ }
+ if (data[0] instanceof HashMap) {
+ if (! (data[1] instanceof PAToken)) {
+ throw new PainterException("get: wrong arguments");
+ }
+ patoken = (PAToken) data[1];
+ if (! (patoken.type == PAToken.KEY)) {
+ throw new PainterException("get: wrong arguments");
+ }
+ context.operands.push( ( (HashMap) data[0]).get(patoken.value));
+ }
+ else if (data[0] instanceof ArrayList) {
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("get: wrong arguments");
+ }
+ context.operands.push( ( (ArrayList) data[0]).get( ( (Number) data[1]).
+ intValue()));
+ }
+ }
+ });
+ // getinterval
+ systemDict.put("getinterval", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ data = context.popOperands(3);
+ if (! (data[0] instanceof HashMap) && ! (data[0] instanceof ArrayList)) {
+ throw new PainterException("getinterval: wrong arguments");
+ }
+ if (data[0] instanceof HashMap) {
+ if (! (data[1] instanceof PAToken)) {
+ throw new PainterException("getinterval: wrong arguments");
+ }
+ patoken = (PAToken) data[1];
+ if (! (patoken.type == PAToken.KEY)) {
+ throw new PainterException("getinterval: wrong arguments");
+ }
+ if (! (data[2] instanceof Number)) {
+ throw new PainterException("getinterval: wrong arguments");
+ }
+ HashMap target = new HashMap();
+ context.operands.push( ( (HashMap) data[0]).get(patoken.value));
+ }
+ else if (data[0] instanceof ArrayList) {
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("getinterval: wrong arguments");
+ }
+ if (! (data[2] instanceof Number)) {
+ throw new PainterException("getinterval: wrong arguments");
+ }
+ ArrayList source = ( (ArrayList) data[0]);
+ int from = ( (Number) data[1]).intValue();
+ int to = from + ( (Number) data[2]).intValue();
+ ArrayList target = new ArrayList(source.subList(from, to));
+ context.operands.push(target);
+ }
+ }
+ });
+ // load
+ systemDict.put("load", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ data = context.popOperands(1);
+ if (! (data[0] instanceof PAToken)) {
+ throw new PainterException("load: wrong arguments");
+ }
+ patoken = (PAToken) data[0];
+ if (! (patoken.type == PAToken.KEY)) {
+ throw new PainterException("load: wrong arguments");
+ }
+ context.operands.push(context.findIdentifier(patoken.value));
+ }
+ });
+
+ // length
+ systemDict.put("length", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ int size = 0;
+ data = context.popOperands(1);
+ if (data[0] instanceof PAToken) {
+ patoken = (PAToken) data[0];
+ if (! (patoken.type == PAToken.KEY)) {
+ throw new PainterException("length: wrong arguments");
+ }
+ size = ( (String) patoken.value).length();
+ }
+ else if (data[0] instanceof HashMap) {
+ size = ( (HashMap) data[0]).size();
+ }
+ else if (data[0] instanceof ArrayList) {
+ size = ( (ArrayList) data[0]).size();
+ }
+ else if (data[0] instanceof String) {
+ size = ( (String) data[0]).length();
+ }
+ else {
+ throw new PainterException("length: wrong arguments");
+ }
+
+ context.operands.push(new Integer(size));
+ }
+ });
+
+ // begin
+ systemDict.put("begin", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(1);
+ if (! (data[0] instanceof HashMap)) {
+ throw new PainterException("begin: wrong arguments");
+ }
+ context.dictionaries.push(data[0]);
+ }
+ });
+
+ // end
+ systemDict.put("end", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ try {
+ context.dictionaries.pop();
+ }
+ catch (EmptyStackException e) {
+ throw new PainterException("Dictionary stack is empty");
+ }
+ }
+ });
+
+ // undef
+ systemDict.put("undef", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ data = context.popOperands(2);
+ if (! (data[0] instanceof HashMap)) {
+ throw new PainterException("undef: wrong arguments");
+ }
+ if (! (data[1] instanceof PAToken)) {
+ throw new PainterException("undef: wrong arguments");
+ }
+ patoken = (PAToken) data[1];
+ if (! (patoken.type == PAToken.KEY)) {
+ throw new PainterException("undef: wrong arguments");
+ }
+ // we don't do an actual undef because we don't care
+ }
+ });
+
+ // known
+ systemDict.put("known", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[], foundObject;
+ PAToken patoken;
+ data = context.popOperands(1);
+ if (! (data[0] instanceof PAToken)) {
+ throw new PainterException("known: wrong arguments");
+ }
+ patoken = (PAToken) data[0];
+ if (! (patoken.type == PAToken.KEY)) {
+ throw new PainterException("known: wrong arguments");
+ }
+ foundObject = context.findIdentifier(patoken.value);
+ if (foundObject != null) {
+ context.operands.push(new Boolean(true));
+ }
+ else {
+ context.operands.push(new Boolean(false));
+ }
+ }
+ });
+
+ // where
+ systemDict.put("where", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[], foundObject;
+ PAToken patoken;
+ data = context.popOperands(1);
+ if (! (data[0] instanceof PAToken)) {
+ throw new PainterException("where: wrong arguments");
+ }
+ patoken = (PAToken) data[0];
+ if (! (patoken.type == PAToken.KEY)) {
+ throw new PainterException("where: wrong arguments");
+ }
+ foundObject = context.findDictionary(patoken.value);
+ if (foundObject != null) {
+ context.operands.push(foundObject);
+ context.operands.push(new Boolean(true));
+ }
+ else {
+ context.operands.push(new Boolean(false));
+ }
+ }
+ });
+
+ // aload
+ systemDict.put("aload", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object[] data;
+ java.util.AbstractList list;
+ data = context.popOperands(1);
+ if (data[0] instanceof PAToken) {
+ data[0] = ( (PAToken) data[0]).value;
+ }
+ if (! (data[0] instanceof java.util.AbstractList)) {
+ throw new PainterException("aload: wrong arguments");
+ }
+
+ list = (java.util.AbstractList) data[0];
+ Iterator iterator = list.iterator();
+ while (iterator.hasNext()) {
+ context.operands.push(iterator.next());
+ }
+ context.operands.push(data[0]);
+ }
+ });
+
+ // forall
+ systemDict.put("forall", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ ArrayList list;
+ PAToken patoken;
+
+ data = context.popOperands(2);
+ if (! (data[0] instanceof ArrayList)) {
+ throw new PainterException("forall: wrong arguments");
+ }
+ if (! (data[1] instanceof PAToken)) {
+ throw new PainterException("forall: wrong arguments");
+ }
+
+ patoken = (PAToken) data[1];
+ if (! (patoken.type == PAToken.PROCEDURE)) {
+ throw new PainterException("forall: wrong arguments");
+ }
+
+ list = (ArrayList) data[0];
+ Iterator iterator = list.iterator();
+ while (iterator.hasNext()) {
+ context.operands.push(iterator.next());
+ context.engine.process(patoken);
+ }
+
+ }
+ });
+
+ // currentflat PENDING(uweh):placeholder for now
+ systemDict.put("currentflat", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics;
+ PdfContentByte cb = pdfg2d.getContent();
+ context.operands.push(new Double(1.0f));
+ }
+ });
+
+ // setflat PENDING(uweh):placeholder for now
+ systemDict.put("setflat", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double[] data;
+ data = context.popNumberOperands(1);
+ PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics;
+ PdfContentByte cb = pdfg2d.getContent();
+ cb.setFlatness( ( (float) data[0]));
+ }
+ });
+
+ // round
+ systemDict.put("round", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ data = context.popNumberOperands(1);
+ context.operands.push(new Long(Math.round(data[0])));
+ }
+ });
+
+ // abs
+ systemDict.put("abs", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ data = context.popNumberOperands(1);
+ context.operands.push(new Double(Math.abs(data[0])));
+ }
+ });
+
+ // transform
+ systemDict.put("transform", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ if (context.peekOperand() instanceof Number) {
+ double data[];
+ double[] transformedData = new double[2];
+ data = context.popNumberOperands(2);
+ AffineTransform at = context.pencil.graphics.getTransform();
+ at.transform(data, 0, transformedData, 0, 1);
+ context.operands.push(new Double(transformedData[0]));
+ context.operands.push(new Double(transformedData[1]));
+ }
+ else {
+ Object data[];
+
+ data = context.popOperands(3);
+ if (! (data[0] instanceof Number)) {
+ throw new PainterException("transform: wrong arguments");
+ }
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("transform: wrong arguments");
+ }
+ if (! (data[2] instanceof ArrayList)) {
+ throw new PainterException("transform: wrong arguments");
+ }
+
+ ArrayList array = (ArrayList) data[2];
+
+ double[] entries = new double[6];
+
+ if (! (array.size() == 6)) {
+ throw new PainterException("transform: wrong arguments");
+ }
+
+ for (int i = 0; i < 6; i++) {
+ entries[i] = ( (Number) array.get(i)).doubleValue();
+ }
+
+ AffineTransform at = new AffineTransform(entries);
+
+ double numberdata[] = new double[2];
+ numberdata[0] = ( (Number) data[0]).doubleValue();
+ numberdata[1] = ( (Number) data[1]).doubleValue();
+
+ double[] transformedData = new double[2];
+
+ at.transform(numberdata, 0, transformedData, 0, 1);
+ context.operands.push(new Double(transformedData[0]));
+ context.operands.push(new Double(transformedData[1]));
+ }
+ }
+ });
+
+ // itransform
+ systemDict.put("itransform", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ if (context.peekOperand() instanceof Number) {
+ double data[];
+ double[] transformedData = new double[2];
+ data = context.popNumberOperands(2);
+ AffineTransform at = context.pencil.graphics.getTransform();
+ try {
+ at.inverseTransform(data, 0, transformedData, 0, 1);
+ }
+ catch (NoninvertibleTransformException e) {
+ throw new PainterException(e.toString());
+ }
+ context.operands.push(new Double(transformedData[0]));
+ context.operands.push(new Double(transformedData[1]));
+ }
+ else {
+ Object data[];
+
+ data = context.popOperands(3);
+ if (! (data[0] instanceof Number)) {
+ throw new PainterException("itransform: wrong arguments");
+ }
+ if (! (data[1] instanceof Number)) {
+ throw new PainterException("itransform: wrong arguments");
+ }
+ if (! (data[2] instanceof ArrayList)) {
+ throw new PainterException("itransform: wrong arguments");
+ }
+
+ ArrayList array = (ArrayList) data[2];
+
+ double[] entries = new double[6];
+
+ if (! (array.size() == 6)) {
+ throw new PainterException("itransform: wrong arguments");
+ }
+
+ for (int i = 0; i < 6; i++) {
+ entries[i] = ( (Number) array.get(i)).doubleValue();
+ }
+
+ AffineTransform at = new AffineTransform(entries);
+
+ double numberdata[] = new double[2];
+ numberdata[0] = ( (Number) data[0]).doubleValue();
+ numberdata[1] = ( (Number) data[0]).doubleValue();
+
+ double[] transformedData = new double[2];
+
+ try {
+ at.inverseTransform(numberdata, 0, transformedData, 0, 1);
+ }
+ catch (NoninvertibleTransformException e) {
+ throw new PainterException(e.toString());
+ }
+ context.operands.push(new Double(transformedData[0]));
+ context.operands.push(new Double(transformedData[1]));
+ }
+ }
+ });
+
+ // currentpoint
+ // PENDING(uweh): what about CTM, same thing when you construct path
+ // this is different than ps, might not work in a few instances
+ systemDict.put("currentpoint", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Point2D currentPoint = context.pencil.state.path.getCurrentPoint();
+ context.operands.push(new Double(currentPoint.getX()));
+ context.operands.push(new Double(currentPoint.getY()));
+ }
+ });
+
+ // clippath
+ systemDict.put("clippath", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.pencil.clippath();
+ }
+ });
+
+ // matrix
+ systemDict.put("matrix", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ ArrayList identityMatrix = new ArrayList(6);
+
+ identityMatrix.add(new Double(1.0d));
+ identityMatrix.add(new Double(0.0d));
+ identityMatrix.add(new Double(0.0d));
+ identityMatrix.add(new Double(1.0d));
+ identityMatrix.add(new Double(0.0d));
+ identityMatrix.add(new Double(0.0d));
+ context.operands.push(identityMatrix);
+ }
+ });
+
+ // concatmatrix
+ systemDict.put("concatmatrix", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(3);
+ if (! (data[0] instanceof ArrayList)) {
+ throw new PainterException("concatmatrix: wrong arguments");
+ }
+ if (! (data[1] instanceof ArrayList)) {
+ throw new PainterException("concatmatrix: wrong arguments");
+ }
+ if (! (data[2] instanceof ArrayList)) {
+ throw new PainterException("concatmatrix: wrong arguments");
+ }
+ ArrayList arrayOne, arrayTwo, arrayThree;
+ AffineTransform atOne, atTwo;
+
+ arrayOne = (ArrayList) data[0];
+ arrayTwo = (ArrayList) data[1];
+ arrayThree = (ArrayList) data[2];
+
+ double[] entries = new double[6];
+
+ if (! (arrayOne.size() == 6)) {
+ throw new PainterException("concatmatrix: wrong arguments");
+ }
+ if (! (arrayTwo.size() == 6)) {
+ throw new PainterException("concatmatrix: wrong arguments");
+ }
+ if (! (arrayThree.size() == 6)) {
+ throw new PainterException("concatmatrix: wrong arguments");
+ }
+
+ for (int i = 0; i < 6; i++) {
+ entries[i] = ( (Number) arrayOne.get(i)).doubleValue();
+ }
+ atOne = new AffineTransform(entries);
+ for (int i = 0; i < 6; i++) {
+ entries[i] = ( (Number) arrayTwo.get(i)).doubleValue();
+ }
+ atTwo = new AffineTransform(entries);
+
+ atOne.concatenate(atTwo);
+
+ atOne.getMatrix(entries);
+ for (int i = 0; i < 6; i++) {
+ arrayThree.set(i, new Double(entries[i]));
+ }
+ context.operands.push(arrayThree);
+ }
+ });
+
+ // pathbbox
+ systemDict.put("pathbbox", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Rectangle2D pathBounds = context.pencil.state.path.getBounds2D();
+
+ context.operands.push(new Double(pathBounds.getMinX()));
+ context.operands.push(new Double(pathBounds.getMinY()));
+ context.operands.push(new Double(pathBounds.getMaxX()));
+ context.operands.push(new Double(pathBounds.getMaxY()));
+ }
+ });
+
+ // initmatrix
+ systemDict.put("initmatrix", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics;
+ PdfContentByte cb = pdfg2d.getContent();
+// cb.transform(Affine);
+ }
+ });
+ // initclip
+ systemDict.put("initclip", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics;
+ PdfContentByte cb = pdfg2d.getContent();
+ context.pencil.clippath();
+// pdfg2d.setClip(context.);
+// if(!PAContext.IgnoreUnknownCommands)
+// throw new UnsupportedOperationException("initclip");
+ }
+ });
+
+ // truncate
+ systemDict.put("truncate", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+ double truncated;
+
+ data = context.popNumberOperands(1);
+ if (data[0] < 0) {
+ truncated = Math.ceil(data[0]);
+ }
+ else {
+ truncated = Math.floor(data[0]);
+ }
+ context.operands.push(new Double(truncated));
+ }
+ });
+
+ // rand
+ systemDict.put("rand", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.operands.push(new Integer(Math.abs(randomNumberGenerator.
+ nextInt( (1 << 31) - 1))));
+ }
+ });
+
+ // srand
+ systemDict.put("srand", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ double data[];
+
+ data = context.popNumberOperands(1);
+ randomNumberGenerator = new Random(Math.round(data[0]));
+ }
+ });
+ // version
+ systemDict.put("version", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.operands.push("2016");
+ }
+ });
+ // cvi
+ systemDict.put("cvi", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+
+ data = context.popOperands(1);
+ if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) {
+ throw new PainterException("cvi: wrong arguments");
+ }
+ if (data[0] instanceof Number) {
+ int d;
+
+ d = ( (Number) data[0]).intValue();
+ context.operands.push(new Integer(d));
+ }
+ else {
+ String s;
+ s = (String) data[0];
+
+ context.operands.push(new Integer(s));
+ }
+ }
+ });
+ // cvr
+ systemDict.put("cvr", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+
+ data = context.popOperands(1);
+ if (! (data[0] instanceof Number) && ! (data[0] instanceof String)) {
+ throw new PainterException("cvr: wrong arguments");
+ }
+ if (data[0] instanceof Number) {
+ int d;
+
+ d = ( (Number) data[0]).intValue();
+ context.operands.push(new Double(d));
+ }
+ else {
+ String s;
+ s = (String) data[0];
+
+ context.operands.push(new Double(s));
+ }
+ }
+ });
+ // usertime
+ systemDict.put("usertime", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.operands.push(new Long(System.currentTimeMillis()));
+ }
+ });
+// save
+ systemDict.put("save", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics;
+ PdfContentByte cb = pdfg2d.getContent();
+ cb.saveState();
+ context.operands.push(new Long(0));
+ }
+ });
+// restore
+ systemDict.put("restore", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics;
+ PdfContentByte cb = pdfg2d.getContent();
+ cb.restoreState();
+ Object data[];
+ data = context.popOperands(1);
+ }
+ });
+// clear
+ systemDict.put("clear", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.operands.clear();
+ }
+ });
+ // readonly
+ systemDict.put("readonly", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ }
+ });
+
+// currentfile
+ systemDict.put("currentfile", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ final JavaCharStream jcs=context.poorscript.jj_input_stream;
+ InputStream ins=new InputStream(){
+ /**
+ * Reads the next byte of data from the input stream.
+ *
+ * @return the next byte of data, or -1
if the end of the stream is reached.
+ * @throws IOException if an I/O error occurs.
+ * @todo Implement this java.io.InputStream method
+ */
+ public int read() throws IOException {
+ return jcs.readChar();
+ }
+
+ };
+ context.operands.push(ins);
+ }
+ });
+ // flushfile
+ systemDict.put("flushfile", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(1);
+ if (! (data[0] instanceof InputStream)) {
+ throw new PainterException("flushfile: wrong arguments");
+ }
+
+ InputStream is = (InputStream) data[0];
+ try {
+ while (is.read() != -1) {
+ }
+ }
+ catch (IOException ex) {
+ }
+ }
+ });
+
+ // closefile
+ systemDict.put("closefile", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(1);
+ if (! (data[0] instanceof InputStream)) {
+ throw new PainterException("closefile: wrong arguments");
+ }
+
+ InputStream is = (InputStream) data[0];
+ try {
+ is.close();
+ }
+ catch (IOException ex) {
+ }
+ }
+ });
+
+ // string
+ systemDict.put("string", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(1);
+ if (! (data[0] instanceof Number)) {
+ throw new PainterException("string: wrong arguments");
+ }
+ int d;
+ d = ( (Number) data[0]).intValue();
+ StringBuffer sb = new StringBuffer(d);
+ sb.setLength(d);
+ context.operands.push(sb);
+ }
+ });
+ // null
+ systemDict.put("null", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.operands.push(null);
+ }
+ });
+ // currentscreen
+ systemDict.put("currentscreen", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ if (!PAContext.IgnoreUnknownCommands) {
+ throw new UnsupportedOperationException("currentscreen");
+ }
+ else {
+ context.operands.push(new Double(60));
+ context.operands.push(new Double(0));
+ context.operands.push(new Double(0));
+ }
+ }
+ });
+ // setscreen
+ systemDict.put("setscreen", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(3);
+
+// if (!PAContext.IgnoreUnknownCommands)
+// throw new UnsupportedOperationException("setscreen");
+// else {
+//
+// }
+ }
+ });
+
+ // flattenpath
+ systemDict.put("flattenpath", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+
+ }
+ });
+ // filter
+ systemDict.put("filter", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ String filtername;
+ filtername = (String) ( (PAToken) context.popOperands(1)[0]).value;
+ Object obj;
+ while (! ( (obj = context.peekOperand()) instanceof InputStream)) {
+ Object param = context.popOperands(1);
+ }
+
+ InputStream datasrc;
+ datasrc = (InputStream) (context.popOperands(1)[0]);
+
+ InputStream dis;
+ if (filtername.equals("ASCIIHexDecode")) {
+ // dis = new ASCIIHexInputStream(datasrc);
+ final InputStream is=datasrc;
+ dis=new InputStream(){
+
+ /**
+ * Reads the next byte of data from the input stream.
+ *
+ * @return the next byte of data, or -1
if the end of the stream is reached.
+ * @throws IOException if an I/O error occurs.
+ * @todo Implement this java.io.InputStream method
+ */
+ public int read() throws IOException {
+ int firstchar,secondchar;
+ for(;;){
+ firstchar=is.read();
+ if(firstchar==-1)return -1;
+ if(firstchar=='>')return -1;
+ if(firstchar=='\n')continue;
+ if(firstchar=='\r')continue;
+ break;
+ }
+ for(;;){
+ secondchar=is.read();
+ if(secondchar=='>')return -1;
+ if(secondchar==-1)return -1;
+ if(secondchar=='\n')continue;
+ if(secondchar=='\r')continue;
+ break;
+ }
+ int highbyte=0;
+ if(firstchar>=48&&firstchar<=57)highbyte=firstchar-48;
+ if(firstchar>=65&&firstchar<=70)highbyte=firstchar-55;
+ int lowbyte=0;
+ if(secondchar>=48&&secondchar<=57)lowbyte=secondchar-48;
+ if(secondchar>=65&&secondchar<=70)lowbyte=secondchar-55;
+
+ return(highbyte*16+lowbyte);
+ }
+ };
+ }
+// else
+// if (filtername.equals("DCTDecode")) {
+// dis = new DCTInputStream(datasrc);
+// }
+ else {
+ dis = datasrc;
+ }
+
+ context.operands.push(dis);
+ }
+ });
+ // clip
+ systemDict.put("clip", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.pencil.clip();
+ }
+ });
+ // setcolorspace
+ systemDict.put("setcolorspace", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics;
+ PdfContentByte cb = pdfg2d.getContent();
+ Object data[];
+ data = context.popOperands(1);
+ if (data[0] instanceof PAToken) {
+ String colorspace = ( (String) ( (PAToken) data[0]).value);
+ cb.setDefaultColorspace(PdfName.COLORSPACE, PdfName.DEVICERGB);
+
+ }
+ }
+ });
+ // image
+ systemDict.put("image", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ PdfGraphics2D pdfg2d = (PdfGraphics2D) context.pencil.graphics;
+ PdfContentByte cb = pdfg2d.getContent();
+ Object data[];
+ data = context.popOperands(1);
+ if (data[0] instanceof Number) {
+ /**
+ * Level1 image
+ */
+ int width = ( (Number) data[0]).intValue();
+ data = context.popOperands(4);
+ int height = ( (Number) data[0]).intValue();
+ int bits = ( (Number) data[1]).intValue();
+
+ }else if (data[0] instanceof PAToken) {
+ PAToken proc = (PAToken) data[0];
+
+ data = context.popOperands(4);
+ int width = ( (Number) data[0]).intValue();
+ int height = ( (Number) data[1]).intValue();
+ int bitspercomponent = ( (Number) data[2]).intValue();
+ ArrayList ar = (ArrayList) data[3];
+ System.out.println("I " + width + "*" + height + " " +
+ bitspercomponent + " " + ar);
+
+// context.engine.process(proc);
+ }
+ else if (data[0] instanceof HashMap){
+ HashMap hsm = (HashMap) data[0];
+ Iterator it = hsm.keySet().iterator();
+ int width = 0, height = 0, bitspercomponent = 0;
+ int imagetype = 0;
+ InputStream datasrc = null;
+ Object decode = null;
+ Object imagematrix = null;
+ while (it.hasNext()) {
+ PAToken token = (PAToken) it.next();
+ if (token.value.toString().equals("ImageType")) {
+ imagetype = ( (Number) hsm.get(token)).intValue();
+ }
+ if (token.value.toString().equals("DataSource")) {
+ datasrc = (InputStream) hsm.get(token);
+ }
+
+ if (token.value.toString().equals("BitsPerComponent")) {
+ bitspercomponent = ( (Number) hsm.get(token)).intValue();
+ }
+ if (token.value.toString().equals("Width")) {
+ width = ( (Number) hsm.get(token)).intValue();
+ }
+ if (token.value.toString().equals("Height")) {
+ height = ( (Number) hsm.get(token)).intValue();
+ }
+ if (token.value.toString().equals("Decode")) {
+ decode = ( (Object) hsm.get(token));
+ }
+ if (token.value.toString().equals("ImageMatrix")) {
+ imagematrix = ( (Object) hsm.get(token));
+ }
+ }
+
+ try {
+ byte[] barr = {};
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ int aByte;
+ while ( (aByte = datasrc.read()) >= 0) {
+ bout.write(aByte);
+// System.out.print((char)aByte);
+ }
+ System.out.println("I " + width + "*" + height + " " +
+ bitspercomponent + " " + imagetype + " " +
+ decode + " " + imagematrix + " " + datasrc);
+ barr = bout.toByteArray();
+// com.lowagie.text.Image img = new ImgRaw(width, height, 1,
+// bitspercomponent, barr);
+ com.lowagie.text.Image img = new Jpeg(barr);
+ try {
+ cb.addImage(img,width,0,0,height,0,0);
+ }
+ catch (DocumentException ex1) {
+ ex1.printStackTrace();
+ }
+ }
+ catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ catch (BadElementException ex) {
+ ex.printStackTrace();
+ }
+
+ }
+ }
+ });
+
+ // imagemask
+ systemDict.put("imagemask", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(5);
+// if (data[0] instanceof PAToken) {
+// PAToken token = (PAToken) data[0];
+// context.engine.process(token);
+// }
+ }
+ });
+
+
+ // exec
+ systemDict.put("exec", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ data = context.popOperands(1);
+ if (data[0] instanceof PAToken) {
+ PAToken token = (PAToken) data[0];
+ context.engine.process(token);
+ }
+ }
+ });
+ // currentdict
+ systemDict.put("currentdict", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.operands.push(context.dictionaries.peek());
+ }
+ });
+
+// cleardictstack
+ systemDict.put("cleardictstack", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ context.dictionaries.clear();
+ HashMap systemDict = context.constructSystemDict();
+ context.dictionaries.push(systemDict);
+ HashMap globalDict = context.constructGlobalDict();
+ context.dictionaries.push(globalDict);
+ HashMap userDict = context.constructUserDict();
+ systemDict.put("userdict", userDict);
+ systemDict.put("globaldict", globalDict);
+ context.dictionaries.push(userDict);
+ }
+ });
+
+ // charpath
+ systemDict.put("charpath", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+
+ data = context.popOperands(2);
+ if (! (data[0] instanceof String)) {
+ throw new PainterException("charpath: wrong arguments");
+ }
+ if (! (data[1] instanceof Boolean)) {
+ throw new PainterException("charpath: wrong arguments");
+ }
+
+ context.pencil.charpath( (String) data[0],
+ ( (Boolean) data[1]).booleanValue());
+ }
+ });
+
+ // PENDING(uweh): we only support procedure right now and always push false on the stack
+ // stopped
+ systemDict.put("stopped", new PACommand() {
+ public void execute(PAContext context) throws PainterException {
+ Object data[];
+ PAToken patoken;
+ data = context.popOperands(1);
+ if (! (data[0] instanceof PAToken)) {
+ throw new PainterException("stopped: wrong arguments");
+ }
+
+ patoken = (PAToken) data[0];
+ if (! (patoken.type == PAToken.PROCEDURE)) {
+ throw new PainterException("stopped: wrong arguments");
+ }
+ context.engine.process(patoken);
+ context.operands.push(new Boolean(false));
+ }
+ });
+ systemDict.put("systemdict", systemDict);
+ return systemDict;
+ }
+
+ /**
+ * PdfWriter
for this document
+ * @param document
+ * the document
+ */
+ public void onOpenDocument(PdfWriter writer, Document document) {
+ PdfPageEvent event;
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ event = (PdfPageEvent)i.next();
+ event.onOpenDocument(writer, document);
+ }
+ }
+
+ /**
+ * Called when a page is initialized.
+ * onEndPage
to avoid infinite loops.
+ *
+ * @param writer
+ * the PdfWriter
for this document
+ * @param document
+ * the document
+ */
+ public void onStartPage(PdfWriter writer, Document document) {
+ PdfPageEvent event;
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ event = (PdfPageEvent)i.next();
+ event.onStartPage(writer, document);
+ }
+ }
+
+ /**
+ * Called when a page is finished, just before being written to the
+ * document.
+ *
+ * @param writer
+ * the PdfWriter
for this document
+ * @param document
+ * the document
+ */
+ public void onEndPage(PdfWriter writer, Document document) {
+ PdfPageEvent event;
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ event = (PdfPageEvent)i.next();
+ event.onEndPage(writer, document);
+ }
+ }
+
+ /**
+ * Called when the document is closed.
+ * PdfWriter
for this document
+ * @param document
+ * the document
+ */
+ public void onCloseDocument(PdfWriter writer, Document document) {
+ PdfPageEvent event;
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ event = (PdfPageEvent)i.next();
+ event.onCloseDocument(writer, document);
+ }
+ }
+
+ /**
+ * Called when a Paragraph is written.
+ * paragraphPosition
will hold the height at which the
+ * paragraph will be written to. This is useful to insert bookmarks with
+ * more control.
+ *
+ * @param writer
+ * the PdfWriter
for this document
+ * @param document
+ * the document
+ * @param paragraphPosition
+ * the position the paragraph will be written to
+ */
+ public void onParagraph(PdfWriter writer, Document document,
+ float paragraphPosition) {
+ PdfPageEvent event;
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ event = (PdfPageEvent)i.next();
+ event.onParagraph(writer, document, paragraphPosition);
+ }
+ }
+
+ /**
+ * Called when a Paragraph is written.
+ * paragraphPosition
will hold the height of the end of the
+ * paragraph.
+ *
+ * @param writer
+ * the PdfWriter
for this document
+ * @param document
+ * the document
+ * @param paragraphPosition
+ * the position of the end of the paragraph
+ */
+ public void onParagraphEnd(PdfWriter writer, Document document,
+ float paragraphPosition) {
+ PdfPageEvent event;
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ event = (PdfPageEvent)i.next();
+ event.onParagraphEnd(writer, document, paragraphPosition);
+ }
+ }
+
+ /**
+ * Called when a Chapter is written.
+ * position
will hold the height at which the chapter will be
+ * written to.
+ *
+ * @param writer
+ * the PdfWriter
for this document
+ * @param document
+ * the document
+ * @param paragraphPosition
+ * the position the chapter will be written to
+ * @param title
+ * the title of the Chapter
+ */
+ public void onChapter(PdfWriter writer, Document document,
+ float paragraphPosition, Paragraph title) {
+ PdfPageEvent event;
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ event = (PdfPageEvent)i.next();
+ event.onChapter(writer, document, paragraphPosition, title);
+ }
+ }
+
+ /**
+ * Called when the end of a Chapter is reached.
+ * position
will hold the height of the end of the chapter.
+ *
+ * @param writer
+ * the PdfWriter
for this document
+ * @param document
+ * the document
+ * @param position
+ * the position of the end of the chapter.
+ */
+ public void onChapterEnd(PdfWriter writer, Document document, float position) {
+ PdfPageEvent event;
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ event = (PdfPageEvent)i.next();
+ event.onChapterEnd(writer, document, position);
+ }
+ }
+
+ /**
+ * Called when a Section is written.
+ * position
will hold the height at which the section will be
+ * written to.
+ *
+ * @param writer
+ * the PdfWriter
for this document
+ * @param document
+ * the document
+ * @param paragraphPosition
+ * the position the section will be written to
+ * @param depth
+ * the number depth of the Section
+ * @param title
+ * the title of the section
+ */
+ public void onSection(PdfWriter writer, Document document,
+ float paragraphPosition, int depth, Paragraph title) {
+ PdfPageEvent event;
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ event = (PdfPageEvent)i.next();
+ event.onSection(writer, document, paragraphPosition, depth, title);
+ }
+ }
+
+ /**
+ * Called when the end of a Section is reached.
+ * position
will hold the height of the section end.
+ *
+ * @param writer
+ * the PdfWriter
for this document
+ * @param document
+ * the document
+ * @param position
+ * the position of the end of the section
+ */
+ public void onSectionEnd(PdfWriter writer, Document document, float position) {
+ PdfPageEvent event;
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ event = (PdfPageEvent)i.next();
+ event.onSectionEnd(writer, document, position);
+ }
+ }
+
+ /**
+ * Called when a Chunk
with a generic tag is written.
+ * Chunk
location to generate
+ * bookmarks, for example.
+ *
+ * @param writer
+ * the PdfWriter
for this document
+ * @param document
+ * the document
+ * @param rect
+ * the Rectangle
containing the Chunk
+ *
+ * @param text
+ * the text of the tag
+ */
+ public void onGenericTag(PdfWriter writer, Document document,
+ Rectangle rect, String text) {
+ PdfPageEvent event;
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ event = (PdfPageEvent)i.next();
+ event.onGenericTag(writer, document, rect, text);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/pdf/fonts/FontsResourceAnchor.java b/src/main/java/com/lowagie/text/pdf/fonts/FontsResourceAnchor.java
new file mode 100644
index 0000000..57cb502
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/fonts/FontsResourceAnchor.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2004 by Paulo Soares.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.pdf.fonts;
+
+/**
+ * A class to facilitate the loading of resources
+ *
+ * @author Paulo Soares (psoares@consiste.pt)
+ */
+public class FontsResourceAnchor {
+
+ /**
+ * Creates a FontsResourceAnchor
+ */
+ public FontsResourceAnchor() {
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/pdf/hyphenation/ByteVector.java b/src/main/java/com/lowagie/text/pdf/hyphenation/ByteVector.java
new file mode 100644
index 0000000..f3a83ec
--- /dev/null
+++ b/src/main/java/com/lowagie/text/pdf/hyphenation/ByteVector.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.lowagie.text.pdf.hyphenation;
+
+import java.io.Serializable;
+
+/**
+ * This class implements a simple byte vector with access to the
+ * underlying array.
+ *
+ * @author Carlos Villegas
+ * for(i=0; i
to which this Ternary Search Tree.
+ *
+ *
+ * RtfWriter
.
+ * RtfWriter
to which this RtfCell
belongs. */
+ private RtfWriter writer = null;
+ /** The RtfTable
to which this RtfCell
belongs. */
+ private RtfTable mainTable = null;
+
+ /** Cell width */
+ private int cellWidth = 0;
+ /** Cell right border position */
+ private int cellRight = 0;
+ /** Cell
containing the actual data */
+ private Cell store = null;
+ /** Is this an empty cell */
+ private boolean emptyCell = true;
+ /** Type of merging to do */
+ private int mergeType = 0;
+ /** cell padding, because the table only renders the left and right cell padding
+ * and not the top and bottom one
+ */
+ private int cellpadding = 0;
+
+ /**
+ * Create a new RtfCell
.
+ *
+ * @param writer The RtfWriter
that this RtfCell
belongs to
+ * @param mainTable The RtfTable
that created the
+ * RtfRow
that created the RtfCell
:-)
+ */
+ public RtfCell(RtfWriter writer, RtfTable mainTable) {
+ super();
+ this.writer = writer;
+ this.mainTable = mainTable;
+ }
+
+ /**
+ * Import a Cell
.
+ * Cell
containing the data for this
+ * RtfCell
+ * @param cellLeft The position of the left border
+ * @param cellWidth The default width of a cell
+ * @param x The column index of this RtfCell
+ * @param y The row index of this RtfCell
+ * @param cellpadding the cellpadding
+ * @return the position of the right side of the cell
+ */
+ public int importCell(Cell cell, int cellLeft, int cellWidth, int x, int y, int cellpadding) {
+ this.cellpadding = cellpadding;
+
+ // set this value in any case
+ this.cellWidth = cellWidth;
+ if (cell == null) {
+ cellRight = cellLeft + cellWidth;
+ return cellRight;
+ }
+ if (cell.cellWidth() != null && !cell.cellWidth().equals("")) {
+
+ this.cellWidth = (int) (Integer.parseInt(cell.cellWidth()) * RtfWriter.TWIPSFACTOR);
+ }
+ cellRight = cellLeft + this.cellWidth;
+ store = cell;
+ emptyCell = false;
+ if (cell.colspan() > 1) {
+ if (cell.rowspan() > 1) {
+ mergeType = MERGE_BOTH_FIRST;
+ for (int i = y; i < y + cell.rowspan(); i++) {
+ if (i > y) mainTable.setMerge(x, i, MERGE_VERT_PREV, this);
+ for (int j = x + 1; j < x + cell.colspan(); j++) {
+ mainTable.setMerge(j, i, MERGE_BOTH_PREV, this);
+ }
+ }
+ } else {
+ mergeType = MERGE_HORIZ_FIRST;
+ for (int i = x + 1; i < x + cell.colspan(); i++) {
+ mainTable.setMerge(i, y, MERGE_HORIZ_PREV, this);
+ }
+ }
+ } else if (cell.rowspan() > 1) {
+ mergeType = MERGE_VERT_FIRST;
+ for (int i = y + 1; i < y + cell.rowspan(); i++) {
+ mainTable.setMerge(x, i, MERGE_VERT_PREV, this);
+ }
+ }
+ return cellRight;
+ }
+
+ /**
+ * Write the properties of the RtfCell
.
+ *
+ * @param os The OutputStream
to which to write the properties
+ * of the RtfCell
to.
+ * @return true if writing the cell settings succeeded
+ * @throws DocumentException
+ */
+ public boolean writeCellSettings(ByteArrayOutputStream os) throws DocumentException {
+ try {
+ float lWidth, tWidth, rWidth, bWidth;
+ byte[] lStyle, tStyle, rStyle, bStyle;
+
+ if (store instanceof RtfTableCell) {
+ RtfTableCell c = (RtfTableCell) store;
+ lWidth = c.leftBorderWidth();
+ tWidth = c.topBorderWidth();
+ rWidth = c.rightBorderWidth();
+ bWidth = c.bottomBorderWidth();
+ lStyle = RtfTableCell.getStyleControlWord(c.leftBorderStyle());
+ tStyle = RtfTableCell.getStyleControlWord(c.topBorderStyle());
+ rStyle = RtfTableCell.getStyleControlWord(c.rightBorderStyle());
+ bStyle = RtfTableCell.getStyleControlWord(c.bottomBorderStyle());
+ } else {
+ lWidth = tWidth = rWidth = bWidth = store.borderWidth();
+ lStyle = tStyle = rStyle = bStyle = RtfRow.tableBorder;
+ }
+
+ if (mergeType == MERGE_HORIZ_PREV || mergeType == MERGE_BOTH_PREV) {
+ return true;
+ }
+ switch (mergeType) {
+ case MERGE_VERT_FIRST:
+ os.write(RtfWriter.escape);
+ os.write(cellVMergeFirst);
+ break;
+ case MERGE_BOTH_FIRST:
+ os.write(RtfWriter.escape);
+ os.write(cellVMergeFirst);
+ break;
+ case MERGE_HORIZ_PREV:
+ os.write(RtfWriter.escape);
+ os.write(cellMergePrev);
+ break;
+ case MERGE_VERT_PREV:
+ os.write(RtfWriter.escape);
+ os.write(cellVMergePrev);
+ break;
+ case MERGE_BOTH_PREV:
+ os.write(RtfWriter.escape);
+ os.write(cellMergeFirst);
+ break;
+ }
+ switch (store.verticalAlignment()) {
+ case Element.ALIGN_BOTTOM:
+ os.write(RtfWriter.escape);
+ os.write(cellVerticalAlignBottom);
+ break;
+ case Element.ALIGN_CENTER:
+ case Element.ALIGN_MIDDLE:
+ os.write(RtfWriter.escape);
+ os.write(cellVerticalAlignCenter);
+ break;
+ case Element.ALIGN_TOP:
+ os.write(RtfWriter.escape);
+ os.write(cellVerticalAlignTop);
+ break;
+ }
+
+ if (((store.border() & Rectangle.LEFT) == Rectangle.LEFT) &&
+ (lWidth > 0)) {
+ os.write(RtfWriter.escape);
+ os.write(cellBorderLeft);
+ os.write(RtfWriter.escape);
+ os.write(lStyle);
+ os.write(RtfWriter.escape);
+ os.write(RtfRow.tableBorderWidth);
+ writeInt(os, (int) (lWidth * RtfWriter.TWIPSFACTOR));
+ os.write(RtfWriter.escape);
+ os.write(RtfRow.tableBorderColor);
+ if (store.borderColor() == null)
+ writeInt(os, writer.addColor(new
+ Color(0, 0, 0)));
+ else
+ writeInt(os, writer.addColor(store.borderColor()));
+ os.write((byte) '\n');
+ }
+ if (((store.border() & Rectangle.TOP) == Rectangle.TOP) && (tWidth > 0)) {
+ os.write(RtfWriter.escape);
+ os.write(cellBorderTop);
+ os.write(RtfWriter.escape);
+ os.write(tStyle);
+ os.write(RtfWriter.escape);
+ os.write(RtfRow.tableBorderWidth);
+ writeInt(os, (int) (tWidth * RtfWriter.TWIPSFACTOR));
+ os.write(RtfWriter.escape);
+ os.write(RtfRow.tableBorderColor);
+ if (store.borderColor() == null)
+ writeInt(os, writer.addColor(new
+ Color(0, 0, 0)));
+ else
+ writeInt(os, writer.addColor(store.borderColor()));
+ os.write((byte) '\n');
+ }
+ if (((store.border() & Rectangle.BOTTOM) == Rectangle.BOTTOM) &&
+ (bWidth > 0)) {
+ os.write(RtfWriter.escape);
+ os.write(cellBorderBottom);
+ os.write(RtfWriter.escape);
+ os.write(bStyle);
+ os.write(RtfWriter.escape);
+ os.write(RtfRow.tableBorderWidth);
+ writeInt(os, (int) (bWidth * RtfWriter.TWIPSFACTOR));
+ os.write(RtfWriter.escape);
+ os.write(RtfRow.tableBorderColor);
+ if (store.borderColor() == null)
+ writeInt(os, writer.addColor(new
+ Color(0, 0, 0)));
+ else
+ writeInt(os, writer.addColor(store.borderColor()));
+ os.write((byte) '\n');
+ }
+ if (((store.border() & Rectangle.RIGHT) == Rectangle.RIGHT) &&
+ (rWidth > 0)) {
+ os.write(RtfWriter.escape);
+ os.write(cellBorderRight);
+ os.write(RtfWriter.escape);
+ os.write(rStyle);
+ os.write(RtfWriter.escape);
+ os.write(RtfRow.tableBorderWidth);
+ writeInt(os, (int) (rWidth * RtfWriter.TWIPSFACTOR));
+ os.write(RtfWriter.escape);
+ os.write(RtfRow.tableBorderColor);
+ if (store.borderColor() == null)
+ writeInt(os, writer.addColor(new
+ Color(0, 0, 0)));
+ else
+ writeInt(os, writer.addColor(store.borderColor()));
+ os.write((byte) '\n');
+ }
+ os.write(RtfWriter.escape);
+ os.write(cellBackgroundColor);
+ if (store.backgroundColor() == null) {
+ writeInt(os, writer.addColor(new Color(255, 255, 255)));
+ } else {
+ writeInt(os, writer.addColor(store.backgroundColor()));
+ }
+ os.write((byte) '\n');
+ os.write(RtfWriter.escape);
+ os.write(cellWidthStyle);
+ os.write((byte) '\n');
+ os.write(RtfWriter.escape);
+ os.write(cellWidthTag);
+ writeInt(os, cellWidth);
+ os.write((byte) '\n');
+ if (cellpadding > 0) {
+ // values
+ os.write(RtfWriter.escape);
+ os.write(cellPaddingLeft);
+ writeInt(os, cellpadding / 2);
+ os.write(RtfWriter.escape);
+ os.write(cellPaddingTop);
+ writeInt(os, cellpadding / 2);
+ os.write(RtfWriter.escape);
+ os.write(cellPaddingRight);
+ writeInt(os, cellpadding / 2);
+ os.write(RtfWriter.escape);
+ os.write(cellPaddingBottom);
+ writeInt(os, cellpadding / 2);
+ // unit
+ os.write(RtfWriter.escape);
+ os.write(cellPaddingLeftUnit);
+ os.write(RtfWriter.escape);
+ os.write(cellPaddingTopUnit);
+ os.write(RtfWriter.escape);
+ os.write(cellPaddingRightUnit);
+ os.write(RtfWriter.escape);
+ os.write(cellPaddingBottomUnit);
+ }
+ os.write(RtfWriter.escape);
+ os.write(cellRightBorder);
+ writeInt(os, cellRight);
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Write the content of the RtfCell
.
+ *
+ * @param os The OutputStream
to which to write the content of
+ * the RtfCell
to.
+ * @return true if writing the cell content succeeded
+ * @throws DocumentException
+ */
+ public boolean writeCellContent(ByteArrayOutputStream os) throws DocumentException {
+ try {
+ if (mergeType == MERGE_HORIZ_PREV || mergeType == MERGE_BOTH_PREV) {
+ return true;
+ }
+
+ if (!emptyCell) {
+ Iterator cellIterator = store.getElements();
+ Paragraph container = null;
+ while (cellIterator.hasNext()) {
+ Element element = (Element) cellIterator.next();
+ // should we wrap it in a paragraph
+ if(!(element instanceof Paragraph)) {
+ if(container != null) {
+ container.add(element);
+ } else {
+ container = new Paragraph();
+ container.setAlignment(store.horizontalAlignment());
+ container.add(element);
+ }
+ } else {
+ if(container != null) {
+ writer.addElement(container, os);
+ container =null;
+ container =null;
+ }
+
+
+ // if horizontal alignment is undefined overwrite
+ // with that of enclosing cell
+ if (element instanceof Paragraph && ((Paragraph) element).alignment() == Element.ALIGN_UNDEFINED) {
+ ((Paragraph) element).setAlignment(store.horizontalAlignment());
+ }
+ writer.addElement(element, os);
+ if (element.type() == Element.PARAGRAPH && cellIterator.hasNext()) {
+ os.write(RtfWriter.escape);
+ os.write(RtfWriter.paragraph);
+ }
+ }
+ }
+ if(container != null) {
+ writer.addElement(container, os);
+ container =null;
+ }
+ } else {
+ os.write(RtfWriter.escape);
+ os.write(RtfWriter.paragraphDefaults);
+ os.write(RtfWriter.escape);
+ os.write(cellInTable);
+ }
+ os.write(RtfWriter.escape);
+ os.write(cellEnd);
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Sets the merge type and the RtfCell
with which this
+ * RtfCell
is to be merged.
+ *
+ * @param mergeType The merge type specifies the kind of merge to be applied
+ * (MERGE_HORIZ_PREV, MERGE_VERT_PREV, MERGE_BOTH_PREV)
+ * @param mergeCell The RtfCell
that the cell at x and y is to
+ * be merged with
+ */
+ public void setMerge(int mergeType, RtfCell mergeCell) {
+ this.mergeType = mergeType;
+ store = mergeCell.getStore();
+ }
+
+ /**
+ * Get the Cell
with the actual content.
+ *
+ * @return Cell
which is contained in the RtfCell
+ */
+ public Cell getStore() {
+ return store;
+ }
+
+ /**
+ * Get the with of this RtfCell
+ *
+ * @return Width of the current RtfCell
+ */
+ public int getCellWidth() {
+ return cellWidth;
+ }
+
+ /**
+ * sets the width of the cell
+ * @param value a width
+ */
+ public void setCellWidth(int value) {
+ cellWidth = value;
+ }
+
+ /**
+ * Get the position of the right border of this RtfCell
.
+ * @return position of the right border
+ */
+ public int getCellRight() {
+ return cellRight;
+ }
+
+
+ /**
+ * Sets the right position of the cell
+ * @param value a cell position
+ */
+ public void setCellRight(int value) {
+ cellRight = value;
+ }
+
+ /**
+ * Write an Integer to the Outputstream.
+ *
+ * @param out The OutputStream
to be written to.
+ * @param i The int to be written.
+ * @throws IOException
+ */
+ private void writeInt(ByteArrayOutputStream out, int i) throws IOException {
+ out.write(Integer.toString(i).getBytes());
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfElement.java b/src/main/java/com/lowagie/text/rtf/RtfElement.java
new file mode 100644
index 0000000..03d05c0
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfElement.java
@@ -0,0 +1,149 @@
+/*
+ * $Id: RtfElement.java,v 1.8 2004/12/14 15:14:44 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import com.lowagie.text.rtf.document.RtfDocument;
+
+/**
+ * RtfElement is the base class for all RTF Element classes
+ *
+ * Version: $Id: RtfElement.java,v 1.8 2004/12/14 15:14:44 blowagie Exp $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfElement implements RtfBasicElement {
+ /**
+ * Constant for a rtf escape
+ */
+ //public static final byte[] ESCAPE = "\\".getBytes();
+ /**
+ * Constant for a rtf extended escape
+ */
+ //public static final byte[] EXTENDED_ESCAPE = "\\*\\".getBytes();
+
+ /**
+ * The RtfDocument this RtfElement belongs to
+ */
+ protected RtfDocument document = null;
+ /**
+ * Whether this RtfElement is in a table
+ */
+ protected boolean inTable = false;
+ /**
+ * Whether this RtfElement is in a header
+ */
+ protected boolean inHeader = false;
+
+ /**
+ * Constructs a RtfElement belonging to the specified RtfDocument.
+ *
+ * @param doc The RtfDocument this RtfElement belongs to
+ */
+ public RtfElement(RtfDocument doc) {
+ super();
+ this.document = doc;
+ }
+
+ /**
+ * Transforms an integer into its String representation and then returns the bytes
+ * of that string.
+ *
+ * @param i The integer to convert
+ * @return A byte array representing the integer
+ */
+ public byte[] intToByteArray(int i) {
+ return Integer.toString(i).getBytes();
+ }
+
+ /**
+ * Returns the content of the RtfElement in a byte array.
+ *
+ * @return An empty byte array
+ */
+ public byte[] write() {
+ return new byte[0];
+ }
+
+ /**
+ * Sets the RtfDocument this RtfElement belongs to
+ *
+ * @param doc The RtfDocument to use
+ */
+ public void setRtfDocument(RtfDocument doc) {
+ this.document = doc;
+ }
+
+ /**
+ * Gets whether this RtfElement is in a table
+ *
+ * @return Whether this RtfElement is in a table
+ */
+ public boolean isInTable() {
+ return inTable;
+ }
+
+ /**
+ * Sets whether this RtfElement is in a table
+ *
+ * @param inTable True
if this RtfElement is in a table, false
otherwise
+ */
+ public void setInTable(boolean inTable) {
+ this.inTable = inTable;
+ }
+
+ /**
+ * Sets whether this RtfElement is in a header
+ *
+ * @param inHeader True
if this RtfElement is in a header, false
otherwise
+ */
+ public void setInHeader(boolean inHeader) {
+ this.inHeader = inHeader;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfExtendedElement.java b/src/main/java/com/lowagie/text/rtf/RtfExtendedElement.java
new file mode 100644
index 0000000..39a6efa
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfExtendedElement.java
@@ -0,0 +1,65 @@
+/*
+ * $Id: RtfExtendedElement.java,v 1.16 2005/05/04 14:33:37 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+/**
+ * The RtfExtendedElement interface is to be used for elements that also
+ * write data into the definition part of the rtf document
+ * Version: $Id: RtfExtendedElement.java,v 1.16 2005/05/04 14:33:37 blowagie Exp $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public interface RtfExtendedElement extends RtfBasicElement {
+ /**
+ * Return the definition part of the element in a byte array
+ * @return A byte array containing the definition data of the Element
+ */
+ public byte[] writeDefinition();
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfField.java b/src/main/java/com/lowagie/text/rtf/RtfField.java
new file mode 100644
index 0000000..a98fb31
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfField.java
@@ -0,0 +1,84 @@
+/**
+ * $Id: RtfField.java,v 1.18 2006/02/09 17:25:25 hallm Exp $
+ *
+ * Copyright 2002 by
+ * SMB
+ * Steffen.Stundzig@smb-tec.com
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the “GNU LIBRARY GENERAL PUBLIC LICENSE”), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import com.lowagie.text.rtf.RtfWriter;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+
+/**
+ * This interface should mark classes, that can be represented
+ * as RTF fields, such as pagenumber, toc entries and so on.
+ *
+ * ONLY FOR USE WITH THE RtfWriter NOT with the RtfWriter2.
+ *
+ * This class is based on the RtfWriter-package from Mark Hall.
+ * @author Steffen.Stundzig@smb-tec.com
+ * @version $Revision: 1.18 $Date: 2006/02/09 17:25:25 $
+ * @deprecated Please move to the RtfWriter2 and associated classes.
+ */
+public interface RtfField {
+
+
+ /**
+ * Writes an RTF field.
+ * @param writer
+ * @param out
+ * @throws IOException
+ */
+ public void write( RtfWriter writer, OutputStream out ) throws IOException;
+}
+
+
diff --git a/src/main/java/com/lowagie/text/rtf/RtfFont.java b/src/main/java/com/lowagie/text/rtf/RtfFont.java
new file mode 100644
index 0000000..3fd4651
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfFont.java
@@ -0,0 +1,162 @@
+/*
+ * $Id: RtfFont.java,v 1.25 2006/02/09 17:25:26 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import com.lowagie.text.Font;
+
+import java.awt.*;
+
+/**
+ * The RtfFont class enables you to add arbitrary Fonts to a rtf document by specifying
+ * the font name you want to have. The font has to be installed on the client for this to
+ * work.
+ *
+ * ONLY FOR USE WITH THE RtfWriter NOT with the RtfWriter2.
+ *
+ * @author mhall@myrealbox.com
+ * @deprecated Please move to the RtfWriter2 and associated classes. com.lowagie.text.rtf.style.RtfFont replaces the functionality of this class.
+ */
+public class RtfFont extends Font {
+ /**
+ * The font family name of this RtfFont
+ */
+ private String familyName = "";
+
+ /**
+ * Constructs a RtfFont
+ *
+ * @param familyName The family name of this RtfFont
+ */
+ public RtfFont(String familyName) {
+ super(Font.UNDEFINED);
+ this.familyName = familyName;
+ }
+
+ /**
+ * Constructs a RtfFont
+ *
+ * @param familyName The font family name of this RtfFont
+ * @param size The font size of this RtfFont
+ */
+ public RtfFont(String familyName, float size) {
+ super(Font.UNDEFINED, size);
+ this.familyName = familyName;
+ }
+
+ /**
+ * Constructs a RtfFont
+ *
+ * @param familyName The font family name of this RtfFont
+ * @param size The font size of this RtfFont
+ * @param style The font style of this RtfFont
+ */
+ public RtfFont(String familyName, float size, int style) {
+ super(Font.UNDEFINED, size, style);
+ this.familyName = familyName;
+ }
+
+ /**
+ * Constructs a RtfFont
+ *
+ * @param familyName The font family name of this RtfFont
+ * @param size The font size of this RtfFont
+ * @param style The font style of this RtfFont
+ * @param color The font color of this RtfFont
+ */
+ public RtfFont(String familyName, float size, int style, Color color) {
+ super(Font.UNDEFINED, size, style, color);
+ this.familyName = familyName;
+ }
+
+ /**
+ * Gets the familyname as a String.
+ *
+ * @return the familyname
+ */
+ public String getFamilyname() {
+ return this.familyName;
+ }
+
+ /**
+ * Replaces the attributes that are equal to null with
+ * the attributes of a given font.
+ *
+ * @param font the font of a bigger element class
+ * @return a Font
+ */
+ public Font difference(Font font) {
+ String dFamilyname = font.getFamilyname();
+ if(dFamilyname == null || dFamilyname.trim().equals("")) {
+ dFamilyname = this.familyName;
+ }
+
+ float dSize = font.size();
+ if(dSize == Font.UNDEFINED) {
+ dSize = this.size();
+ }
+
+ int dStyle = Font.UNDEFINED;
+ if(this.style() != Font.UNDEFINED && font.style() != Font.UNDEFINED) {
+ dStyle = this.style() | font.style();
+ } else if(this.style() != Font.UNDEFINED) {
+ dStyle = this.style();
+ } else if(font.style() != Font.UNDEFINED) {
+ dStyle = font.style();
+ }
+
+ Color dColor = font.color();
+ if(dColor == null) {
+ dColor = this.color();
+ }
+
+ return new RtfFont(dFamilyname, dSize, dStyle, dColor);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfHeaderFooter.java b/src/main/java/com/lowagie/text/rtf/RtfHeaderFooter.java
new file mode 100644
index 0000000..d1f59f5
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfHeaderFooter.java
@@ -0,0 +1,114 @@
+/**
+ * $Id: RtfHeaderFooter.java,v 1.20 2006/02/09 17:25:25 hallm Exp $
+ *
+ * Copyright 2002 by
+ * SMB
+ * Steffen.Stundzig@smb-tec.com
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without right the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.text.rtf;
+
+import com.lowagie.text.Element;
+import com.lowagie.text.HeaderFooter;
+import com.lowagie.text.Phrase;
+
+
+/**
+ * This HeaderFooter specialization extends the normal HeaderFooter impl.
+ * by the new ctor with 'Element' param.
+ * So we have the ability to add a table or some moe sophisticated stuff
+ * as header or footer content.
+ *
+ * ONLY FOR USE WITH THE RtfWriter NOT with the RtfWriter2.
+ *
+ * This class is based on the RtfWriter-package from Mark Hall.
+ * @author Steffen.Stundzig@smb-tec.com
+ * @author Mark.Hall@myrealbox.com
+ * @version $Revision: 1.20 $Date: 2006/02/09 17:25:25 $
+ * @deprecated Please move to the RtfWriter2 and associated classes. com.lowagie.text.rtf.headerfooter.RtfHeaderFooter replaces the functionality of this class.
+ */
+public class RtfHeaderFooter extends HeaderFooter {
+
+
+ private Element content = null;
+
+
+ /**
+ * Constructs a new header
+ * @param before
+ * @param after
+ */
+ public RtfHeaderFooter( Phrase before, Phrase after ) {
+ super( before, after );
+ }
+
+
+ /**
+ * Constructs a new header
+ * @param before
+ * @param numbered
+ */
+ public RtfHeaderFooter( Phrase before, boolean numbered ) {
+ super( before, numbered );
+ }
+
+
+ /**
+ * Constructs a new header
+ * @param content
+ */
+ public RtfHeaderFooter( Element content ) {
+ super(new Phrase(content.toString()), false);
+ this.content = content;
+ }
+
+
+ /**
+ * @return the element specified in the ctor or null;
+ */
+ public Element content() {
+ return content;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfHeaderFooters.java b/src/main/java/com/lowagie/text/rtf/RtfHeaderFooters.java
new file mode 100644
index 0000000..70d0c83
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfHeaderFooters.java
@@ -0,0 +1,160 @@
+/**
+ * $Id: RtfHeaderFooters.java,v 1.21 2006/02/09 17:25:25 hallm Exp $
+ *
+ * Copyright 2002 by
+ * SMB
+ * Steffen.Stundzig@smb-tec.com
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without right the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import com.lowagie.text.HeaderFooter;
+import com.lowagie.text.Phrase;
+
+
+/**
+ * This HeaderFooter specialization contains some headers or footers for several
+ * pages. Is a list of headerFooters but also a sub class of header footer, to change
+ * as less as possible of the current API.
+ *
+ * ONLY FOR USE WITH THE RtfWriter NOT with the RtfWriter2.
+ *
+ * This class is based on the RtfWriter-package from Mark Hall.
+ * @author Steffen.Stundzig@smb-tec.com
+ * @author Mark.Hall@myrealbox.com
+ * @version $Revision: 1.21 $Date: 2006/02/09 17:25:25 $
+ * @deprecated Please move to the RtfWriter2 and associated classes. com.lowagie.text.rtf.headerfooter.RtfHeaderFooterGroup replaces the functionality of this class.
+ */
+public class RtfHeaderFooters extends HeaderFooter {
+ /** an attribute value */
+ public final static int ALL_PAGES = 0;
+ /** an attribute value */
+ public final static int LEFT_PAGES = 1;
+ /** an attribute value */
+ public final static int RIGHT_PAGES = 2;
+ /** an attribute value */
+ public final static int FIRST_PAGE = 3;
+
+// public int defaultHeader = ALL_PAGES;
+
+ /** header or footer placeholder */
+ private HeaderFooter allPages = null;
+ /** header or footer placeholder */
+ private HeaderFooter leftPages = null;
+ /** header or footer placeholder */
+ private HeaderFooter rightPages = null;
+ /** header or footer placeholder */
+ private HeaderFooter firstPage = null;
+
+ /**
+ * Contructs a HeaderFooters object
+ */
+ public RtfHeaderFooters() {
+ super( new Phrase(""), false );
+ }
+
+ /**
+ * Contructs a HeaderFooters object
+ * @param before
+ * @param after
+ */
+ public RtfHeaderFooters( Phrase before, Phrase after ) {
+ super( before, after );
+ }
+
+ /**
+ * Contructs a HeaderFooters object
+ * @param before
+ * @param numbered
+ */
+ public RtfHeaderFooters( Phrase before, boolean numbered ) {
+ super( before, numbered );
+ }
+
+ /**
+ * Adds a HeaderFooter to this HeaderFooters object
+ * @param type
+ * @param hf
+ */
+ public void set( int type, HeaderFooter hf ) {
+ switch (type) {
+ case ALL_PAGES:
+ allPages = hf;
+ break;
+ case LEFT_PAGES:
+ leftPages = hf;
+ break;
+ case RIGHT_PAGES:
+ rightPages = hf;
+ break;
+ case FIRST_PAGE:
+ firstPage = hf;
+ break;
+ default:
+ throw new IllegalStateException( "unknown type " + type );
+ }
+ }
+
+ /**
+ * Returns a type of HeaderFooter object registered in this HeaderFooters object.
+ * @param type type of the HeaderFooter object
+ * @return a HeaderFooter object
+ */
+ public HeaderFooter get( int type ) {
+ switch (type) {
+ case ALL_PAGES:
+ return allPages;
+ case LEFT_PAGES:
+ return leftPages;
+ case RIGHT_PAGES:
+ return rightPages;
+ case FIRST_PAGE:
+ return firstPage;
+ default:
+ throw new IllegalStateException( "unknown type " + type );
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfMapper.java b/src/main/java/com/lowagie/text/rtf/RtfMapper.java
new file mode 100644
index 0000000..1464c81
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfMapper.java
@@ -0,0 +1,182 @@
+/*
+ * $Id: RtfMapper.java,v 1.10 2005/05/04 14:41:15 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import com.lowagie.text.Anchor;
+import com.lowagie.text.Annotation;
+import com.lowagie.text.Chapter;
+import com.lowagie.text.Chunk;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.Image;
+import com.lowagie.text.List;
+import com.lowagie.text.ListItem;
+import com.lowagie.text.Meta;
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Section;
+import com.lowagie.text.SimpleTable;
+import com.lowagie.text.Table;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.document.RtfInfoElement;
+import com.lowagie.text.rtf.field.RtfAnchor;
+import com.lowagie.text.rtf.graphic.RtfImage;
+import com.lowagie.text.rtf.list.RtfList;
+import com.lowagie.text.rtf.list.RtfListItem;
+import com.lowagie.text.rtf.table.RtfTable;
+import com.lowagie.text.rtf.text.RtfAnnotation;
+import com.lowagie.text.rtf.text.RtfChapter;
+import com.lowagie.text.rtf.text.RtfChunk;
+import com.lowagie.text.rtf.text.RtfNewPage;
+import com.lowagie.text.rtf.text.RtfParagraph;
+import com.lowagie.text.rtf.text.RtfPhrase;
+import com.lowagie.text.rtf.text.RtfSection;
+
+
+/**
+ * The RtfMapper provides mappings between com.lowagie.text.* classes
+ * and the corresponding com.lowagie.text.rtf.** classes.
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfMapper {
+
+ /**
+ * The RtfDocument this RtfMapper belongs to
+ */
+ RtfDocument rtfDoc;
+
+ /**
+ * Constructs a RtfMapper for a RtfDocument
+ *
+ * @param doc The RtfDocument this RtfMapper belongs to
+ */
+ public RtfMapper(RtfDocument doc) {
+ this.rtfDoc = doc;
+ }
+
+ /**
+ * Takes an Element subclass and returns the correct RtfBasicElement
+ * subclass, that wraps the Element subclass.
+ *
+ * @param element The Element to wrap
+ * @return A RtfBasicElement wrapping the Element
+ * @throws DocumentException
+ */
+ public RtfBasicElement mapElement(Element element) throws DocumentException {
+ RtfBasicElement rtfElement = null;
+
+ if(element instanceof RtfBasicElement) {
+ rtfElement = (RtfBasicElement) element;
+ rtfElement.setRtfDocument(this.rtfDoc);
+ return rtfElement;
+ }
+ switch(element.type()) {
+ case Element.CHUNK:
+ if(((Chunk) element).getImage() != null) {
+ rtfElement = new RtfImage(rtfDoc, ((Chunk) element).getImage());
+ } else if(((Chunk) element).hasAttributes() && ((Chunk) element).getAttributes().containsKey(Chunk.NEWPAGE)) {
+ rtfElement = new RtfNewPage(rtfDoc);
+ } else {
+ rtfElement = new RtfChunk(rtfDoc, (Chunk) element);
+ }
+ break;
+ case Element.PHRASE:
+ rtfElement = new RtfPhrase(rtfDoc, (Phrase) element);
+ break;
+ case Element.PARAGRAPH:
+ rtfElement = new RtfParagraph(rtfDoc, (Paragraph) element);
+ break;
+ case Element.ANCHOR:
+ rtfElement = new RtfAnchor(rtfDoc, (Anchor) element);
+ break;
+ case Element.ANNOTATION:
+ rtfElement = new RtfAnnotation(rtfDoc, (Annotation) element);
+ break;
+ case Element.IMGRAW:
+ case Element.IMGTEMPLATE:
+ case Element.JPEG:
+ rtfElement = new RtfImage(rtfDoc, (Image) element);
+ break;
+ case Element.AUTHOR:
+ case Element.SUBJECT:
+ case Element.KEYWORDS:
+ case Element.TITLE:
+ case Element.PRODUCER:
+ case Element.CREATIONDATE:
+ rtfElement = new RtfInfoElement(rtfDoc, (Meta) element);
+ break;
+ case Element.LIST:
+ rtfElement = new RtfList(rtfDoc, (List) element);
+ break;
+ case Element.LISTITEM:
+ rtfElement = new RtfListItem(rtfDoc, (ListItem) element);
+ break;
+ case Element.SECTION:
+ rtfElement = new RtfSection(rtfDoc, (Section) element);
+ break;
+ case Element.CHAPTER:
+ rtfElement = new RtfChapter(rtfDoc, (Chapter) element);
+ break;
+ case Element.TABLE:
+ try {
+ rtfElement = new RtfTable(rtfDoc, (Table) element);
+ }
+ catch(ClassCastException e) {
+ rtfElement = new RtfTable(rtfDoc, ((SimpleTable) element).createTable());
+ }
+ break;
+ }
+
+ return rtfElement;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfPageNumber.java b/src/main/java/com/lowagie/text/rtf/RtfPageNumber.java
new file mode 100644
index 0000000..1e966a2
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfPageNumber.java
@@ -0,0 +1,105 @@
+/**
+ * $Id: RtfPageNumber.java,v 1.26 2006/02/09 17:25:25 hallm Exp $
+ *
+ * Copyright 2002 by
+ * SMB
+ * Steffen.Stundzig@smb-tec.com
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.lowagie.text.Font;
+
+/**
+ * A rtf page number field.
+ *
+ * ONLY FOR USE WITH THE RtfWriter NOT with the RtfWriter2.
+ *
+ * This class is based on the RtfWriter-package from Mark Hall.
+ * @author Steffen.Stundzig@smb-tec.com
+ * @author mhall@myrealbox.com
+ * @version $Revision: 1.26 $Date: 2006/02/09 17:25:25 $
+ * @deprecated Please move to the RtfWriter2 and associated classes. com.lowagie.text.rtf.field.RtfPageNumber replaces the functionality of this class.
+ */
+public class RtfPageNumber extends GenericRtfField implements RtfField {
+ private String content;
+
+ /**
+ * construct a RtfPageNumber. The parameter content will be
+ * displayed in front of the page number using the font given as
+ * second argument.
+ * @param content the String that will be displayed in front of the page number
+ * @param contentFont the font to use to display this page number
+ */
+ public RtfPageNumber( String content, Font contentFont ) {
+ super("PAGE", "", contentFont);
+ this.content = content;
+ }
+
+ /**
+ * write this RtfField into a stream using the writer given as
+ * first argument.
+ * @param writer the RtfWriter to use to write this RtfField
+ * @param out the Stream to write this RtfField into.
+ * @throws IOException
+ */
+ public void write( RtfWriter writer, OutputStream out ) throws IOException {
+ writer.writeInitialFontSignature( out, this );
+ out.write(content.getBytes());
+ writer.writeFinishingFontSignature( out, this );
+ super.write(writer, out);
+ }
+
+ /**
+ * @see com.lowagie.text.Element#toString()
+ */
+ public String toString() {
+ return content;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfRow.java b/src/main/java/com/lowagie/text/rtf/RtfRow.java
new file mode 100644
index 0000000..4a68987
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfRow.java
@@ -0,0 +1,443 @@
+/**
+ * $Id: RtfRow.java,v 1.38 2006/02/09 17:25:25 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import com.lowagie.text.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.io.*;
+import java.awt.Color;
+
+/**
+ * A Helper Class for the RtfWriter
.
+ * RtfCell
s in this RtfRow
*/
+ private ArrayList cells = new ArrayList();
+ /** The RtfWriter
to which this RtfRow
belongs */
+ private RtfWriter writer = null;
+ /** The RtfRow
belongs */
+ private RtfTable mainTable = null;
+
+ /** The width of this RtfRow
(in percent) */
+ private int width = 100;
+ /** The default cellpadding of RtfCells
in this
+ * RtfRow
*/
+ private int cellpadding = 115;
+ /** The default cellspacing of RtfCells
in this
+ * RtfRow
*/
+ private int cellspacing = 14;
+ /** The borders of this RtfRow
*/
+ private int borders = 0;
+ /** The border color of this RtfRow
*/
+ private java.awt.Color borderColor = null;
+ /** The border width of this RtfRow
*/
+ private float borderWidth = 0;
+
+ /** Original Row */
+ private Row origRow = null;
+
+ /**
+ * Create a new RtfRow
.
+ *
+ * @param writer The RtfWriter
that this RtfRow
belongs to
+ * @param mainTable The RtfTable
that created this
+ * RtfRow
+ */
+ public RtfRow(RtfWriter writer, RtfTable mainTable) {
+ super();
+ this.writer = writer;
+ this.mainTable = mainTable;
+ }
+
+ /**
+ * Pregenerate the RtfCell
s in this RtfRow
.
+ *
+ * @param columns The number of RtfCell
s to be generated.
+ */
+ public void pregenerateRows(int columns) {
+ for (int i = 0; i < columns; i++) {
+ RtfCell rtfCell = new RtfCell(writer, mainTable);
+ cells.add(rtfCell);
+ }
+ }
+
+ /**
+ * Import a Row
.
+ * RtfTable
which contains
+ * this RtfRow
and they do exactely what they say
+ * @param row
+ * @param propWidths in percent
+ * @param tableWidth in percent
+ * @param pageWidth
+ * @param cellpadding
+ * @param cellspacing
+ * @param borders
+ * @param borderColor
+ * @param borderWidth
+ * @param y
+ * @return true if importing the row succeeded
+ */
+ public boolean importRow(Row row, float[] propWidths, int tableWidth, int pageWidth, int cellpadding,
+ int cellspacing, int borders, java.awt.Color borderColor, float borderWidth,
+ int y) {
+ // the width of this row is the absolute witdh, calculated from the
+ // proportional with of the table and the total width of the page
+ this.origRow = row;
+ this.width = pageWidth / 100 * tableWidth;
+ this.cellpadding = cellpadding;
+ this.cellspacing = cellspacing;
+ this.borders = borders;
+ this.borderColor = borderColor;
+ this.borderWidth = borderWidth;
+
+ if (this.borderWidth > 2) this.borderWidth = 2;
+
+ int cellLeft = 0;
+ for (int i = 0; i < row.columns(); i++) {
+ Element cell = (Element) row.getCell(i);
+
+ // cellWidth is an absolute argument
+ // it's based on the absolute of this row and the proportional
+ // width of this column
+ int cellWidth = (int) (width / 100 * propWidths[i]);
+ if (cell != null) {
+ if (cell.type() == Element.CELL) {
+ RtfCell rtfCell = (RtfCell) cells.get(i);
+ cellLeft = rtfCell.importCell((Cell) cell, cellLeft, cellWidth, i, y, cellpadding);
+ }
+ } else {
+ RtfCell rtfCell = (RtfCell) cells.get(i);
+ cellLeft = rtfCell.importCell(null, cellLeft, cellWidth, i, y, cellpadding);
+ }
+ }
+
+ // recalculate the cell right border and the cumulative width
+ // on col spanning cells.
+ // col + row spanning cells are also handled by this loop, because the real cell of
+ // the upper left corner in such an col, row matrix is copied as first cell
+ // in each row in this matrix
+ int columns = row.columns();
+ for (int i = 0; i < columns; i++) {
+ RtfCell firstCell = (RtfCell) cells.get(i);
+ Cell cell = firstCell.getStore();
+ int cols = 0;
+ if(cell != null) {
+ cols = cell.colspan();
+ }
+ if (cols > 1) {
+ RtfCell lastCell = (RtfCell) cells.get(i + cols - 1);
+ firstCell.setCellRight(lastCell.getCellRight());
+ int width = firstCell.getCellWidth();
+ for (int j = i + 1; j < i + cols; j++) {
+ RtfCell cCell = (RtfCell) cells.get(j);
+ width += cCell.getCellWidth();
+ }
+ firstCell.setCellWidth(width);
+ i += cols - 1;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Write the RtfRow
to the specified OutputStream
.
+ *
+ * @param os The OutputStream
to which this RtfRow
+ * should be written to.
+ * @param rowNum The index
of this row in the containing table.
+ * @param table The Table
which contains the original Row
.
+ * @return true if writing the row succeeded
+ * @throws DocumentException
+ * @throws IOException
+ */
+ public boolean writeRow(ByteArrayOutputStream os, int rowNum, Table table) throws DocumentException,
+ IOException {
+ os.write(RtfWriter.escape);
+ os.write(rowBegin);
+ os.write((byte) '\n');
+ os.write(RtfWriter.escape);
+ os.write(rowWidthStyle);
+ os.write(RtfWriter.escape);
+ os.write(rowWidth);
+ writeInt(os, width);
+// os.write(RtfWriter.escape);
+// os.write(rowAutofit);
+ if (mainTable.getOriginalTable().hasToFitPageCells()) {
+ os.write(RtfWriter.escape);
+ os.write(rowKeep);
+ }
+ // check if this row is a header row
+ if (rowNum < table.firstDataRow()) {
+ os.write(RtfWriter.escape);
+ os.write(rowHeader);
+ }
+ os.write(RtfWriter.escape);
+ switch (this.origRow.horizontalAlignment()) {
+ case Element.ALIGN_LEFT:
+ os.write(rowAlignLeft);
+ break;
+ case Element.ALIGN_CENTER:
+ os.write(rowAlignCenter);
+ break;
+ case Element.ALIGN_RIGHT:
+ os.write(rowAlignRight);
+ break;
+ default :
+ os.write(rowAlignLeft);
+ break;
+ }
+ os.write(RtfWriter.escape);
+ os.write(graphLeft);
+ writeInt(os, 10);
+ if (((borders & Rectangle.LEFT) == Rectangle.LEFT) && (borderWidth > 0)) {
+ writeBorder(os, rowBorderLeft);
+ }
+ if (((borders & Rectangle.TOP) == Rectangle.TOP) && (borderWidth > 0)) {
+ writeBorder(os, rowBorderTop);
+ }
+ if (((borders & Rectangle.BOTTOM) == Rectangle.BOTTOM) && (borderWidth > 0)) {
+ writeBorder(os, rowBorderBottom);
+ }
+ if (((borders & Rectangle.RIGHT) == Rectangle.RIGHT) && (borderWidth > 0)) {
+ writeBorder(os, rowBorderRight);
+ }
+ if (((borders & Rectangle.BOX) == Rectangle.BOX) && (borderWidth > 0)) {
+ writeBorder(os, rowBorderInlineHorizontal);
+ writeBorder(os, rowBorderInlineVertical);
+ }
+
+ if (cellspacing > 0) {
+ os.write(RtfWriter.escape);
+ os.write(rowSpacingLeft);
+ writeInt(os, cellspacing / 2);
+ os.write(RtfWriter.escape);
+ os.write(rowSpacingLeftStyle);
+ os.write(RtfWriter.escape);
+ os.write(rowSpacingTop);
+ writeInt(os, cellspacing / 2);
+ os.write(RtfWriter.escape);
+ os.write(rowSpacingTopStyle);
+ os.write(RtfWriter.escape);
+ os.write(rowSpacingBottom);
+ writeInt(os, cellspacing / 2);
+ os.write(RtfWriter.escape);
+ os.write(rowSpacingBottomStyle);
+ os.write(RtfWriter.escape);
+ os.write(rowSpacingRight);
+ writeInt(os, cellspacing / 2);
+ os.write(RtfWriter.escape);
+ os.write(rowSpacingRightStyle);
+ }
+ os.write(RtfWriter.escape);
+ os.write(rowPaddingLeft);
+ writeInt(os, cellpadding / 2);
+ os.write(RtfWriter.escape);
+ os.write(rowPaddingRight);
+ writeInt(os, cellpadding / 2);
+ os.write(RtfWriter.escape);
+ os.write(rowPaddingLeftStyle);
+ os.write(RtfWriter.escape);
+ os.write(rowPaddingRightStyle);
+ os.write((byte) '\n');
+
+ Iterator cellIterator = cells.iterator();
+ while (cellIterator.hasNext()) {
+ RtfCell cell = (RtfCell) cellIterator.next();
+ cell.writeCellSettings(os);
+ }
+
+ os.write(RtfWriter.escape);
+ os.write("intbl".getBytes());
+
+ cellIterator = cells.iterator();
+ while (cellIterator.hasNext()) {
+ RtfCell cell = (RtfCell) cellIterator.next();
+ cell.writeCellContent(os);
+ }
+ os.write(RtfWriter.delimiter);
+ os.write(RtfWriter.escape);
+ os.write(rowEnd);
+ return true;
+ }
+
+
+ private void writeBorder(ByteArrayOutputStream os, byte[] borderType) throws IOException {
+ // horizontal and vertical, top, left, bottom, right
+ os.write(RtfWriter.escape);
+ os.write(borderType);
+ // line style
+ os.write(RtfWriter.escape);
+ os.write(RtfRow.tableBorder);
+ // borderwidth
+ os.write(RtfWriter.escape);
+ os.write(RtfRow.tableBorderWidth);
+ writeInt(os, (int) (borderWidth * RtfWriter.TWIPSFACTOR));
+ // border color
+ os.write(RtfWriter.escape);
+ os.write(RtfRow.tableBorderColor);
+ if (borderColor == null) {
+ writeInt(os, writer.addColor(new Color(0, 0, 0)));
+ } else {
+ writeInt(os, writer.addColor(borderColor));
+ }
+ os.write((byte) '\n');
+ }
+
+
+ /**
+ * RtfTable
s call this method from their own setMerge() to
+ * specify that a certain other cell is to be merged with it.
+ *
+ * @param x The column position of the cell to be merged
+ * @param mergeType The merge type specifies the kind of merge to be applied
+ * (MERGE_HORIZ_PREV, MERGE_VERT_PREV, MERGE_BOTH_PREV)
+ * @param mergeCell The RtfCell
that the cell at x and y is to
+ * be merged with
+ */
+ public void setMerge(int x, int mergeType, RtfCell mergeCell) {
+ RtfCell cell = (RtfCell) cells.get(x);
+ cell.setMerge(mergeType, mergeCell);
+ }
+
+ /*
+ * Write an Integer to the Outputstream.
+ *
+ * @param out The OutputStream
to be written to.
+ * @param i The int to be written.
+ */
+ private void writeInt(ByteArrayOutputStream out, int i) throws IOException {
+ out.write(Integer.toString(i).getBytes());
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfTOC.java b/src/main/java/com/lowagie/text/rtf/RtfTOC.java
new file mode 100644
index 0000000..044a871
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfTOC.java
@@ -0,0 +1,187 @@
+/**
+ * $Id: RtfTOC.java,v 1.23 2006/02/09 17:25:25 hallm Exp $
+ *
+ * Copyright 2002 by
+ * SMB
+ * Steffen.Stundzig@smb-tec.com
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the “GNU LIBRARY GENERAL PUBLIC LICENSE”), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.lowagie.text.Chunk;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Font;
+import com.lowagie.text.ExceptionConverter;
+
+/**
+ * This class can be used to insert a table of contents into
+ * the RTF document.
+ * Therefore the field TOC is used. It works great in Word 2000.
+ * StarOffice doesn't support such fields. Other word version
+ * are not tested yet.
+ *
+ * ONLY FOR USE WITH THE RtfWriter NOT with the RtfWriter2.
+ *
+ * This class is based on the RtfWriter-package from Mark Hall.
+ * @author Steffen.Stundzig@smb-tec.com
+ * @version $Revision: 1.23 $Date: 2006/02/09 17:25:25 $
+ * @deprecated Please move to the RtfWriter2 and associated classes. com.lowagie.text.rtf.field.RtfTableOfContents replaces the functionality of this class.
+ */
+public class RtfTOC extends Chunk implements RtfField {
+
+
+ private String defaultText = "Klicken Sie mit der rechten Maustaste auf diesen Text, um das Inhaltsverzeichnis zu aktualisieren!";
+
+ private boolean addTOCAsTOCEntry = false;
+
+ private Font entryFont = null;
+ private String entryName = null;
+
+
+ /**
+ * @param tocName the headline of the table of contents
+ * @param tocFont the font for the headline
+ */
+ public RtfTOC( String tocName, Font tocFont ) {
+ super( tocName, tocFont );
+ }
+
+ /**
+ * @see com.lowagie.text.rtf.RtfField#write(com.lowagie.text.rtf.RtfWriter, java.io.OutputStream)
+ */
+ public void write( RtfWriter writer, OutputStream out ) throws IOException {
+
+ writer.writeInitialFontSignature( out, this );
+ out.write( RtfWriter.filterSpecialChar( content(), true ).getBytes() );
+ writer.writeFinishingFontSignature( out, this );
+
+ if (addTOCAsTOCEntry) {
+ RtfTOCEntry entry = new RtfTOCEntry( entryName, entryFont );
+ entry.hideText();
+ try {
+ writer.add( entry );
+ } catch ( DocumentException de ) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ // line break after headline
+ out.write( RtfWriter.escape );
+ out.write( RtfWriter.paragraph );
+ out.write( RtfWriter.delimiter );
+
+ // toc field entry
+ out.write( RtfWriter.openGroup );
+ out.write( RtfWriter.escape );
+ out.write( RtfWriter.field );
+ // field initialization stuff
+ out.write( RtfWriter.openGroup );
+ out.write( RtfWriter.escape );
+ out.write( RtfWriter.fieldContent );
+ out.write( RtfWriter.delimiter );
+ out.write( "TOC".getBytes() );
+ // create the TOC based on the 'toc entries'
+ out.write( RtfWriter.delimiter );
+ out.write( RtfWriter.escape );
+ out.write( RtfWriter.escape );
+ out.write( "f".getBytes() );
+ out.write( RtfWriter.delimiter );
+ // create Hyperlink TOC Entrie
+ out.write( RtfWriter.escape );
+ out.write( RtfWriter.escape );
+ out.write( "h".getBytes() );
+ out.write( RtfWriter.delimiter );
+ // create the TOC based on the paragraph level
+ out.write( RtfWriter.delimiter );
+ out.write( RtfWriter.escape );
+ out.write( RtfWriter.escape );
+ out.write( "u".getBytes() );
+ out.write( RtfWriter.delimiter );
+ // create the TOC based on the paragraph headlines 1-5
+ out.write( RtfWriter.delimiter );
+ out.write( RtfWriter.escape );
+ out.write( RtfWriter.escape );
+ out.write( "o".getBytes() );
+ out.write( RtfWriter.delimiter );
+ out.write( "\"1-5\"".getBytes() );
+ out.write( RtfWriter.delimiter );
+ out.write( RtfWriter.closeGroup );
+
+ // field default result stuff
+ out.write( RtfWriter.openGroup );
+ out.write( RtfWriter.escape );
+ out.write( RtfWriter.fieldDisplay );
+ out.write( RtfWriter.delimiter );
+ out.write( defaultText.getBytes() );
+ out.write( RtfWriter.delimiter );
+ out.write( RtfWriter.closeGroup );
+ out.write( RtfWriter.closeGroup );
+ }
+
+
+ /**
+ * Add a toc entry
+ * @param entryName the name of the entry
+ * @param entryFont the font to be used for the entry
+ */
+ public void addTOCAsTOCEntry( String entryName, Font entryFont ) {
+ this.addTOCAsTOCEntry = true;
+ this.entryFont = entryFont;
+ this.entryName = entryName;
+ }
+
+
+ /**
+ * Sets the default text of the Table of Contents
+ * @param text the default text
+ */
+ public void setDefaultText( String text ) {
+ this.defaultText = text;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfTOCEntry.java b/src/main/java/com/lowagie/text/rtf/RtfTOCEntry.java
new file mode 100644
index 0000000..c3ceab1
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfTOCEntry.java
@@ -0,0 +1,174 @@
+/**
+ * $Id: RtfTOCEntry.java,v 1.23 2006/02/09 17:25:26 hallm Exp $
+ *
+ * Copyright 2002 by
+ * SMB
+ * Steffen.Stundzig@smb-tec.com
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the “GNU LIBRARY GENERAL PUBLIC LICENSE”), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import com.lowagie.text.Chunk;
+import com.lowagie.text.Font;
+
+/**
+ * This class can be used to insert entries for a table of contents into
+ * the RTF document.
+ *
+ * ONLY FOR USE WITH THE RtfWriter NOT with the RtfWriter2.
+ *
+ * This class is based on the RtfWriter-package from Mark Hall.
+ * @author Steffen.Stundzig@smb-tec.com
+ * @version $Revision: 1.23 $Date: 2006/02/09 17:25:26 $
+ * @deprecated Please move to the RtfWriter2 and associated classes. com.lowagie.text.rtf.field.RtfTOCEntry replaces the functionality of this class.
+ */
+public class RtfTOCEntry extends Chunk implements RtfField {
+
+
+ private boolean hideText = false;
+
+ private boolean hidePageNumber = false;
+
+ private String entryName;
+
+ private Font entryFont;
+
+ private Font contentFont;
+
+
+ /**
+ * Constructs an entry for the Table of Contents
+ * @param content the content of the entry
+ * @param contentFont the font
+ */
+ public RtfTOCEntry( String content, Font contentFont ) {
+ this( content, contentFont, content, contentFont );
+// super( content, font );
+// this.entryName = content;
+// printEntryNameAsText = true;
+ }
+
+
+ /**
+ * Constructs an entry for the Table of Contents
+ * @param content the content of the entry
+ * @param contentFont the font
+ * @param entryName name of the entry
+ * @param entryFont font of the entryname
+ */
+ public RtfTOCEntry( String content, Font contentFont, String entryName, Font entryFont ) {
+ super( content, contentFont );
+ // hide the text of the entry, because it is printed
+ this.entryName = entryName;
+ this.entryFont = entryFont;
+ this.contentFont = contentFont;
+ }
+
+ /**
+ * @see com.lowagie.text.rtf.RtfField#write(com.lowagie.text.rtf.RtfWriter, java.io.OutputStream)
+ */
+ public void write( RtfWriter writer, OutputStream out ) throws IOException {
+
+ if (!hideText) {
+ writer.writeInitialFontSignature( out, new Chunk("", contentFont) );
+ out.write( RtfWriter.filterSpecialChar( content(), true ).getBytes() );
+ writer.writeFinishingFontSignature( out, new Chunk("", contentFont) );
+ }
+
+ if (!entryFont.equals( contentFont )) {
+ writer.writeInitialFontSignature(out, new Chunk("", entryFont) );
+ writeField( out );
+ writer.writeFinishingFontSignature(out, new Chunk("", entryFont) );
+ } else {
+ writer.writeInitialFontSignature(out, new Chunk("", contentFont) );
+ writeField( out );
+ writer.writeFinishingFontSignature(out, new Chunk("", contentFont) );
+ }
+ }
+
+
+ private void writeField( OutputStream out ) throws IOException {
+
+ // always hide the toc entry
+ out.write( RtfWriter.openGroup );
+ out.write( RtfWriter.escape );
+ out.write( "v".getBytes() );
+
+ // tc field entry
+ out.write( RtfWriter.openGroup );
+ out.write( RtfWriter.escape );
+ if (!hidePageNumber) {
+ out.write( "tc".getBytes() );
+ } else {
+ out.write( "tcn".getBytes() );
+ }
+ out.write( RtfWriter.delimiter );
+ out.write( RtfWriter.filterSpecialChar( entryName, true ).getBytes() );
+ out.write( RtfWriter.delimiter );
+ out.write( RtfWriter.closeGroup );
+
+ out.write( RtfWriter.closeGroup );
+ }
+
+ /**
+ * sets the hideText value to true
+ */
+ public void hideText() {
+ hideText = true;
+ }
+
+ /**
+ * sets the hidePageNumber value to true
+ */
+ public void hidePageNumber() {
+ hidePageNumber = true;
+ }
+}
+
+
diff --git a/src/main/java/com/lowagie/text/rtf/RtfTable.java b/src/main/java/com/lowagie/text/rtf/RtfTable.java
new file mode 100644
index 0000000..70c403e
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfTable.java
@@ -0,0 +1,201 @@
+/**
+ * $Id: RtfTable.java,v 1.31 2006/02/09 17:25:25 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import com.lowagie.text.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.io.*;
+
+/**
+ * A Helper Class for the RtfWriter
.
+ * DocumentListener
for Rtf
+ *
+ * ONLY FOR USE WITH THE RtfWriter NOT with the RtfWriter2.
+ *
+ * Parts of this Class were contributed by Steffen Stundzig. Many thanks for the
+ * improvements.
+ * Updates Benoit WIART RtfTable
.
+ *
+ * @param writer The RtfWriter
that created this Table
+ */
+ public RtfTable(RtfWriter writer) {
+ super();
+ this.writer = writer;
+ }
+
+ /**
+ * Import a Table
into the RtfTable
.
+ * Table
specifying the Table
to be imported
+ * @param pageWidth An int
specifying the page width
+ * @return true if importing the table succeeded
+ */
+ public boolean importTable(Table table, int pageWidth) {
+ origTable = table;
+ // All Cells are pregenerated first, so that cell and rowspanning work
+ Iterator rows = table.iterator();
+ Row row = null;
+
+ int tableWidth = (int) table.widthPercentage();
+ int cellpadding = (int) (table.cellpadding() * RtfWriter.TWIPSFACTOR);
+ int cellspacing = (int) (table.cellspacing() * RtfWriter.TWIPSFACTOR);
+ float[] propWidths = table.getProportionalWidths();
+
+ int borders = table.border();
+ java.awt.Color borderColor = table.borderColor();
+ float borderWidth = table.borderWidth();
+
+ for (int i = 0; i < table.size(); i++) {
+ RtfRow rtfRow = new RtfRow(writer, this);
+ rtfRow.pregenerateRows(table.columns());
+ rowsList.add(rtfRow);
+ }
+ int i = 0;
+ while (rows.hasNext()) {
+ row = (Row) rows.next();
+ row.setHorizontalAlignment(table.alignment());
+ RtfRow rtfRow = (RtfRow) rowsList.get(i);
+ rtfRow.importRow(row, propWidths, tableWidth, pageWidth, cellpadding, cellspacing, borders, borderColor, borderWidth, i);
+ i++;
+ }
+ return true;
+ }
+
+ /**
+ * Output the content of the RtfTable
to an OutputStream.
+ *
+ * @param os The OutputStream
that the content of the RtfTable
is to be written to
+ * @return true if writing the table succeeded
+ * @throws DocumentException
+ * @throws IOException
+ */
+ public boolean writeTable(ByteArrayOutputStream os) throws DocumentException, IOException {
+
+ if(!this.writer.writingHeaderFooter()) {
+ // Added by Benoit WIART RtfCell
s call this method to specify that a certain other cell is to be merged with it.
+ *
+ * @param x The column position of the cell to be merged
+ * @param y The row position of the cell to be merged
+ * @param mergeType The merge type specifies the kind of merge to be applied (MERGE_HORIZ_PREV, MERGE_VERT_PREV, MERGE_BOTH_PREV)
+ * @param mergeCell The RtfCell
that the cell at x and y is to be merged with
+ */
+ public void setMerge(int x, int y, int mergeType, RtfCell mergeCell) {
+ RtfRow row = (RtfRow) rowsList.get(y);
+ row.setMerge(x, mergeType, mergeCell);
+ }
+
+ /**
+ * This method allows access to the original Table that led to this RtfTable.
+ *
+ * @return The Table object that is the basis of this RtfTable.
+ */
+ protected Table getOriginalTable() {
+ return origTable;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfTableCell.java b/src/main/java/com/lowagie/text/rtf/RtfTableCell.java
new file mode 100644
index 0000000..1d9b220
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfTableCell.java
@@ -0,0 +1,392 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import com.lowagie.text.Cell;
+import com.lowagie.text.Element;
+import com.lowagie.text.BadElementException;
+
+import java.util.Properties;
+
+/**
+ * A Cell
with extended style attributes
+ *
+ * ONLY FOR USE WITH THE RtfWriter NOT with the RtfWriter2.
+ * @deprecated Please move to the RtfWriter2 and associated classes. com.lowagie.text.rtf.table.RtfCell replaces the functionality of this class.
+ */
+public class RtfTableCell extends Cell
+{
+ /* Table border styles */
+
+ /** Table border solid */
+ public static final int BORDER_UNDEFINED = 0;
+
+ /** Table border solid */
+ public static final int BORDER_SINGLE = 1;
+
+ /** Table border double thickness */
+ public static final int BORDER_DOUBLE_THICK = 2;
+
+ /** Table border shadowed */
+ public static final int BORDER_SHADOWED = 3;
+
+ /** Table border dotted */
+ public static final int BORDER_DOTTED = 4;
+
+ /** Table border dashed */
+ public static final int BORDER_DASHED = 5;
+
+ /** Table border hairline */
+ public static final int BORDER_HAIRLINE = 6;
+
+ /** Table border double line */
+ public static final int BORDER_DOUBLE = 7;
+
+ /** Table border dot dash line */
+ public static final int BORDER_DOT_DASH = 8;
+
+ /** Table border dot dot dash line */
+ public static final int BORDER_DOT_DOT_DASH = 9;
+
+ /** Table border triple line */
+ public static final int BORDER_TRIPLE = 10;
+
+ /** Table border line */
+ public static final int BORDER_THICK_THIN = 11;
+
+ /** Table border line */
+ public static final int BORDER_THIN_THICK = 12;
+
+ /** Table border line */
+ public static final int BORDER_THIN_THICK_THIN = 13;
+
+ /** Table border line */
+ public static final int BORDER_THICK_THIN_MED = 14;
+
+ /** Table border line */
+ public static final int BORDER_THIN_THICK_MED = 15;
+
+ /** Table border line */
+ public static final int BORDER_THIN_THICK_THIN_MED = 16;
+
+ /** Table border line */
+ public static final int BORDER_THICK_THIN_LARGE = 17;
+
+ /** Table border line */
+ public static final int BORDER_THIN_THICK_LARGE = 18;
+
+ /** Table border line */
+ public static final int BORDER_THIN_THICK_THIN_LARGE = 19;
+
+ /** Table border line */
+ public static final int BORDER_WAVY = 20;
+
+ /** Table border line */
+ public static final int BORDER_DOUBLE_WAVY = 21;
+
+ /** Table border line */
+ public static final int BORDER_STRIPED = 22;
+
+ /** Table border line */
+ public static final int BORDER_EMBOSS = 23;
+
+ /** Table border line */
+ public static final int BORDER_ENGRAVE = 24;
+
+ /* Instance variables */
+ private float topBorderWidth;
+ private float leftBorderWidth;
+ private float rightBorderWidth;
+ private float bottomBorderWidth;
+ private int topBorderStyle = 1;
+ private int leftBorderStyle = 1;
+ private int rightBorderStyle = 1;
+ private int bottomBorderStyle = 1;
+
+/**
+ * Constructs an empty Cell
(for internal use only).
+ *
+ * @param dummy a dummy value
+ */
+
+ public RtfTableCell(boolean dummy) {
+ super(dummy);
+ }
+
+/**
+ * Constructs a Cell
with a certain Element
.
+ * ListItem
, Row
or
+ * Cell
, an exception will be thrown.
+ *
+ * @param element the element
+ * @throws BadElementException when the creator was called with a ListItem
, Row
or Cell
+ */
+ public RtfTableCell(Element element) throws BadElementException {
+ super(element);
+ }
+
+/**
+ * Constructs a Cell
with a certain content.
+ * String
will be converted into a Paragraph
.
+ *
+ * @param content a String
+ */
+ public RtfTableCell(String content) {
+ super(content);
+ }
+
+/**
+ * Returns a Cell
that has been constructed taking in account
+ * the value of some attributes.
+ *
+ * @param attributes Some attributes
+ */
+
+ public RtfTableCell(Properties attributes) {
+ super(attributes);
+ }
+
+ /**
+ * Set all four borders to f
width
+ *
+ * @param f the desired width
+ */
+ public void setBorderWidth(float f) {
+ super.setBorderWidth(f);
+ topBorderWidth = f;
+ leftBorderWidth = f;
+ rightBorderWidth = f;
+ bottomBorderWidth = f;
+ }
+
+ /**
+ * Set the top border to f
width
+ *
+ * @param f the desired width
+ */
+ public void setTopBorderWidth(float f) {
+ topBorderWidth = f;
+ }
+
+ /**
+ * Get the top border width
+ * @return a width
+ */
+ public float topBorderWidth() {
+ return topBorderWidth;
+ }
+
+ /**
+ * Set the left border to f
width
+ *
+ * @param f the desired width
+ */
+ public void setLeftBorderWidth(float f) {
+ leftBorderWidth = f;
+ }
+
+ /**
+ * Get the left border width
+ * @return a width
+ */
+ public float leftBorderWidth() {
+ return leftBorderWidth;
+ }
+
+ /**
+ * Set the right border to f
width
+ *
+ * @param f the desired width
+ */
+ public void setRightBorderWidth(float f) {
+ rightBorderWidth = f;
+ }
+
+ /**
+ * Get the right border width
+ * @return a width
+ */
+ public float rightBorderWidth() {
+ return rightBorderWidth;
+ }
+
+ /**
+ * Set the bottom border to f
width
+ *
+ * @param f the desired width
+ */
+ public void setBottomBorderWidth(float f) {
+ bottomBorderWidth = f;
+ }
+
+ /**
+ * Get the bottom border width
+ * @return a width
+ */
+ public float bottomBorderWidth() {
+ return bottomBorderWidth;
+ }
+
+ /**
+ * Set all four borders to style defined by style
+ *
+ * @param style the desired style
+ */
+ public void setBorderStyle(int style) {
+ topBorderStyle = style;
+ leftBorderStyle = style;
+ rightBorderStyle = style;
+ bottomBorderStyle = style;
+ }
+
+ /**
+ * Set the top border to style defined by style
+ *
+ * @param style the desired style
+ */
+ public void setTopBorderStyle(int style) {
+ topBorderStyle = style;
+ }
+
+ /**
+ * Get the top border style
+ * @return a style value
+ */
+ public int topBorderStyle() {
+ return topBorderStyle;
+ }
+
+ /**
+ * Set the left border to style defined by style
+ *
+ * @param style the desired style
+ */
+ public void setLeftBorderStyle(int style) {
+ leftBorderStyle = style;
+ }
+
+ /**
+ * Get the left border style
+ * @return a style value
+ */
+ public int leftBorderStyle() {
+ return leftBorderStyle;
+ }
+
+ /**
+ * Set the right border to style defined by style
+ *
+ * @param style the desired style
+ */
+ public void setRightBorderStyle(int style) {
+ rightBorderStyle = style;
+ }
+
+ /**
+ * Get the right border style
+ * @return a style value
+ */
+ public int rightBorderStyle() {
+ return rightBorderStyle;
+ }
+
+ /**
+ * Set the bottom border to style defined by style
+ *
+ * @param style the desired style
+ */
+ public void setBottomBorderStyle(int style) {
+ bottomBorderStyle = style;
+ }
+
+ /**
+ * Get the bottom border style
+ * @return a style value
+ */
+ public int bottomBorderStyle() {
+ return bottomBorderStyle;
+ }
+
+ /**
+ * Get the RTF control word for style
+ * @param style a style value
+ * @return a byte array corresponding with a style control word
+ */
+ protected static byte[] getStyleControlWord(int style) {
+ switch(style)
+ {
+ case BORDER_UNDEFINED : return "brdrs".getBytes();
+ case BORDER_SINGLE : return "brdrs".getBytes();
+ case BORDER_DOUBLE_THICK : return "brdrth".getBytes();
+ case BORDER_SHADOWED : return "brdrsh".getBytes();
+ case BORDER_DOTTED : return "brdrdot".getBytes();
+ case BORDER_DASHED : return "brdrdash".getBytes();
+ case BORDER_HAIRLINE : return "brdrhair".getBytes();
+ case BORDER_DOUBLE : return "brdrdb".getBytes();
+ case BORDER_DOT_DASH : return "brdrdashd".getBytes();
+ case BORDER_DOT_DOT_DASH : return "brdrdashdd".getBytes();
+ case BORDER_TRIPLE : return "brdrtriple".getBytes();
+ case BORDER_THICK_THIN : return "brdrtnthsg".getBytes();
+ case BORDER_THIN_THICK : return "brdrthtnsg".getBytes();
+ case BORDER_THIN_THICK_THIN : return "brdrtnthtnsg".getBytes();
+ case BORDER_THICK_THIN_MED : return "brdrtnthmg".getBytes();
+ case BORDER_THIN_THICK_MED : return "brdrthtnmg".getBytes();
+ case BORDER_THIN_THICK_THIN_MED : return "brdrtnthtnmg".getBytes();
+ case BORDER_THICK_THIN_LARGE : return "brdrtnthlg".getBytes();
+ case BORDER_THIN_THICK_LARGE : return "brdrthtnlg".getBytes();
+ case BORDER_THIN_THICK_THIN_LARGE : return "brdrtnthtnlg".getBytes();
+ case BORDER_WAVY : return "brdrwavy".getBytes();
+ case BORDER_DOUBLE_WAVY : return "brdrwavydb".getBytes();
+ case BORDER_STRIPED : return "brdrdashdotstr".getBytes();
+ case BORDER_EMBOSS : return "brdremboss".getBytes();
+ case BORDER_ENGRAVE : return "brdrengrave".getBytes();
+ }
+
+ return "brdrs".getBytes();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/RtfWriter.java b/src/main/java/com/lowagie/text/rtf/RtfWriter.java
new file mode 100644
index 0000000..43b47da
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfWriter.java
@@ -0,0 +1,2299 @@
+/*
+ * $Id: RtfWriter.java,v 1.70 2006/02/09 17:25:25 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import com.lowagie.text.*;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.ListIterator;
+import java.util.Iterator;
+import java.util.Calendar;
+import java.util.Date;
+import java.awt.Color;
+import java.text.SimpleDateFormat;
+import java.text.ParsePosition;
+import com.lowagie.text.pdf.codec.wmf.MetaDo;
+
+/**
+ * If you are creating a new project using the rtf part of iText, please
+ * consider using the new RtfWriter2. The RtfWriter is in bug-fix-only mode,
+ * will be deprecated end of 2005 and removed end of 2007.
+ *
+ * A DocWriter
class for Rich Text Files (RTF).
+ * RtfWriter
can be added as a DocListener
+ * to a certain Document
by getting an instance.
+ * Every Element
added to the original Document
+ * will be written to the OutputStream
of this RtfWriter
.
+ *
+ *
+ * // creation of the document with a certain size and certain margins
+ * Document document = new Document(PageSize.A4, 50, 50, 50, 50);
+ * try {
+ * // this will write RTF to the Standard OutputStream
+ * RtfWriter.getInstance(document, System.out);
+ * // this will write Rtf to a file called text.rtf
+ * RtfWriter.getInstance(document, new FileOutputStream("text.rtf"));
+ * // this will write Rtf to for instance the OutputStream of a HttpServletResponse-object
+ * RtfWriter.getInstance(document, response.getOutputStream());
+ * }
+ * catch(DocumentException de) {
+ * System.err.println(de.getMessage());
+ * }
+ * // this will close the document and all the OutputStreams listening to it
+ * document.close();
+ *
+ * There are currently still a few limitations on what the RTF Writer can do:
+ *
+ *
+ *
+ *
+ * @author Mark.Hall@myrealbox.com
+ * @author Steffen Stundzig
+ * @author Eric Mattes
+ * @author Raul Wegmann
+ * @deprecated The RtfWriter is deprecated and will be removed from the iText library end of 2007
+ */
+public class RtfWriter extends DocWriter implements DocListener {
+ /**
+ * Static Constants
+ */
+
+ /**
+ * General
+ */
+
+ /** This is the escape character which introduces RTF tags. */
+ public static final byte escape = (byte) '\\';
+
+ /** This is another escape character which introduces RTF tags. */
+ private static final byte[] extendedEscape = "\\*\\".getBytes();
+
+ /** This is the delimiter between RTF tags and normal text. */
+ protected static final byte delimiter = (byte) ' ';
+
+ /** This is another delimiter between RTF tags and normal text. */
+ private static final byte commaDelimiter = (byte) ';';
+
+ /** This is the character for beginning a new group. */
+ public static final byte openGroup = (byte) '{';
+
+ /** This is the character for closing a group. */
+ public static final byte closeGroup = (byte) '}';
+
+ /**
+ * RTF Information
+ */
+
+ /** RTF begin and version. */
+ private static final byte[] docBegin = "rtf1".getBytes();
+
+ /** RTF encoding. */
+ private static final byte[] ansi = "ansi".getBytes();
+
+ /** RTF encoding codepage. */
+ private static final byte[] ansiCodepage = "ansicpg".getBytes();
+
+ /**
+ *Font Data
+ */
+
+ /** Begin the font table tag. */
+ private static final byte[] fontTable = "fonttbl".getBytes();
+
+ /** Font number tag. */
+ protected static final byte fontNumber = (byte) 'f';
+
+ /** Font size tag. */
+ protected static final byte[] fontSize = "fs".getBytes();
+
+ /** Font color tag. */
+ protected static final byte[] fontColor = "cf".getBytes();
+
+ /** Modern font tag. */
+ private static final byte[] fontModern = "fmodern".getBytes();
+
+ /** Swiss font tag. */
+ private static final byte[] fontSwiss = "fswiss".getBytes();
+
+ /** Roman font tag. */
+ private static final byte[] fontRoman = "froman".getBytes();
+
+ /** Tech font tag. */
+ private static final byte[] fontTech = "ftech".getBytes();
+
+ /** Font charset tag. */
+ private static final byte[] fontCharset = "fcharset".getBytes();
+
+ /** Font Courier tag. */
+ private static final byte[] fontCourier = "Courier".getBytes();
+
+ /** Font Arial tag. */
+ private static final byte[] fontArial = "Arial".getBytes();
+
+ /** Font Symbol tag. */
+ private static final byte[] fontSymbol = "Symbol".getBytes();
+
+ /** Font Times New Roman tag. */
+ private static final byte[] fontTimesNewRoman = "Times New Roman".getBytes();
+
+ /** Font Windings tag. */
+ private static final byte[] fontWindings = "Windings".getBytes();
+
+ /** Default Font. */
+ private static final byte[] defaultFont = "deff".getBytes();
+
+ /** First indent tag. */
+ private static final byte[] firstIndent = "fi".getBytes();
+
+ /** Left indent tag. */
+ private static final byte[] listIndent = "li".getBytes();
+
+ /** Right indent tag. */
+ private static final byte[] rightIndent = "ri".getBytes();
+
+ /**
+ * Sections / Paragraphs
+ */
+
+ /** Reset section defaults tag. */
+ private static final byte[] sectionDefaults = "sectd".getBytes();
+
+ /** Begin new section tag. */
+ private static final byte[] section = "sect".getBytes();
+
+ /** Reset paragraph defaults tag. */
+ public static final byte[] paragraphDefaults = "pard".getBytes();
+
+ /** Begin new paragraph tag. */
+ public static final byte[] paragraph = "par".getBytes();
+
+ /** Page width of a section. */
+ public static final byte[] sectionPageWidth = "pgwsxn".getBytes();
+
+ /** Page height of a section. */
+ public static final byte[] sectionPageHeight = "pghsxn".getBytes();
+
+ /**
+ * Lists
+ */
+
+ /** Begin the List Table */
+ private static final byte[] listtableGroup = "listtable".getBytes();
+
+ /** Begin the List Override Table */
+ private static final byte[] listoverridetableGroup = "listoverridetable".getBytes();
+
+ /** Begin a List definition */
+ private static final byte[] listDefinition = "list".getBytes();
+
+ /** List Template ID */
+ private static final byte[] listTemplateID = "listtemplateid".getBytes();
+
+ /** RTF Writer outputs hybrid lists */
+ private static final byte[] hybridList = "hybrid".getBytes();
+
+ /** Current List level */
+ private static final byte[] listLevelDefinition = "listlevel".getBytes();
+
+ /** Level numbering (old) */
+ private static final byte[] listLevelTypeOld = "levelnfc".getBytes();
+
+ /** Level numbering (new) */
+ private static final byte[] listLevelTypeNew = "levelnfcn".getBytes();
+
+ /** Level alignment (old) */
+ private static final byte[] listLevelAlignOld = "leveljc".getBytes();
+
+ /** Level alignment (new) */
+ private static final byte[] listLevelAlignNew = "leveljcn".getBytes();
+
+ /** Level starting number */
+ private static final byte[] listLevelStartAt = "levelstartat".getBytes();
+
+ /** Level text group */
+ private static final byte[] listLevelTextDefinition = "leveltext".getBytes();
+
+ /** Filler for Level Text Length */
+ private static final byte[] listLevelTextLength = "\'0".getBytes();
+
+ /** Level Text Numbering Style */
+ private static final byte[] listLevelTextStyleNumbers = "\'00.".getBytes();
+
+ /** Level Text Bullet Style */
+ private static final byte[] listLevelTextStyleBullet = "u-3913 ?".getBytes();
+
+ /** Level Numbers Definition */
+ private static final byte[] listLevelNumbersDefinition = "levelnumbers".getBytes();
+
+ /** Filler for Level Numbers */
+ private static final byte[] listLevelNumbers = "\\'0".getBytes();
+
+ /** Tab Stop */
+ private static final byte[] tabStop = "tx".getBytes();
+
+ /** Actual list begin */
+ private static final byte[] listBegin = "ls".getBytes();
+
+ /** Current list level */
+ private static final byte[] listCurrentLevel = "ilvl".getBytes();
+
+ /** List text group for older browsers */
+ private static final byte[] listTextOld = "listtext".getBytes();
+
+ /** Tab */
+ private static final byte[] tab = "tab".getBytes();
+
+ /** Old Bullet Style */
+ private static final byte[] listBulletOld = "\'b7".getBytes();
+
+ /** Current List ID */
+ private static final byte[] listID = "listid".getBytes();
+
+ /** List override */
+ private static final byte[] listOverride = "listoverride".getBytes();
+
+ /** Number of overrides */
+ private static final byte[] listOverrideCount = "listoverridecount".getBytes();
+
+ /**
+ * Text Style
+ */
+
+ /** Bold tag. */
+ protected static final byte bold = (byte) 'b';
+
+ /** Italic tag. */
+ protected static final byte italic = (byte) 'i';
+
+ /** Underline tag. */
+ protected static final byte[] underline = "ul".getBytes();
+
+ /** Strikethrough tag. */
+ protected static final byte[] strikethrough = "strike".getBytes();
+
+ /** Text alignment left tag. */
+ public static final byte[] alignLeft = "ql".getBytes();
+
+ /** Text alignment center tag. */
+ public static final byte[] alignCenter = "qc".getBytes();
+
+ /** Text alignment right tag. */
+ public static final byte[] alignRight = "qr".getBytes();
+
+ /** Text alignment justify tag. */
+ public static final byte[] alignJustify = "qj".getBytes();
+
+ /**
+ * Colors
+ */
+
+ /** Begin colour table tag. */
+ private static final byte[] colorTable = "colortbl".getBytes();
+
+ /** Red value tag. */
+ private static final byte[] colorRed = "red".getBytes();
+
+ /** Green value tag. */
+ private static final byte[] colorGreen = "green".getBytes();
+
+ /** Blue value tag. */
+ private static final byte[] colorBlue = "blue".getBytes();
+
+ /**
+ * Information Group
+ */
+
+ /** Begin the info group tag.*/
+ private static final byte[] infoBegin = "info".getBytes();
+
+ /** Author tag. */
+ private static final byte[] metaAuthor = "author".getBytes();
+
+ /** Subject tag. */
+ private static final byte[] metaSubject = "subject".getBytes();
+
+ /** Keywords tag. */
+ private static final byte[] metaKeywords = "keywords".getBytes();
+
+ /** Title tag. */
+ private static final byte[] metaTitle = "title".getBytes();
+
+ /** Producer tag. */
+ private static final byte[] metaProducer = "operator".getBytes();
+
+ /** Creation Date tag. */
+ private static final byte[] metaCreationDate = "creationdate".getBytes();
+
+ /** Year tag. */
+ private static final byte[] year = "yr".getBytes();
+
+ /** Month tag. */
+ private static final byte[] month = "mo".getBytes();
+
+ /** Day tag. */
+ private static final byte[] day = "dy".getBytes();
+
+ /** Hour tag. */
+ private static final byte[] hour = "hr".getBytes();
+
+ /** Minute tag. */
+ private static final byte[] minute = "min".getBytes();
+
+ /** Second tag. */
+ private static final byte[] second = "sec".getBytes();
+
+ /** Start superscript. */
+ private static final byte[] startSuper = "super".getBytes();
+
+ /** Start subscript. */
+ private static final byte[] startSub = "sub".getBytes();
+
+ /** End super/sub script. */
+ private static final byte[] endSuperSub = "nosupersub".getBytes();
+
+ /**
+ * Header / Footer
+ */
+
+ /** Title Page tag */
+ private static final byte[] titlePage = "titlepg".getBytes();
+
+ /** Facing pages tag */
+ private static final byte[] facingPages = "facingp".getBytes();
+
+ /** Begin header group tag. */
+ private static final byte[] headerBegin = "header".getBytes();
+
+ /** Begin footer group tag. */
+ private static final byte[] footerBegin = "footer".getBytes();
+
+ // header footer 'left', 'right', 'first'
+ private static final byte[] headerlBegin = "headerl".getBytes();
+
+ private static final byte[] footerlBegin = "footerl".getBytes();
+
+ private static final byte[] headerrBegin = "headerr".getBytes();
+
+ private static final byte[] footerrBegin = "footerr".getBytes();
+
+ private static final byte[] headerfBegin = "headerf".getBytes();
+
+ private static final byte[] footerfBegin = "footerf".getBytes();
+
+ /**
+ * Paper Properties
+ */
+
+ /** Paper width tag. */
+ private static final byte[] rtfPaperWidth = "paperw".getBytes();
+
+ /** Paper height tag. */
+ private static final byte[] rtfPaperHeight = "paperh".getBytes();
+
+ /** Margin left tag. */
+ private static final byte[] rtfMarginLeft = "margl".getBytes();
+
+ /** Margin right tag. */
+ private static final byte[] rtfMarginRight = "margr".getBytes();
+
+ /** Margin top tag. */
+ private static final byte[] rtfMarginTop = "margt".getBytes();
+
+ /** Margin bottom tag. */
+ private static final byte[] rtfMarginBottom = "margb".getBytes();
+
+ /** New Page tag. */
+ private static final byte[] newPage = "page".getBytes();
+
+ /** Document Landscape tag 1. */
+ private static final byte[] landscapeTag1 = "landscape".getBytes();
+
+ /** Document Landscape tag 2. */
+ private static final byte[] landscapeTag2 = "lndscpsxn".getBytes();
+
+ /**
+ * Annotations
+ */
+
+ /** Annotation ID tag. */
+ private static final byte[] annotationID = "atnid".getBytes();
+
+ /** Annotation Author tag. */
+ private static final byte[] annotationAuthor = "atnauthor".getBytes();
+
+ /** Annotation text tag. */
+ private static final byte[] annotation = "annotation".getBytes();
+
+ /**
+ * Images
+ */
+
+ /** Begin the main Picture group tag */
+ private static final byte[] pictureGroup = "shppict".getBytes();
+
+ /** Begin the picture tag */
+ private static final byte[] picture = "pict".getBytes();
+
+ /** PNG Image */
+ private static final byte[] picturePNG = "pngblip".getBytes();
+
+ /** JPEG Image */
+ private static final byte[] pictureJPEG = "jpegblip".getBytes();
+
+ /** BMP Image */
+ private static final byte[] pictureBMP = "dibitmap0".getBytes();
+
+ /** WMF Image */
+ private static final byte[] pictureWMF = "wmetafile8".getBytes();
+
+ /** Picture width */
+ private static final byte[] pictureWidth = "picw".getBytes();
+
+ /** Picture height */
+ private static final byte[] pictureHeight = "pich".getBytes();
+
+ /** Picture scale horizontal percent */
+ private static final byte[] pictureScaleX = "picscalex".getBytes();
+
+ /** Picture scale vertical percent */
+ private static final byte[] pictureScaleY = "picscaley".getBytes();
+
+ /**
+ * Fields (for page numbering)
+ */
+
+ /** Begin field tag */
+ protected static final byte[] field = "field".getBytes();
+
+ /** Content fo the field */
+ protected static final byte[] fieldContent = "fldinst".getBytes();
+
+ /** PAGE numbers */
+ protected static final byte[] fieldPage = "PAGE".getBytes();
+
+ /** HYPERLINK field */
+ protected static final byte[] fieldHyperlink = "HYPERLINK".getBytes();
+
+ /** Last page number (not used) */
+ protected static final byte[] fieldDisplay = "fldrslt".getBytes();
+
+
+ /** Class variables */
+
+ /**
+ * Because of the way RTF works and the way itext works, the text has to be
+ * stored and is only written to the actual OutputStream at the end.
+ */
+
+ /** This ArrayList
contains all fonts used in the document. */
+ private ArrayList fontList = new ArrayList();
+
+ /** This ArrayList
contains all colours used in the document. */
+ private ArrayList colorList = new ArrayList();
+
+ /** This ByteArrayOutputStream
contains the main body of the document. */
+ private ByteArrayOutputStream content = null;
+
+ /** This ByteArrayOutputStream
contains the information group. */
+ private ByteArrayOutputStream info = null;
+
+ /** This ByteArrayOutputStream
contains the list table. */
+ private ByteArrayOutputStream listtable = null;
+
+ /** This ByteArrayOutputStream
contains the list override table. */
+ private ByteArrayOutputStream listoverride = null;
+
+ /** Document header. */
+ private HeaderFooter header = null;
+
+ /** Document footer. */
+ private HeaderFooter footer = null;
+
+ /** Left margin. */
+ private int marginLeft = 1800;
+
+ /** Right margin. */
+ private int marginRight = 1800;
+
+ /** Top margin. */
+ private int marginTop = 1440;
+
+ /** Bottom margin. */
+ private int marginBottom = 1440;
+
+ /** Page width. */
+ private int pageWidth = 11906;
+
+ /** Page height. */
+ private int pageHeight = 16838;
+
+ /** Factor to use when converting. */
+ public final static double TWIPSFACTOR = 20;//20.57140;
+
+ /** Current list ID. */
+ private int currentListID = 1;
+
+ /** List of current Lists. */
+ private ArrayList listIds = null;
+
+ /** Current List Level. */
+ private int listLevel = 0;
+
+ /** Current maximum List Level. */
+ private int maxListLevel = 0;
+
+ /** Write a TOC */
+ private boolean writeTOC = false;
+
+ /** Special title page */
+ private boolean hasTitlePage = false;
+
+ /** Currently writing either Header or Footer */
+ private boolean inHeaderFooter = false;
+
+ /** Currently writing a Table */
+ private boolean inTable = false;
+
+ /** Landscape or Portrait Document */
+ private boolean landscape = false;
+
+ /** Protected Constructor */
+
+ /**
+ * Constructs a RtfWriter
.
+ *
+ * @param doc The Document
that is to be written as RTF
+ * @param os The OutputStream
the writer has to write to.
+ */
+
+ protected RtfWriter(Document doc, OutputStream os) {
+ super(doc, os);
+ document.addDocListener(this);
+ initDefaults();
+ }
+
+ /** Public functions special to the RtfWriter */
+
+ /**
+ * This method controls whether TOC entries are automatically generated
+ *
+ * @param writeTOC boolean value indicating whether a TOC is to be generated
+ */
+ public void setGenerateTOCEntries(boolean writeTOC) {
+ this.writeTOC = writeTOC;
+ }
+
+ /**
+ * Gets the current setting of writeTOC
+ *
+ * @return boolean value indicating whether a TOC is being generated
+ */
+ public boolean getGeneratingTOCEntries() {
+ return writeTOC;
+ }
+
+ /**
+ * This method controls whether the first page is a title page
+ *
+ * @param hasTitlePage boolean value indicating whether the first page is a title page
+ */
+ public void setHasTitlePage(boolean hasTitlePage) {
+ this.hasTitlePage = hasTitlePage;
+ }
+
+ /**
+ * Gets the current setting of hasTitlePage
+ *
+ * @return boolean value indicating whether the first page is a title page
+ */
+ public boolean getHasTitlePage() {
+ return hasTitlePage;
+ }
+
+ /**
+ * Explicitly sets the page format to use.
+ * Otherwise the RtfWriter will try to guess the format by comparing pagewidth and pageheight
+ *
+ * @param landscape boolean value indicating whether we are using landscape format or not
+ */
+ public void setLandscape(boolean landscape) {
+ this.landscape = landscape;
+ }
+
+ /**
+ * Returns the current landscape setting
+ *
+ * @return boolean value indicating the current page format
+ */
+ public boolean getLandscape() {
+ return landscape;
+ }
+
+ /** Public functions from the DocWriter Interface */
+
+ /**
+ * Gets an instance of the RtfWriter
.
+ *
+ * @param document The Document
that has to be written
+ * @param os The OutputStream
the writer has to write to.
+ * @return a new RtfWriter
+ */
+ public static RtfWriter getInstance(Document document, OutputStream os) {
+ return (new RtfWriter(document, os));
+ }
+
+ /**
+ * Signals that the Document
has been opened and that
+ * Elements
can be added.
+ */
+ public void open() {
+ super.open();
+ }
+
+ /**
+ * Signals that the Document
was closed and that no other
+ * Elements
will be added.
+ * OutputStream
+ */
+ public void close() {
+ writeDocument();
+ super.close();
+ }
+
+ /**
+ * Adds the footer to the bottom of the Document
.
+ * @param footer
+ */
+ public void setFooter(HeaderFooter footer) {
+ this.footer = footer;
+ processHeaderFooter(this.footer);
+ }
+
+ /**
+ * Adds the header to the top of the Document
.
+ * @param header
+ */
+ public void setHeader(HeaderFooter header) {
+ this.header = header;
+ processHeaderFooter(this.header);
+ }
+
+ /**
+ * Resets the footer.
+ */
+ public void resetFooter() {
+ setFooter(null);
+ }
+
+ /**
+ * Resets the header.
+ */
+ public void resetHeader() {
+ setHeader(null);
+ }
+
+ /**
+ * Tells the RtfWriter
that a new page is to be begun.
+ *
+ * @return true
if a new Page was begun.
+ * @throws DocumentException if the Document was not open or had been closed.
+ */
+ public boolean newPage() throws DocumentException {
+ try {
+ content.write(escape);
+ content.write(newPage);
+ content.write(escape);
+ content.write(paragraph);
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Sets the page margins
+ *
+ * @param marginLeft The left margin
+ * @param marginRight The right margin
+ * @param marginTop The top margin
+ * @param marginBottom The bottom margin
+ *
+ * @return true
if the page margins were set.
+ */
+ public boolean setMargins(float marginLeft, float marginRight, float marginTop, float marginBottom) {
+ this.marginLeft = (int) (marginLeft * TWIPSFACTOR);
+ this.marginRight = (int) (marginRight * TWIPSFACTOR);
+ this.marginTop = (int) (marginTop * TWIPSFACTOR);
+ this.marginBottom = (int) (marginBottom * TWIPSFACTOR);
+ return true;
+ }
+
+ /**
+ * Sets the page size
+ *
+ * @param pageSize A Rectangle
specifying the page size
+ *
+ * @return true
if the page size was set
+ */
+ public boolean setPageSize(Rectangle pageSize) {
+ if (!parseFormat(pageSize, false)) {
+ pageWidth = (int) (pageSize.width() * TWIPSFACTOR);
+ pageHeight = (int) (pageSize.height() * TWIPSFACTOR);
+ landscape = pageWidth > pageHeight;
+ }
+ return true;
+ }
+
+ /**
+ * Write the table of contents.
+ *
+ * @param tocTitle The title that will be displayed above the TOC
+ * @param titleFont The Font
that will be used for the tocTitle
+ * @param showTOCasEntry Set this to true if you want the TOC to appear as an entry in the TOC
+ * @param showTOCEntryFont Use this Font
to specify what Font to use when showTOCasEntry is true
+ *
+ * @return true
if the TOC was added.
+ */
+ public boolean writeTOC(String tocTitle, Font titleFont, boolean showTOCasEntry, Font showTOCEntryFont) {
+ try {
+ RtfTOC toc = new RtfTOC(tocTitle, titleFont);
+ if (showTOCasEntry) {
+ toc.addTOCAsTOCEntry(tocTitle, showTOCEntryFont);
+ }
+ add(new Paragraph(toc));
+ } catch (DocumentException de) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Signals that an Element
was added to the Document
.
+ *
+ * @param element A high level object to add
+ * @return true
if the element was added, false
if not.
+ * @throws DocumentException if a document isn't open yet, or has been closed
+ */
+ public boolean add(Element element) throws DocumentException {
+ if (pause) {
+ return false;
+ }
+ return addElement(element, content);
+ }
+
+
+ /** Private functions */
+
+ /**
+ * Adds an Element
to the Document
.
+ * @param element the high level element to add
+ * @param out the outputstream to which the RTF data is sent
+ * @return true
if the element was added, false
if not.
+ * @throws DocumentException if a document isn't open yet, or has been closed
+ */
+ protected boolean addElement(Element element, ByteArrayOutputStream out) throws DocumentException {
+ try {
+ switch (element.type()) {
+ case Element.CHUNK:
+ writeChunk((Chunk) element, out);
+ break;
+ case Element.PARAGRAPH:
+ writeParagraph((Paragraph) element, out);
+ break;
+ case Element.ANCHOR:
+ writeAnchor((Anchor) element, out);
+ break;
+ case Element.PHRASE:
+ writePhrase((Phrase) element, out);
+ break;
+ case Element.CHAPTER:
+ case Element.SECTION:
+ writeSection((Section) element, out);
+ break;
+ case Element.LIST:
+ writeList((com.lowagie.text.List) element, out);
+ break;
+ case Element.TABLE:
+ try {
+ writeTable((Table) element, out);
+ }
+ catch(ClassCastException cce) {
+ writeTable(((SimpleTable)element).createTable(), out);
+ }
+ break;
+ case Element.ANNOTATION:
+ writeAnnotation((Annotation) element, out);
+ break;
+ case Element.IMGRAW:
+ case Element.IMGTEMPLATE:
+ case Element.JPEG:
+ Image img = (Image)element;
+ writeImage(img, out);
+ break;
+
+ case Element.AUTHOR:
+ writeMeta(metaAuthor, (Meta) element);
+ break;
+ case Element.SUBJECT:
+ writeMeta(metaSubject, (Meta) element);
+ break;
+ case Element.KEYWORDS:
+ writeMeta(metaKeywords, (Meta) element);
+ break;
+ case Element.TITLE:
+ writeMeta(metaTitle, (Meta) element);
+ break;
+ case Element.PRODUCER:
+ writeMeta(metaProducer, (Meta) element);
+ break;
+ case Element.CREATIONDATE:
+ writeMeta(metaCreationDate, (Meta) element);
+ break;
+ }
+ } catch (IOException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Write the beginning of a new Section
+ *
+ * @param sectionElement The Section
be written
+ * @param out The ByteArrayOutputStream
to write to
+ *
+ * @throws IOException
+ * @throws DocumentException
+ */
+ private void writeSection(Section sectionElement, ByteArrayOutputStream out) throws IOException, DocumentException {
+ if (sectionElement.type() == Element.CHAPTER) {
+ out.write(escape);
+ out.write(sectionDefaults);
+ writeSectionDefaults(out);
+ }
+ if (sectionElement.title() != null) {
+ if (writeTOC) {
+ StringBuffer title = new StringBuffer("");
+ for (ListIterator li = sectionElement.title().getChunks().listIterator(); li.hasNext();) {
+ title.append(((Chunk) li.next()).content());
+ }
+ add(new RtfTOCEntry(title.toString(), sectionElement.title().font()));
+ } else {
+ add(sectionElement.title());
+ }
+ out.write(escape);
+ out.write(paragraph);
+ }
+ sectionElement.process(this);
+ if (sectionElement.type() == Element.CHAPTER) {
+ out.write(escape);
+ out.write(section);
+ }
+ if (sectionElement.type() == Element.SECTION) {
+ out.write(escape);
+ out.write(paragraph);
+ }
+ }
+
+ /**
+ * Write the beginning of a new Paragraph
+ *
+ * @param paragraphElement The Paragraph
to be written
+ * @param out The ByteArrayOutputStream
to write to
+ *
+ * @throws IOException
+ */
+ private void writeParagraph(Paragraph paragraphElement, ByteArrayOutputStream out) throws IOException {
+ out.write(escape);
+ out.write(paragraphDefaults);
+ if (inTable) {
+ out.write(escape);
+ out.write(RtfCell.cellInTable);
+ }
+ switch (paragraphElement.alignment()) {
+ case Element.ALIGN_LEFT:
+ out.write(escape);
+ out.write(alignLeft);
+ break;
+ case Element.ALIGN_RIGHT:
+ out.write(escape);
+ out.write(alignRight);
+ break;
+ case Element.ALIGN_CENTER:
+ out.write(escape);
+ out.write(alignCenter);
+ break;
+ case Element.ALIGN_JUSTIFIED:
+ case Element.ALIGN_JUSTIFIED_ALL:
+ out.write(escape);
+ out.write(alignJustify);
+ break;
+ }
+ out.write(escape);
+ out.write(listIndent);
+ writeInt(out, (int) (paragraphElement.indentationLeft() * TWIPSFACTOR));
+ out.write(escape);
+ out.write(rightIndent);
+ writeInt(out, (int) (paragraphElement.indentationRight() * TWIPSFACTOR));
+ Iterator chunks = paragraphElement.getChunks().iterator();
+ while (chunks.hasNext()) {
+ Chunk ch = (Chunk) chunks.next();
+ ch.setFont(paragraphElement.font().difference(ch.font()));
+ }
+ ByteArrayOutputStream save = content;
+ content = out;
+ paragraphElement.process(this);
+ content = save;
+ if (!inTable) {
+ out.write(escape);
+ out.write(paragraph);
+ }
+ }
+
+ /**
+ * Write a Phrase
.
+ *
+ * @param phrase The Phrase
item to be written
+ * @param out The ByteArrayOutputStream
to write to
+ *
+ * @throws IOException
+ */
+ private void writePhrase(Phrase phrase, ByteArrayOutputStream out) throws IOException {
+ out.write(escape);
+ out.write(paragraphDefaults);
+ if (inTable) {
+ out.write(escape);
+ out.write(RtfCell.cellInTable);
+ }
+ Iterator chunks = phrase.getChunks().iterator();
+ while (chunks.hasNext()) {
+ Chunk ch = (Chunk) chunks.next();
+ ch.setFont(phrase.font().difference(ch.font()));
+ }
+ ByteArrayOutputStream save = content;
+ content = out;
+ phrase.process(this);
+ content = save;
+ }
+
+ /**
+ * Write an Anchor
. Anchors are treated like Phrases.
+ *
+ * @param anchor The Chunk
item to be written
+ * @param out The ByteArrayOutputStream
to write to
+ *
+ * @throws IOException
+ */
+ private void writeAnchor(Anchor anchor, ByteArrayOutputStream out) throws IOException {
+ if (anchor.url() != null) {
+ out.write(openGroup);
+ out.write(escape);
+ out.write(field);
+ out.write(openGroup);
+ out.write(extendedEscape);
+ out.write(fieldContent);
+ out.write(openGroup);
+ out.write(fieldHyperlink);
+ out.write(delimiter);
+ out.write(anchor.url().toString().getBytes());
+ out.write(closeGroup);
+ out.write(closeGroup);
+ out.write(openGroup);
+ out.write(escape);
+ out.write(fieldDisplay);
+ out.write(delimiter);
+ writePhrase(anchor, out);
+ out.write(closeGroup);
+ out.write(closeGroup);
+ } else {
+ writePhrase(anchor, out);
+ }
+ }
+
+ /**
+ * Write a Chunk
and all its font properties.
+ *
+ * @param chunk The Chunk
item to be written
+ * @param out The ByteArrayOutputStream
to write to
+ *
+ * @throws IOException
+ * @throws DocumentException
+ */
+ private void writeChunk(Chunk chunk, ByteArrayOutputStream out) throws IOException, DocumentException {
+ if (chunk instanceof RtfField) {
+ ((RtfField) chunk).write(this, out);
+ } else {
+ if (chunk.getImage() != null) {
+ writeImage(chunk.getImage(), out);
+ } else {
+ writeInitialFontSignature(out, chunk);
+ out.write(filterSpecialChar(chunk.content(), false).getBytes());
+ writeFinishingFontSignature(out, chunk);
+ }
+ }
+ }
+
+
+ protected void writeInitialFontSignature(OutputStream out, Chunk chunk) throws IOException {
+ Font font = chunk.font();
+
+ out.write(escape);
+ out.write(fontNumber);
+ if (!font.getFamilyname().equalsIgnoreCase("unknown")) {
+ writeInt(out, addFont(font));
+ } else {
+ writeInt(out, 0);
+ }
+ out.write(escape);
+ out.write(fontSize);
+ if (font.size() > 0) {
+ writeInt(out, (int) (font.size() * 2));
+ } else {
+ writeInt(out, 20);
+ }
+ out.write(escape);
+ out.write(fontColor);
+ writeInt(out, addColor(font.color()));
+ if (font.isBold()) {
+ out.write(escape);
+ out.write(bold);
+ }
+ if (font.isItalic()) {
+ out.write(escape);
+ out.write(italic);
+ }
+ if (font.isUnderlined()) {
+ out.write(escape);
+ out.write(underline);
+ }
+ if (font.isStrikethru()) {
+ out.write(escape);
+ out.write(strikethrough);
+ }
+
+ /*
+ * Superscript / Subscript added by Scott Dietrich (sdietrich@emlab.com)
+ */
+ if (chunk.getAttributes() != null) {
+ Float f = (Float) chunk.getAttributes().get(Chunk.SUBSUPSCRIPT);
+ if (f != null)
+ if (f.floatValue() > 0) {
+ out.write(escape);
+ out.write(startSuper);
+ } else if (f.floatValue() < 0) {
+ out.write(escape);
+ out.write(startSub);
+ }
+ }
+
+ out.write(delimiter);
+ }
+
+
+ protected void writeFinishingFontSignature(OutputStream out, Chunk chunk) throws IOException {
+ Font font = chunk.font();
+
+ if (font.isBold()) {
+ out.write(escape);
+ out.write(bold);
+ writeInt(out, 0);
+ }
+ if (font.isItalic()) {
+ out.write(escape);
+ out.write(italic);
+ writeInt(out, 0);
+ }
+ if (font.isUnderlined()) {
+ out.write(escape);
+ out.write(underline);
+ writeInt(out, 0);
+ }
+ if (font.isStrikethru()) {
+ out.write(escape);
+ out.write(strikethrough);
+ writeInt(out, 0);
+ }
+
+ /*
+ * Superscript / Subscript added by Scott Dietrich (sdietrich@emlab.com)
+ */
+ if (chunk.getAttributes() != null) {
+ Float f = (Float) chunk.getAttributes().get(Chunk.SUBSUPSCRIPT);
+ if (f != null)
+ if (f.floatValue() != 0) {
+ out.write(escape);
+ out.write(endSuperSub);
+ }
+ }
+ }
+
+ /**
+ * Write a ListItem
+ *
+ * @param listItem The ListItem
to be written
+ * @param out The ByteArrayOutputStream
to write to
+ *
+ * @throws IOException
+ * @throws DocumentException
+ */
+ private void writeListElement(ListItem listItem, ByteArrayOutputStream out) throws IOException, DocumentException {
+ Iterator chunks = listItem.getChunks().iterator();
+ while (chunks.hasNext()) {
+ Chunk ch = (Chunk) chunks.next();
+ addElement(ch, out);
+ }
+ out.write(escape);
+ out.write(paragraph);
+ }
+
+ /**
+ * Write a List
+ *
+ * @param list The List
to be written
+ * @param out The ByteArrayOutputStream
to write to
+ *
+ * @throws IOException
+ * @throws DocumentException
+ */
+ private void writeList(com.lowagie.text.List list, ByteArrayOutputStream out) throws IOException, DocumentException {
+ int type = 0;
+ int align = 0;
+ int fontNr = addFont(new Font(Font.SYMBOL, 10, Font.NORMAL, new Color(0, 0, 0)));
+ if (!list.isNumbered()) type = 23;
+ if (listLevel == 0) {
+ maxListLevel = 0;
+ listtable.write(openGroup);
+ listtable.write(escape);
+ listtable.write(listDefinition);
+ int i = getRandomInt();
+ listtable.write(escape);
+ listtable.write(listTemplateID);
+ writeInt(listtable, i);
+ listtable.write(escape);
+ listtable.write(hybridList);
+ listtable.write((byte) '\n');
+ }
+ if (listLevel >= maxListLevel) {
+ maxListLevel++;
+ listtable.write(openGroup);
+ listtable.write(escape);
+ listtable.write(listLevelDefinition);
+ listtable.write(escape);
+ listtable.write(listLevelTypeOld);
+ writeInt(listtable, type);
+ listtable.write(escape);
+ listtable.write(listLevelTypeNew);
+ writeInt(listtable, type);
+ listtable.write(escape);
+ listtable.write(listLevelAlignOld);
+ writeInt(listtable, align);
+ listtable.write(escape);
+ listtable.write(listLevelAlignNew);
+ writeInt(listtable, align);
+ listtable.write(escape);
+ listtable.write(listLevelStartAt);
+ writeInt(listtable, 1);
+ listtable.write(openGroup);
+ listtable.write(escape);
+ listtable.write(listLevelTextDefinition);
+ listtable.write(escape);
+ listtable.write(listLevelTextLength);
+ if (list.isNumbered()) {
+ writeInt(listtable, 2);
+ } else {
+ writeInt(listtable, 1);
+ }
+ listtable.write(escape);
+ if (list.isNumbered()) {
+ listtable.write(listLevelTextStyleNumbers);
+ } else {
+ listtable.write(listLevelTextStyleBullet);
+ }
+ listtable.write(commaDelimiter);
+ listtable.write(closeGroup);
+ listtable.write(openGroup);
+ listtable.write(escape);
+ listtable.write(listLevelNumbersDefinition);
+ if (list.isNumbered()) {
+ listtable.write(delimiter);
+ listtable.write(listLevelNumbers);
+ writeInt(listtable, listLevel + 1);
+ }
+ listtable.write(commaDelimiter);
+ listtable.write(closeGroup);
+ if (!list.isNumbered()) {
+ listtable.write(escape);
+ listtable.write(fontNumber);
+ writeInt(listtable, fontNr);
+ }
+ listtable.write(escape);
+ listtable.write(firstIndent);
+ writeInt(listtable, (int) (list.indentationLeft() * TWIPSFACTOR * -1));
+ listtable.write(escape);
+ listtable.write(listIndent);
+ writeInt(listtable, (int) ((list.indentationLeft() + list.symbolIndent()) * TWIPSFACTOR));
+ listtable.write(escape);
+ listtable.write(rightIndent);
+ writeInt(listtable, (int) (list.indentationRight() * TWIPSFACTOR));
+ listtable.write(escape);
+ listtable.write(tabStop);
+ writeInt(listtable, (int) (list.symbolIndent() * TWIPSFACTOR));
+ listtable.write(closeGroup);
+ listtable.write((byte) '\n');
+ }
+ // Actual List Begin in Content
+ out.write(escape);
+ out.write(paragraphDefaults);
+ out.write(escape);
+ out.write(alignLeft);
+ out.write(escape);
+ out.write(firstIndent);
+ writeInt(out, (int) (list.indentationLeft() * TWIPSFACTOR * -1));
+ out.write(escape);
+ out.write(listIndent);
+ writeInt(out, (int) ((list.indentationLeft() + list.symbolIndent()) * TWIPSFACTOR));
+ out.write(escape);
+ out.write(rightIndent);
+ writeInt(out, (int) (list.indentationRight() * TWIPSFACTOR));
+ out.write(escape);
+ out.write(fontSize);
+ writeInt(out, 20);
+ out.write(escape);
+ out.write(listBegin);
+ writeInt(out, currentListID);
+ if (listLevel > 0) {
+ out.write(escape);
+ out.write(listCurrentLevel);
+ writeInt(out, listLevel);
+ }
+ out.write(openGroup);
+ ListIterator listItems = list.getItems().listIterator();
+ Element listElem;
+ int count = 1;
+ while (listItems.hasNext()) {
+ listElem = (Element) listItems.next();
+ if (listElem.type() == Element.CHUNK) {
+ listElem = new ListItem((Chunk) listElem);
+ }
+ if (listElem.type() == Element.LISTITEM) {
+ out.write(openGroup);
+ out.write(escape);
+ out.write(listTextOld);
+ out.write(escape);
+ out.write(paragraphDefaults);
+ out.write(escape);
+ out.write(fontNumber);
+ if (list.isNumbered()) {
+ writeInt(out, addFont(new Font(Font.TIMES_ROMAN, Font.NORMAL, 10, new Color(0, 0, 0))));
+ } else {
+ writeInt(out, fontNr);
+ }
+ out.write(escape);
+ out.write(firstIndent);
+ writeInt(out, (int) (list.indentationLeft() * TWIPSFACTOR * -1));
+ out.write(escape);
+ out.write(listIndent);
+ writeInt(out, (int) ((list.indentationLeft() + list.symbolIndent()) * TWIPSFACTOR));
+ out.write(escape);
+ out.write(rightIndent);
+ writeInt(out, (int) (list.indentationRight() * TWIPSFACTOR));
+ out.write(delimiter);
+ if (list.isNumbered()) {
+ writeInt(out, count);
+ out.write(".".getBytes());
+ } else {
+ out.write(escape);
+ out.write(listBulletOld);
+ }
+ out.write(escape);
+ out.write(tab);
+ out.write(closeGroup);
+ writeListElement((ListItem) listElem, out);
+ count++;
+ } else if (listElem.type() == Element.LIST) {
+ listLevel++;
+ writeList((com.lowagie.text.List) listElem, out);
+ listLevel--;
+ out.write(escape);
+ out.write(paragraphDefaults);
+ out.write(escape);
+ out.write(alignLeft);
+ out.write(escape);
+ out.write(firstIndent);
+ writeInt(out, (int) (list.indentationLeft() * TWIPSFACTOR * -1));
+ out.write(escape);
+ out.write(listIndent);
+ writeInt(out, (int) ((list.indentationLeft() + list.symbolIndent()) * TWIPSFACTOR));
+ out.write(escape);
+ out.write(rightIndent);
+ writeInt(out, (int) (list.indentationRight() * TWIPSFACTOR));
+ out.write(escape);
+ out.write(fontSize);
+ writeInt(out, 20);
+ out.write(escape);
+ out.write(listBegin);
+ writeInt(out, currentListID);
+ if (listLevel > 0) {
+ out.write(escape);
+ out.write(listCurrentLevel);
+ writeInt(out, listLevel);
+ }
+ }
+ out.write((byte) '\n');
+ }
+ out.write(closeGroup);
+ if (listLevel == 0) {
+ int i = getRandomInt();
+ listtable.write(escape);
+ listtable.write(listID);
+ writeInt(listtable, i);
+ listtable.write(closeGroup);
+ listtable.write((byte) '\n');
+ listoverride.write(openGroup);
+ listoverride.write(escape);
+ listoverride.write(listOverride);
+ listoverride.write(escape);
+ listoverride.write(listID);
+ writeInt(listoverride, i);
+ listoverride.write(escape);
+ listoverride.write(listOverrideCount);
+ writeInt(listoverride, 0);
+ listoverride.write(escape);
+ listoverride.write(listBegin);
+ writeInt(listoverride, currentListID);
+ currentListID++;
+ listoverride.write(closeGroup);
+ listoverride.write((byte) '\n');
+ }
+ out.write(escape);
+ out.write(paragraphDefaults);
+ }
+
+ /**
+ * Write a Table
.
+ *
+ * @param table The table
to be written
+ * @param out The ByteArrayOutputStream
to write to
+ *
+ * Currently no nesting of tables is supported. If a cell contains anything but a Cell Object it is ignored.
+ *
+ * @throws IOException
+ * @throws DocumentException
+ */
+ private void writeTable(Table table, ByteArrayOutputStream out) throws IOException, DocumentException {
+ inTable = true;
+ table.complete();
+ RtfTable rtfTable = new RtfTable(this);
+ rtfTable.importTable(table, pageWidth - marginLeft - marginRight);
+ rtfTable.writeTable(out);
+ inTable = false;
+ }
+
+
+ /**
+ * Write an Image
.
+ *
+ * @param image The image
to be written
+ * @param out The ByteArrayOutputStream
to write to
+ *
+ * At the moment only PNG and JPEG Images are supported.
+ *
+ * @throws IOException
+ * @throws DocumentException
+ */
+ private void writeImage(Image image, ByteArrayOutputStream out) throws IOException, DocumentException {
+ int type = image.getOriginalType();
+ if (!(type == Image.ORIGINAL_JPEG || type == Image.ORIGINAL_BMP
+ || type == Image.ORIGINAL_PNG || type == Image.ORIGINAL_WMF))
+ throw new DocumentException("Only BMP, PNG, WMF and JPEG images are supported by the RTF Writer");
+ switch (image.alignment()) {
+ case Element.ALIGN_LEFT:
+ out.write(escape);
+ out.write(alignLeft);
+ break;
+ case Element.ALIGN_RIGHT:
+ out.write(escape);
+ out.write(alignRight);
+ break;
+ case Element.ALIGN_CENTER:
+ out.write(escape);
+ out.write(alignCenter);
+ break;
+ case Element.ALIGN_JUSTIFIED:
+ out.write(escape);
+ out.write(alignJustify);
+ break;
+ }
+ out.write(openGroup);
+ out.write(extendedEscape);
+ out.write(pictureGroup);
+ out.write(openGroup);
+ out.write(escape);
+ out.write(picture);
+ out.write(escape);
+ switch (type) {
+ case Image.ORIGINAL_JPEG:
+ out.write(pictureJPEG);
+ break;
+ case Image.ORIGINAL_PNG:
+ out.write(picturePNG);
+ break;
+ case Image.ORIGINAL_WMF:
+ case Image.ORIGINAL_BMP:
+ out.write(pictureWMF);
+ break;
+ }
+ out.write(escape);
+ out.write(pictureWidth);
+ writeInt(out, (int) (image.plainWidth() * TWIPSFACTOR));
+ out.write(escape);
+ out.write(pictureHeight);
+ writeInt(out, (int) (image.plainHeight() * TWIPSFACTOR));
+
+
+// For some reason this messes up the intended image size. It makes it too big. Weird
+//
+// out.write(escape);
+// out.write(pictureIntendedWidth);
+// writeInt(out, (int) (image.plainWidth() * twipsFactor));
+// out.write(escape);
+// out.write(pictureIntendedHeight);
+// writeInt(out, (int) (image.plainHeight() * twipsFactor));
+
+
+ if (image.width() > 0) {
+ out.write(escape);
+ out.write(pictureScaleX);
+ writeInt(out, (int) (100 / image.width() * image.plainWidth()));
+ }
+ if (image.height() > 0) {
+ out.write(escape);
+ out.write(pictureScaleY);
+ writeInt(out, (int) (100 / image.height() * image.plainHeight()));
+ }
+ out.write(delimiter);
+ InputStream imgIn;
+ if (type == Image.ORIGINAL_BMP) {
+ imgIn = new ByteArrayInputStream(MetaDo.wrapBMP(image));
+ }
+ else {
+ if (image.getOriginalData() == null) {
+ imgIn = image.url().openStream();
+ } else {
+ imgIn = new ByteArrayInputStream(image.getOriginalData());
+ }
+ if (type == Image.ORIGINAL_WMF) { //remove the placeable header
+ long skipLength = 22;
+ while(skipLength > 0) {
+ skipLength = skipLength - imgIn.skip(skipLength);
+ }
+ }
+ }
+ int buffer = -1;
+ int count = 0;
+ out.write((byte) '\n');
+ while ((buffer = imgIn.read()) != -1) {
+ String helperStr = Integer.toHexString(buffer);
+ if (helperStr.length() < 2) helperStr = "0" + helperStr;
+ out.write(helperStr.getBytes());
+ count++;
+ if (count == 64) {
+ out.write((byte) '\n');
+ count = 0;
+ }
+ }
+ imgIn.close();
+ out.write(closeGroup);
+ out.write(closeGroup);
+ out.write((byte) '\n');
+ }
+
+ /**
+ * Write an Annotation
+ *
+ * @param annotationElement The Annotation
to be written
+ * @param out The ByteArrayOutputStream
to write to
+ *
+ * @throws IOException
+ */
+ private void writeAnnotation(Annotation annotationElement, ByteArrayOutputStream out) throws IOException {
+ int id = getRandomInt();
+ out.write(openGroup);
+ out.write(extendedEscape);
+ out.write(annotationID);
+ out.write(delimiter);
+ writeInt(out, id);
+ out.write(closeGroup);
+ out.write(openGroup);
+ out.write(extendedEscape);
+ out.write(annotationAuthor);
+ out.write(delimiter);
+ out.write(annotationElement.title().getBytes());
+ out.write(closeGroup);
+ out.write(openGroup);
+ out.write(extendedEscape);
+ out.write(annotation);
+ out.write(escape);
+ out.write(paragraphDefaults);
+ out.write(delimiter);
+ out.write(annotationElement.content().getBytes());
+ out.write(closeGroup);
+ }
+
+ /**
+ * Add a Meta
element. It is written to the Inforamtion Group
+ * and merged with the main ByteArrayOutputStream
when the
+ * Document is closed.
+ *
+ * @param metaName The type of Meta
element to be added
+ * @param meta The Meta
element to be added
+ *
+ * Currently only the Meta Elements Author, Subject, Keywords, Title, Producer and CreationDate are supported.
+ *
+ * @throws IOException
+ */
+ private void writeMeta(byte[] metaName, Meta meta) throws IOException {
+ info.write(openGroup);
+ try {
+ info.write(escape);
+ info.write(metaName);
+ info.write(delimiter);
+ if (meta.type() == Meta.CREATIONDATE) {
+ writeFormatedDateTime(meta.content());
+ } else {
+ info.write(meta.content().getBytes());
+ }
+ } finally {
+ info.write(closeGroup);
+ }
+ }
+
+ /**
+ * Writes a date. The date is formated Year, Month, Day, Hour, Minute, Second
+ *
+ * @param date The date to be written
+ *
+ * @throws IOException
+ */
+ private void writeFormatedDateTime(String date) throws IOException {
+ Calendar cal = Calendar.getInstance();
+ SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
+ ParsePosition pp = new ParsePosition(0);
+ Date d = sdf.parse(date, pp);
+ if (d == null) {
+ d = new Date();
+ }
+ cal.setTime(d);
+ info.write(escape);
+ info.write(year);
+ writeInt(info, cal.get(Calendar.YEAR));
+ info.write(escape);
+ info.write(month);
+ writeInt(info, cal.get(Calendar.MONTH));
+ info.write(escape);
+ info.write(day);
+ writeInt(info, cal.get(Calendar.DAY_OF_MONTH));
+ info.write(escape);
+ info.write(hour);
+ writeInt(info, cal.get(Calendar.HOUR_OF_DAY));
+ info.write(escape);
+ info.write(minute);
+ writeInt(info, cal.get(Calendar.MINUTE));
+ info.write(escape);
+ info.write(second);
+ writeInt(info, cal.get(Calendar.SECOND));
+ }
+
+ /**
+ * Add a new Font
to the list of fonts. If the Font
+ * already exists in the list of fonts, then it is not added again.
+ *
+ * @param newFont The Font
to be added
+ *
+ * @return The index of the Font
in the font list
+ */
+ protected int addFont(Font newFont) {
+ int fn = -1;
+
+ for (int i = 0; i < fontList.size(); i++) {
+ if (newFont.getFamilyname().equals(((Font) fontList.get(i)).getFamilyname())) {
+ fn = i;
+ }
+ }
+ if (fn == -1) {
+ fontList.add(newFont);
+ return fontList.size() - 1;
+ }
+ return fn;
+ }
+
+ /**
+ * Add a new Color
to the list of colours. If the Color
+ * already exists in the list of colours, then it is not added again.
+ *
+ * @param newColor The Color
to be added
+ *
+ * @return The index of the color
in the colour list
+ */
+ protected int addColor(Color newColor) {
+ int cn = 0;
+ if (newColor == null) {
+ return cn;
+ }
+ cn = colorList.indexOf(newColor);
+ if (cn == -1) {
+ colorList.add(newColor);
+ return colorList.size() - 1;
+ }
+ return cn;
+ }
+
+ /**
+ * Merge all the different ArrayList
s and ByteArrayOutputStream
s
+ * to the final ByteArrayOutputStream
+ *
+ * @return true
if all information was sucessfully written to the ByteArrayOutputStream
+ */
+ private boolean writeDocument() {
+ try {
+ writeDocumentIntro();
+ writeFontList();
+ os.write((byte) '\n');
+ writeColorList();
+ os.write((byte) '\n');
+ writeList();
+ os.write((byte) '\n');
+ writeInfoGroup();
+ os.write((byte) '\n');
+ writeDocumentFormat();
+ os.write((byte) '\n');
+ ByteArrayOutputStream hf = new ByteArrayOutputStream();
+ writeSectionDefaults(hf);
+ hf.writeTo(os);
+ content.writeTo(os);
+ os.write(closeGroup);
+ return true;
+ } catch (IOException e) {
+ System.err.println(e.getMessage());
+ return false;
+ }
+
+ }
+
+ /** Write the Rich Text file settings
+ * @throws IOException
+ */
+ private void writeDocumentIntro() throws IOException {
+ os.write(openGroup);
+ os.write(escape);
+ os.write(docBegin);
+ os.write(escape);
+ os.write(ansi);
+ os.write(escape);
+ os.write(ansiCodepage);
+ writeInt(os, 1252);
+ os.write((byte)'\n');
+ os.write(escape);
+ os.write(defaultFont);
+ writeInt(os, 0);
+ }
+
+ /**
+ * Write the font list to the final ByteArrayOutputStream
+ * @throws IOException
+ */
+ private void writeFontList() throws IOException {
+ Font fnt;
+
+ os.write(openGroup);
+ os.write(escape);
+ os.write(fontTable);
+ for (int i = 0; i < fontList.size(); i++) {
+ fnt = (Font) fontList.get(i);
+ os.write(openGroup);
+ os.write(escape);
+ os.write(fontNumber);
+ writeInt(os, i);
+ os.write(escape);
+ switch (Font.getFamilyIndex(fnt.getFamilyname())) {
+ case Font.COURIER:
+ os.write(fontModern);
+ os.write(escape);
+ os.write(fontCharset);
+ writeInt(os, 0);
+ os.write(delimiter);
+ os.write(fontCourier);
+ break;
+ case Font.HELVETICA:
+ os.write(fontSwiss);
+ os.write(escape);
+ os.write(fontCharset);
+ writeInt(os, 0);
+ os.write(delimiter);
+ os.write(fontArial);
+ break;
+ case Font.SYMBOL:
+ os.write(fontRoman);
+ os.write(escape);
+ os.write(fontCharset);
+ writeInt(os, 2);
+ os.write(delimiter);
+ os.write(fontSymbol);
+ break;
+ case Font.TIMES_ROMAN:
+ os.write(fontRoman);
+ os.write(escape);
+ os.write(fontCharset);
+ writeInt(os, 0);
+ os.write(delimiter);
+ os.write(fontTimesNewRoman);
+ break;
+ case Font.ZAPFDINGBATS:
+ os.write(fontTech);
+ os.write(escape);
+ os.write(fontCharset);
+ writeInt(os, 0);
+ os.write(delimiter);
+ os.write(fontWindings);
+ break;
+ default:
+ os.write(fontRoman);
+ os.write(escape);
+ os.write(fontCharset);
+ writeInt(os, 0);
+ os.write(delimiter);
+ os.write(filterSpecialChar(fnt.getFamilyname(), true).getBytes());
+ }
+ os.write(commaDelimiter);
+ os.write(closeGroup);
+ }
+ os.write(closeGroup);
+ }
+
+ /**
+ * Write the colour list to the final ByteArrayOutputStream
+ * @throws IOException
+ */
+ private void writeColorList() throws IOException {
+ Color color = null;
+
+ os.write(openGroup);
+ os.write(escape);
+ os.write(colorTable);
+ for (int i = 0; i < colorList.size(); i++) {
+ color = (Color) colorList.get(i);
+ os.write(escape);
+ os.write(colorRed);
+ writeInt(os, color.getRed());
+ os.write(escape);
+ os.write(colorGreen);
+ writeInt(os, color.getGreen());
+ os.write(escape);
+ os.write(colorBlue);
+ writeInt(os, color.getBlue());
+ os.write(commaDelimiter);
+ }
+ os.write(closeGroup);
+ }
+
+ /**
+ * Write the Information Group to the final ByteArrayOutputStream
+ * @throws IOException
+ */
+ private void writeInfoGroup() throws IOException {
+ os.write(openGroup);
+ os.write(escape);
+ os.write(infoBegin);
+ info.writeTo(os);
+ os.write(closeGroup);
+ }
+
+ /**
+ * Write the listtable and listoverridetable to the final ByteArrayOutputStream
+ * @throws IOException
+ */
+ private void writeList() throws IOException {
+ listtable.write(closeGroup);
+ listoverride.write(closeGroup);
+ listtable.writeTo(os);
+ os.write((byte) '\n');
+ listoverride.writeTo(os);
+ }
+
+ /**
+ * Write an integer
+ *
+ * @param out The OuputStream
to which the int
value is to be written
+ * @param i The int
value to be written
+ * @throws IOException
+ */
+ public final static void writeInt(OutputStream out, int i) throws IOException {
+ out.write(Integer.toString(i).getBytes());
+ }
+
+ /**
+ * Get a random integer.
+ * This returns a unique random integer to be used with listids.
+ *
+ * @return Random int
value.
+ */
+ private int getRandomInt() {
+ boolean ok = false;
+ Integer newInt = null;
+ Integer oldInt = null;
+ while (!ok) {
+ newInt = new Integer((int) (Math.random() * Integer.MAX_VALUE));
+ ok = true;
+ for (int i = 0; i < listIds.size(); i++) {
+ oldInt = (Integer) listIds.get(i);
+ if (oldInt.equals(newInt)) {
+ ok = true;
+ }
+ }
+ }
+ listIds.add(newInt);
+ return newInt.intValue();
+ }
+
+ /**
+ * Write the current header and footer to a ByteArrayOutputStream
+ *
+ * @param os The ByteArrayOutputStream
to which the header and footer will be written.
+ * @throws IOException
+ */
+ public void writeHeadersFooters(ByteArrayOutputStream os) throws IOException {
+ if (this.footer instanceof RtfHeaderFooters) {
+ RtfHeaderFooters rtfHf = (RtfHeaderFooters) this.footer;
+ HeaderFooter hf = rtfHf.get(RtfHeaderFooters.ALL_PAGES);
+ if (hf != null) {
+ writeHeaderFooter(hf, footerBegin, os);
+ }
+ hf = rtfHf.get(RtfHeaderFooters.LEFT_PAGES);
+ if (hf != null) {
+ writeHeaderFooter(hf, footerlBegin, os);
+ }
+ hf = rtfHf.get(RtfHeaderFooters.RIGHT_PAGES);
+ if (hf != null) {
+ writeHeaderFooter(hf, footerrBegin, os);
+ }
+ hf = rtfHf.get(RtfHeaderFooters.FIRST_PAGE);
+ if (hf != null) {
+ writeHeaderFooter(hf, footerfBegin, os);
+ }
+ } else {
+ writeHeaderFooter(this.footer, footerBegin, os);
+ }
+ if (this.header instanceof RtfHeaderFooters) {
+ RtfHeaderFooters rtfHf = (RtfHeaderFooters) this.header;
+ HeaderFooter hf = rtfHf.get(RtfHeaderFooters.ALL_PAGES);
+ if (hf != null) {
+ writeHeaderFooter(hf, headerBegin, os);
+ }
+ hf = rtfHf.get(RtfHeaderFooters.LEFT_PAGES);
+ if (hf != null) {
+ writeHeaderFooter(hf, headerlBegin, os);
+ }
+ hf = rtfHf.get(RtfHeaderFooters.RIGHT_PAGES);
+ if (hf != null) {
+ writeHeaderFooter(hf, headerrBegin, os);
+ }
+ hf = rtfHf.get(RtfHeaderFooters.FIRST_PAGE);
+ if (hf != null) {
+ writeHeaderFooter(hf, headerfBegin, os);
+ }
+ } else {
+ writeHeaderFooter(this.header, headerBegin, os);
+ }
+ }
+
+ /**
+ * Write a HeaderFooter
to a ByteArrayOutputStream
+ *
+ * @param headerFooter The HeaderFooter
object to be written.
+ * @param hfType The type of header or footer to be added.
+ * @param target The ByteArrayOutputStream
to which the HeaderFooter
will be written.
+ * @throws IOException
+ */
+ private void writeHeaderFooter(HeaderFooter headerFooter, byte[] hfType, ByteArrayOutputStream target) throws IOException {
+ inHeaderFooter = true;
+ try {
+ target.write(openGroup);
+ target.write(escape);
+ target.write(hfType);
+ target.write(delimiter);
+ if (headerFooter != null) {
+ if (headerFooter instanceof RtfHeaderFooter && ((RtfHeaderFooter) headerFooter).content() != null) {
+ this.addElement(((RtfHeaderFooter) headerFooter).content(), target);
+ } else {
+ Paragraph par = new Paragraph();
+ par.setAlignment(headerFooter.alignment());
+ if (headerFooter.getBefore() != null) {
+ par.add(headerFooter.getBefore());
+ }
+ if (headerFooter.isNumbered()) {
+ par.add(new RtfPageNumber("", headerFooter.getBefore().font()));
+ }
+ if (headerFooter.getAfter() != null) {
+ par.add(headerFooter.getAfter());
+ }
+ this.addElement(par, target);
+ }
+ }
+ target.write(closeGroup);
+ } catch (DocumentException e) {
+ throw new IOException("DocumentException - " + e.getMessage());
+ }
+ inHeaderFooter = false;
+ }
+
+ /**
+ * Write the Document
's Paper and Margin Size
+ * to the final ByteArrayOutputStream
+ * @throws IOException
+ */
+ private void writeDocumentFormat() throws IOException {
+// os.write(openGroup);
+ os.write(escape);
+ os.write(rtfPaperWidth);
+ writeInt(os, pageWidth);
+ os.write(escape);
+ os.write(rtfPaperHeight);
+ writeInt(os, pageHeight);
+ os.write(escape);
+ os.write(rtfMarginLeft);
+ writeInt(os, marginLeft);
+ os.write(escape);
+ os.write(rtfMarginRight);
+ writeInt(os, marginRight);
+ os.write(escape);
+ os.write(rtfMarginTop);
+ writeInt(os, marginTop);
+ os.write(escape);
+ os.write(rtfMarginBottom);
+ writeInt(os, marginBottom);
+// os.write(closeGroup);
+ }
+
+ /**
+ * Initialise all helper classes.
+ * Clears alls lists, creates new ByteArrayOutputStream
's
+ */
+ private void initDefaults() {
+ fontList.clear();
+ colorList.clear();
+ info = new ByteArrayOutputStream();
+ content = new ByteArrayOutputStream();
+ listtable = new ByteArrayOutputStream();
+ listoverride = new ByteArrayOutputStream();
+ document.addProducer();
+ document.addCreationDate();
+ addFont(new Font(Font.TIMES_ROMAN, 10, Font.NORMAL));
+ addColor(new Color(0, 0, 0));
+ addColor(new Color(255, 255, 255));
+ listIds = new ArrayList();
+ try {
+ listtable.write(openGroup);
+ listtable.write(extendedEscape);
+ listtable.write(listtableGroup);
+ listtable.write((byte) '\n');
+ listoverride.write(openGroup);
+ listoverride.write(extendedEscape);
+ listoverride.write(listoverridetableGroup);
+ listoverride.write((byte) '\n');
+ } catch (IOException e) {
+ System.err.println("InitDefaultsError" + e);
+ }
+ }
+
+ /**
+ * Writes the default values for the current Section
+ *
+ * @param out The ByteArrayOutputStream
to be written to
+ * @throws IOException
+ */
+ private void writeSectionDefaults(ByteArrayOutputStream out) throws IOException {
+ if (header instanceof RtfHeaderFooters || footer instanceof RtfHeaderFooters) {
+ RtfHeaderFooters rtfHeader = (RtfHeaderFooters) header;
+ RtfHeaderFooters rtfFooter = (RtfHeaderFooters) footer;
+ if ((rtfHeader != null && (rtfHeader.get(RtfHeaderFooters.LEFT_PAGES) != null || rtfHeader.get(RtfHeaderFooters.RIGHT_PAGES) != null)) || (rtfFooter != null && (rtfFooter.get(RtfHeaderFooters.LEFT_PAGES) != null || rtfFooter.get(RtfHeaderFooters.RIGHT_PAGES) != null))) {
+ out.write(escape);
+ out.write(facingPages);
+ }
+ }
+ if (hasTitlePage) {
+ out.write(escape);
+ out.write(titlePage);
+ }
+ writeHeadersFooters(out);
+ if (landscape) {
+ //out.write(escape);
+ //out.write(landscapeTag1);
+ out.write(escape);
+ out.write(landscapeTag2);
+ out.write(escape);
+ out.write(sectionPageWidth);
+ writeInt(out, pageWidth);
+ out.write(escape);
+ out.write(sectionPageHeight);
+ writeInt(out, pageHeight);
+ } else {
+ out.write(escape);
+ out.write(sectionPageWidth);
+ writeInt(out, pageWidth);
+ out.write(escape);
+ out.write(sectionPageHeight);
+ writeInt(out, pageHeight);
+ }
+ }
+
+ /**
+ * This method tries to fit the Rectangle pageSize
to one of the predefined PageSize rectangles.
+ * If a match is found the pageWidth and pageHeight will be set according to values determined from files
+ * generated by MS Word2000 and OpenOffice 641. If no match is found the method will try to match the rotated
+ * Rectangle by calling itself with the parameter rotate set to true.
+ * @param pageSize a rectangle defining the size of the page
+ * @param rotate portrait or lanscape?
+ * @return true if the format parsing succeeded
+ */
+ private boolean parseFormat(Rectangle pageSize, boolean rotate) {
+ if (rotate) {
+ pageSize = pageSize.rotate();
+ }
+ if (rectEquals(pageSize, PageSize.A3)) {
+ pageWidth = 16837;
+ pageHeight = 23811;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.A4)) {
+ pageWidth = 11907;
+ pageHeight = 16840;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.A5)) {
+ pageWidth = 8391;
+ pageHeight = 11907;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.A6)) {
+ pageWidth = 5959;
+ pageHeight = 8420;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.B4)) {
+ pageWidth = 14570;
+ pageHeight = 20636;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.B5)) {
+ pageWidth = 10319;
+ pageHeight = 14572;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.HALFLETTER)) {
+ pageWidth = 7927;
+ pageHeight = 12247;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.LETTER)) {
+ pageWidth = 12242;
+ pageHeight = 15842;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.LEGAL)) {
+ pageWidth = 12252;
+ pageHeight = 20163;
+ landscape = rotate;
+ return true;
+ }
+ if (!rotate && parseFormat(pageSize, true)) {
+ int x = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = x;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This method compares to Rectangles. They are considered equal if width and height are the same
+ * @param rect1
+ * @param rect2
+ * @return true if rect1 and rect2 represent the same rectangle
+ */
+ private boolean rectEquals(Rectangle rect1, Rectangle rect2) {
+ return (rect1.width() == rect2.width()) && (rect1.height() == rect2.height());
+ }
+
+ /**
+ * Returns whether we are currently writing a header or footer
+ *
+ * @return the value of inHeaderFooter
+ */
+ public boolean writingHeaderFooter() {
+ return inHeaderFooter;
+ }
+
+ /**
+ * Replaces special characters with their unicode values
+ *
+ * @param str The original String
+ * @param useHex
+ * @return The converted String
+ */
+ public final static String filterSpecialChar(String str, boolean useHex) {
+ int length = str.length();
+ int z = (int) 'z';
+ StringBuffer ret = new StringBuffer(length);
+ for (int i = 0; i < length; i++) {
+ char ch = str.charAt(i);
+
+ if (ch == '\\') {
+ ret.append("\\\\");
+ } else if (ch == '\n') {
+ ret.append("\\par ");
+ } else if (((int) ch) > z) {
+ if(useHex) {
+ ret.append("\\\'").append(Long.toHexString((long) ch));
+ } else {
+ ret.append("\\u").append((long) ch).append('?');
+ }
+ } else {
+ ret.append(ch);
+ }
+ }
+ String s = ret.toString();
+ if(s.indexOf("$newpage$") >= 0) {
+ String before = s.substring(0, s.indexOf("$newpage$"));
+ String after = s.substring(s.indexOf("$newpage$") + 9);
+ ret = new StringBuffer(before);
+ ret.append("\\page\\par ");
+ ret.append(after);
+ return ret.toString();
+ }
+ return s;
+ }
+
+ private void addHeaderFooterFontColor(HeaderFooter hf) {
+ if(hf instanceof RtfHeaderFooter) {
+ RtfHeaderFooter rhf = (RtfHeaderFooter) hf;
+ if(rhf.content() instanceof Chunk) {
+ addFont(((Chunk) rhf.content()).font());
+ addColor(((Chunk) rhf.content()).font().color());
+ } else if(rhf.content() instanceof Phrase) {
+ addFont(((Phrase) rhf.content()).font());
+ addColor(((Phrase) rhf.content()).font().color());
+ }
+ }
+ if(hf.getBefore() != null) {
+ addFont(hf.getBefore().font());
+ addColor(hf.getBefore().font().color());
+ }
+ if(hf.getAfter() != null) {
+ addFont(hf.getAfter().font());
+ addColor(hf.getAfter().font().color());
+ }
+ }
+
+ private void processHeaderFooter(HeaderFooter hf) {
+ if(hf != null) {
+ if(hf instanceof RtfHeaderFooters) {
+ RtfHeaderFooters rhf = (RtfHeaderFooters) hf;
+ if(rhf.get(RtfHeaderFooters.ALL_PAGES) != null) {
+ addHeaderFooterFontColor(rhf.get(RtfHeaderFooters.ALL_PAGES));
+ }
+ if(rhf.get(RtfHeaderFooters.LEFT_PAGES) != null) {
+ addHeaderFooterFontColor(rhf.get(RtfHeaderFooters.LEFT_PAGES));
+ }
+ if(rhf.get(RtfHeaderFooters.RIGHT_PAGES) != null) {
+ addHeaderFooterFontColor(rhf.get(RtfHeaderFooters.RIGHT_PAGES));
+ }
+ if(rhf.get(RtfHeaderFooters.FIRST_PAGE) != null) {
+ addHeaderFooterFontColor(rhf.get(RtfHeaderFooters.FIRST_PAGE));
+ }
+ } else {
+ addHeaderFooterFontColor(hf);
+ }
+ }
+ }
+
+ /**
+ * @see com.lowagie.text.DocListener#setMarginMirroring(boolean)
+ */
+ public boolean setMarginMirroring(boolean MarginMirroring) {
+ return false;
+ }
+
+}
+
diff --git a/src/main/java/com/lowagie/text/rtf/RtfWriter2.java b/src/main/java/com/lowagie/text/rtf/RtfWriter2.java
new file mode 100644
index 0000000..40040df
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/RtfWriter2.java
@@ -0,0 +1,268 @@
+/*
+ * $Id: RtfWriter2.java,v 1.11 2005/09/11 19:09:57 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf;
+
+import com.lowagie.text.*;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.document.RtfDocumentSettings;
+import com.lowagie.text.rtf.text.RtfNewPage;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * The RtfWriter allows the creation of rtf documents via the iText system
+ *
+ * Version: $Id: RtfWriter2.java,v 1.11 2005/09/11 19:09:57 hallm Exp $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfWriter2 extends DocWriter implements DocListener {
+ /**
+ * The RtfDocument this RtfWriter is creating
+ */
+ RtfDocument rtfDoc = null;
+
+ /**
+ * Constructs a new RtfWriter that listens to the specified Document and
+ * writes its output to the OutputStream.
+ *
+ * @param doc The Document that this RtfWriter listens to
+ * @param os The OutputStream to write to
+ */
+ protected RtfWriter2(Document doc, OutputStream os) {
+ super(doc, os);
+ doc.addDocListener(this);
+ rtfDoc = new RtfDocument();
+ }
+
+ /**
+ * Static method to generate RtfWriters
+ *
+ * @param doc The Document that this RtfWriter listens to
+ * @param os The OutputStream to write to
+ * @return The new RtfWriter
+ */
+ public static RtfWriter2 getInstance(Document doc, OutputStream os) {
+ return new RtfWriter2(doc, os);
+ }
+
+ /**
+ * Sets the header to use
+ *
+ * @param hf The HeaderFooter to use
+ */
+ public void setHeader(HeaderFooter hf) {
+ this.rtfDoc.getDocumentHeader().setHeader(hf);
+ }
+
+ /**
+ * Resets the header
+ */
+ public void resetHeader() {
+ this.rtfDoc.getDocumentHeader().setHeader(null);
+ }
+
+ /**
+ * Sets the footer to use
+ *
+ * @param hf The HeaderFooter to use
+ */
+ public void setFooter(HeaderFooter hf) {
+ this.rtfDoc.getDocumentHeader().setFooter(hf);
+ }
+
+ /**
+ * Resets the footer
+ */
+ public void resetFooter() {
+ this.rtfDoc.getDocumentHeader().setFooter(null);
+ }
+
+ /**
+ * This method is not supported in the RtfWriter
+ * @param i Unused
+ */
+ public void setPageCount(int i) {}
+
+ /**
+ * This method is not supported in the RtfWriter
+ */
+ public void resetPageCount() {}
+
+ /**
+ * This method is not supported in the RtfWriter
+ *
+ * @param wm Unused
+ * @return false
+ */
+ public boolean add(Watermark wm) { return false; }
+
+ /**
+ * This method is not supported in the RtfWriter
+ */
+ public void removeWatermark() {}
+
+ /**
+ * This method is not supported in the RtfWriter
+ */
+ public void clearTextWrap() {}
+
+ /**
+ * Opens the RtfDocument
+ */
+ public void open() {
+ }
+
+ /**
+ * Closes the RtfDocument. This causes the document to be written
+ * to the specified OutputStream
+ */
+ public void close() {
+ try {
+ os.write(rtfDoc.writeDocument());
+ if(this.closeStream) {
+ os.close();
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ this.rtfDoc = new RtfDocument();
+ }
+
+ /**
+ * Adds an Element to the Document
+ *
+ * @param element The element to be added
+ * @return false
+ * @throws DocumentException
+ */
+ public boolean add(Element element) throws DocumentException {
+ if (pause) {
+ return false;
+ }
+ RtfBasicElement rtfElement = rtfDoc.getMapper().mapElement(element);
+ if(rtfElement != null) {
+ rtfDoc.add(rtfElement);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Adds a page break
+ *
+ * @return false
+ */
+ public boolean newPage() {
+ rtfDoc.add(new RtfNewPage(rtfDoc));
+ return true;
+ }
+
+ /**
+ * Sets the page margins
+ *
+ * @param left The left margin
+ * @param right The right margin
+ * @param top The top margin
+ * @param bottom The bottom margin
+ * @return false
+ */
+ public boolean setMargins(float left, float right, float top, float bottom) {
+ rtfDoc.getDocumentHeader().getPageSetting().setMarginLeft((int) (left * RtfElement.TWIPS_FACTOR));
+ rtfDoc.getDocumentHeader().getPageSetting().setMarginRight((int) (right * RtfElement.TWIPS_FACTOR));
+ rtfDoc.getDocumentHeader().getPageSetting().setMarginTop((int) (top * RtfElement.TWIPS_FACTOR));
+ rtfDoc.getDocumentHeader().getPageSetting().setMarginBottom((int) (bottom * RtfElement.TWIPS_FACTOR));
+ return true;
+ }
+
+ /**
+ * Sets the size of the page
+ *
+ * @param rect A Rectangle representing the page
+ * @return false
+ */
+ public boolean setPageSize(Rectangle rect) {
+ rtfDoc.getDocumentHeader().getPageSetting().setPageSize(rect);
+ return true;
+ }
+
+ /**
+ * Whether to automagically generate table of contents entries when
+ * adding Chapters or Sections.
+ *
+ * @param autogenerate Whether to automatically generate TOC entries
+ */
+ public void setAutogenerateTOCEntries(boolean autogenerate) {
+ this.rtfDoc.setAutogenerateTOCEntries(autogenerate);
+ }
+
+ /**
+ * Sets the rtf data cache style to use. Valid values are given in the
+ * RtfDataCache class.
+ *
+ * @param dataCacheStyle The style to use.
+ * @throws DocumentException If data has already been written into the data cache.
+ * @throws IOException If the disk cache could not be initialised.
+ */
+ public void setDataCacheStyle(int dataCacheStyle) throws DocumentException, IOException {
+ this.rtfDoc.setDataCacheStyle(dataCacheStyle);
+ }
+
+ /**
+ * Gets the RtfDocumentSettings that specify how the rtf document is generated.
+ *
+ * @return The current RtfDocumentSettings.
+ */
+ public RtfDocumentSettings getDocumentSettings() {
+ return this.rtfDoc.getDocumentSettings();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/document/RtfCodePage.java b/src/main/java/com/lowagie/text/rtf/document/RtfCodePage.java
new file mode 100644
index 0000000..e88a942
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/document/RtfCodePage.java
@@ -0,0 +1,102 @@
+/*
+ * $Id: RtfCodePage.java,v 1.16 2005/05/04 14:33:53 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.document;
+
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.RtfExtendedElement;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * The RtfCodePage class allows different code pages to be used in the rtf document.
+ * Currently always ansi / ansicpg1252
+ *
+ * Version: $Id: RtfCodePage.java,v 1.16 2005/05/04 14:33:53 blowagie Exp $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfCodePage extends RtfElement implements RtfExtendedElement {
+ /**
+ * Constant for ansi encoded rtf documents
+ */
+ private static final byte[] ANSI = "\\ansi".getBytes();
+ /**
+ * Constant for the ansi codepage
+ */
+ private static final byte[] ANSI_CODEPAGE = "\\ansicpg".getBytes();
+
+ /**
+ * Construct an RtfCodePage
+ *
+ * @param doc The RtfDocument this RtfCodePage belongs to
+ */
+ public RtfCodePage(RtfDocument doc) {
+ super(doc);
+ }
+
+ /**
+ * Writes the selected codepage to a byte array
+ *
+ * @return Byte array with the current codepage
+ */
+ public byte[] writeDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(ANSI);
+ result.write(ANSI_CODEPAGE);
+ result.write(intToByteArray(1252));
+ result.write((byte)'\n');
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/document/RtfDocument.java b/src/main/java/com/lowagie/text/rtf/document/RtfDocument.java
new file mode 100644
index 0000000..63f0321
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/document/RtfDocument.java
@@ -0,0 +1,301 @@
+/*
+ * $Id: RtfDocument.java,v 1.16 2005/12/24 13:14:59 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2003, 2004, 2005 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.document;
+
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.rtf.RtfBasicElement;
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.RtfMapper;
+import com.lowagie.text.rtf.document.output.RtfDataCache;
+import com.lowagie.text.rtf.document.output.RtfDiskCache;
+import com.lowagie.text.rtf.document.output.RtfMemoryCache;
+import com.lowagie.text.rtf.graphic.RtfImage;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * The RtfDocument stores all document related data and also the main data stream.
+ * INTERNAL CLASS - NOT TO BE USED DIRECTLY
+ *
+ * Version: $Id: RtfDocument.java,v 1.16 2005/12/24 13:14:59 hallm Exp $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ * @author Todd Bush (Todd.Bush@canopysystems.com) [Tab support]
+ */
+public class RtfDocument extends RtfElement {
+ /**
+ * Stores the actual document data
+ */
+ private RtfDataCache data = null;
+ /**
+ * The RtfMapper to use in this RtfDocument
+ */
+ private RtfMapper mapper = null;
+ /**
+ * The RtfDocumentHeader that handles all document header methods
+ */
+ private RtfDocumentHeader documentHeader = null;
+ /**
+ * Stores integers that have been generated as unique random numbers
+ */
+ private ArrayList previousRandomInts = null;
+ /**
+ * Whether to automatically generate TOC entries for Chapters and Sections. Defaults to false
+ */
+ private boolean autogenerateTOCEntries = false;
+ /**
+ * Whether data has been written to the RtfDataCache.
+ */
+ private boolean dataWritten = false;
+ /**
+ * The RtfDocumentSettings for this RtfDocument.
+ */
+ private RtfDocumentSettings documentSettings = null;
+ /**
+ * The last RtfBasicElement that was added directly to the RtfDocument.
+ */
+ private RtfBasicElement lastElementWritten = null;
+
+ /**
+ * Constant for the Rtf document start
+ */
+ private static final byte[] RTF_DOCUMENT = "\\rtf1".getBytes();
+
+ /**
+ * The default constructor for a RtfDocument
+ */
+ public RtfDocument() {
+ super(null);
+ data = new RtfMemoryCache();
+ mapper = new RtfMapper(this);
+ documentHeader = new RtfDocumentHeader(this);
+ documentHeader.init();
+ previousRandomInts = new ArrayList();
+ this.documentSettings = new RtfDocumentSettings(this);
+ }
+
+ /**
+ * Writes the document
+ *
+ * @return A byte array containing the complete rtf document
+ */
+ public byte[] writeDocument() {
+ ByteArrayOutputStream docStream = new ByteArrayOutputStream();
+ try {
+ docStream.write(OPEN_GROUP);
+ docStream.write(RtfDocument.RTF_DOCUMENT);
+ docStream.write(documentHeader.write());
+ data.writeTo(docStream);
+ docStream.write(CLOSE_GROUP);
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return docStream.toByteArray();
+ }
+
+ /**
+ * Adds an element to the rtf document
+ *
+ * @param element The element to add
+ */
+ public void add(RtfBasicElement element) {
+ try {
+ if(element instanceof RtfInfoElement) {
+ this.documentHeader.addInfoElement((RtfInfoElement) element);
+ } else {
+ this.dataWritten = true;
+ if(element instanceof RtfImage) {
+ ((RtfImage) element).setTopLevelElement(true);
+ }
+ data.getOutputStream().write(element.write());
+ this.lastElementWritten = element;
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+
+ /**
+ * Gets the RtfMapper object of this RtfDocument
+ *
+ * @return The RtfMapper
+ */
+ public RtfMapper getMapper() {
+ return mapper;
+ }
+
+ /**
+ * Generates a random integer that is unique with respect to the document.
+ *
+ * @return A random int
+ */
+ public int getRandomInt() {
+ Integer newInt = null;
+ do {
+ newInt = new Integer((int) (Math.random() * Integer.MAX_VALUE));
+ } while(previousRandomInts.contains(newInt));
+ previousRandomInts.add(newInt);
+ return newInt.intValue();
+ }
+
+ /**
+ * Gets the RtfDocumentHeader of this RtfDocument
+ *
+ * @return The RtfDocumentHeader of this RtfDocument
+ */
+ public RtfDocumentHeader getDocumentHeader() {
+ return this.documentHeader;
+ }
+
+ /**
+ * Replaces special characters with their unicode values
+ * @param str The original String
+ * @param useHex indicated if the hexadecimal value has to be used
+ * @param softLineBreaks whether to use soft line breaks instead of default hard ones.
+ *
+ * @return The converted String
+ */
+ public String filterSpecialChar(String str, boolean useHex, boolean softLineBreaks) {
+ int length = str.length();
+ int z = (int) 'z';
+ StringBuffer ret = new StringBuffer(length);
+ for (int i = 0; i < length; i++) {
+ char ch = str.charAt(i);
+
+ if (ch == '\\') {
+ ret.append("\\\\");
+ } else if (ch == '\n') {
+ if(softLineBreaks) {
+ ret.append("\\line ");
+ } else {
+ ret.append("\\par ");
+ }
+ } else if (ch == '\t') {
+ ret.append("\\tab ");
+ } else if (((int) ch) > z && this.documentSettings.isAlwaysUseUnicode()) {
+ if(useHex) {
+ ret.append("\\\'").append(Long.toHexString((long) ch));
+ } else {
+ ret.append("\\u").append((long) ch).append('?');
+ }
+ } else {
+ ret.append(ch);
+ }
+ }
+ String s = ret.toString();
+ if(s.indexOf("$newpage$") >= 0) {
+ String before = s.substring(0, s.indexOf("$newpage$"));
+ String after = s.substring(s.indexOf("$newpage$") + 9);
+ ret = new StringBuffer(before);
+ ret.append("\\page\\par ");
+ ret.append(after);
+ return ret.toString();
+ }
+ return s;
+ }
+
+ /**
+ * Whether to automagically generate table of contents entries when
+ * adding Chapters or Sections.
+ *
+ * @param autogenerate Whether to automatically generate TOC entries
+ */
+ public void setAutogenerateTOCEntries(boolean autogenerate) {
+ this.autogenerateTOCEntries = autogenerate;
+ }
+
+ /**
+ * Get whether to autmatically generate table of contents entries
+ *
+ * @return Wheter to automatically generate TOC entries
+ */
+ public boolean getAutogenerateTOCEntries() {
+ return this.autogenerateTOCEntries;
+ }
+
+ /**
+ * Sets the rtf data cache style to use. Valid values are given in the
+ * RtfDataCache class.
+ *
+ * @param dataCacheStyle The style to use.
+ * @throws DocumentException If data has already been written into the data cache.
+ * @throws IOException If the disk cache could not be initialised.
+ */
+ public void setDataCacheStyle(int dataCacheStyle) throws DocumentException, IOException {
+ if(dataWritten) {
+ throw new DocumentException("Data has already been written into the data cache. You can not change the cache style anymore.");
+ }
+ switch(dataCacheStyle) {
+ case RtfDataCache.CACHE_MEMORY : this.data = new RtfMemoryCache(); break;
+ case RtfDataCache.CACHE_DISK : this.data = new RtfDiskCache(); break;
+ default : this.data = new RtfMemoryCache(); break;
+ }
+ }
+
+ /**
+ * Gets the RtfDocumentSettings that specify how the rtf document is generated.
+ *
+ * @return The current RtfDocumentSettings.
+ */
+ public RtfDocumentSettings getDocumentSettings() {
+ return this.documentSettings;
+ }
+
+ /**
+ * Gets the last RtfBasicElement that was directly added to the RtfDocument.
+ *
+ * @return The last RtfBasicElement that was directly added to the RtfDocument.
+ */
+ public RtfBasicElement getLastElementWritten() {
+ return this.lastElementWritten;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/document/RtfDocumentHeader.java b/src/main/java/com/lowagie/text/rtf/document/RtfDocumentHeader.java
new file mode 100644
index 0000000..1ae8027
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/document/RtfDocumentHeader.java
@@ -0,0 +1,310 @@
+/*
+ * $Id: RtfDocumentHeader.java,v 1.16 2005/12/24 13:14:59 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.document;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.HeaderFooter;
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.headerfooter.RtfHeaderFooter;
+import com.lowagie.text.rtf.headerfooter.RtfHeaderFooterGroup;
+import com.lowagie.text.rtf.list.RtfList;
+import com.lowagie.text.rtf.list.RtfListTable;
+import com.lowagie.text.rtf.style.RtfColor;
+import com.lowagie.text.rtf.style.RtfColorList;
+import com.lowagie.text.rtf.style.RtfFont;
+import com.lowagie.text.rtf.style.RtfFontList;
+import com.lowagie.text.rtf.style.RtfParagraphStyle;
+import com.lowagie.text.rtf.style.RtfStylesheetList;
+
+
+/**
+ * The RtfDocumentHeader contains all classes required for the generation of
+ * the document header area.
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfDocumentHeader extends RtfElement {
+ /**
+ * Constant for the title page
+ */
+ private static final byte[] TITLE_PAGE = "\\titlepg".getBytes();
+ /**
+ * Constant for facing pages
+ */
+ private static final byte[] FACING_PAGES = "\\facingp".getBytes();
+
+ /**
+ * The code page to use
+ */
+ private RtfCodePage codePage = null;
+ /**
+ * Stores all the colors used in the document
+ */
+ private RtfColorList colorList = null;
+ /**
+ * Stores all the fonts used in the document
+ */
+ private RtfFontList fontList = null;
+ /**
+ * Manages List tables
+ */
+ private RtfListTable listTable = null;
+ /**
+ * Stores all paragraph styles used in the document.
+ */
+ private RtfStylesheetList stylesheetList = null;
+ /**
+ * The information group with author/subject/keywords/title/producer/creationdate data
+ */
+ private RtfInfoGroup infoGroup = null;
+ /**
+ * The page settings
+ */
+ private RtfPageSetting pageSetting = null;
+ /**
+ * The current RtfHeaderFooterGroup for the header
+ */
+ private RtfHeaderFooterGroup header = null;
+ /**
+ * The current RtfHeaderFooterGroup for the footer
+ */
+ private RtfHeaderFooterGroup footer = null;
+
+ /**
+ * Constructs a RtfDocumentHeader for a RtfDocument
+ *
+ * @param doc The RtfDocument this RtfDocumentHeader belongs to
+ */
+ protected RtfDocumentHeader(RtfDocument doc) {
+ super(doc);
+ }
+
+ /**
+ * Initialises the RtfDocumentHeader.
+ */
+ protected void init() {
+ this.codePage = new RtfCodePage(this.document);
+ this.colorList = new RtfColorList(this.document);
+ this.fontList = new RtfFontList(this.document);
+ this.listTable = new RtfListTable(this.document);
+ this.stylesheetList = new RtfStylesheetList(this.document);
+ this.infoGroup = new RtfInfoGroup(this.document);
+ this.pageSetting = new RtfPageSetting(this.document);
+ this.header = new RtfHeaderFooterGroup(this.document, RtfHeaderFooter.TYPE_HEADER);
+ this.footer = new RtfHeaderFooterGroup(this.document, RtfHeaderFooter.TYPE_FOOTER);
+ }
+
+ /**
+ * Write the contents of the document header area.
+ *
+ * @return A byte array with the contents of the document header area
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(this.codePage.writeDefinition());
+ result.write(this.fontList.writeDefinition());
+ result.write(this.colorList.writeDefinition());
+ result.write(this.stylesheetList.writeDefinition());
+ result.write(this.listTable.writeDefinition());
+ result.write(this.infoGroup.write());
+ result.write(this.pageSetting.writeDefinition());
+ result.write(writeSectionDefinition());
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the section definition data
+ *
+ * @return A byte array with the section definition data
+ */
+ public byte[] writeSectionDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ if(header.hasTitlePage() || footer.hasTitlePage()) {
+ result.write(TITLE_PAGE);
+ header.setHasTitlePage();
+ footer.setHasTitlePage();
+ }
+ if(header.hasFacingPages() || footer.hasFacingPages()) {
+ result.write(FACING_PAGES);
+ header.setHasFacingPages();
+ footer.setHasFacingPages();
+ }
+ result.write(footer.write());
+ result.write(header.write());
+ result.write(pageSetting.writeSectionDefinition());
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Gets the number of the specified RtfFont
+ *
+ * @param font The RtfFont for which to get the number
+ * @return The number of the font
+ */
+ public int getFontNumber(RtfFont font) {
+ return this.fontList.getFontNumber(font);
+ }
+
+ /**
+ * Gets the number of the specified RtfColor
+ *
+ * @param color The RtfColor for which to get the number
+ * @return The number of the color
+ */
+ public int getColorNumber(RtfColor color) {
+ return this.colorList.getColorNumber(color);
+ }
+
+ /**
+ * Gets the number of the specified RtfList
+ *
+ * @param list The RtfList for which to get the number
+ * @return The number of the list
+ */
+ public int getListNumber(RtfList list) {
+ return this.listTable.getListNumber(list);
+ }
+
+ /**
+ * Gets the RtfParagraphStyle with the given style name.
+ *
+ * @param styleName The style name of the RtfParagraphStyle to get.
+ * @return The RtfParagraphStyle with the given style name or null.
+ */
+ public RtfParagraphStyle getRtfParagraphStyle(String styleName) {
+ return this.stylesheetList.getRtfParagraphStyle(styleName);
+ }
+
+ /**
+ * Removes a RtfList from the list table
+ *
+ * @param list The RtfList to remove
+ */
+ public void freeListNumber(RtfList list) {
+ this.listTable.freeListNumber(list);
+ }
+
+ /**
+ * Gets the RtfPageSetting object of this RtfDocument
+ *
+ * @return The RtfPageSetting object
+ */
+ public RtfPageSetting getPageSetting() {
+ return this.pageSetting;
+ }
+
+ /**
+ * Adds an RtfInfoElement to the list of RtfInfoElements
+ *
+ * @param rtfInfoElement The RtfInfoElement to add
+ */
+ public void addInfoElement(RtfInfoElement rtfInfoElement) {
+ this.infoGroup.add(rtfInfoElement);
+ }
+
+ /**
+ * Sets the current header to use
+ *
+ * @param header The HeaderFooter to use as header
+ */
+ public void setHeader(HeaderFooter header) {
+ if(header != null) {
+ if(header instanceof RtfHeaderFooterGroup) {
+ this.header = new RtfHeaderFooterGroup(this.document, (RtfHeaderFooterGroup) header, RtfHeaderFooter.TYPE_HEADER);
+ } else if(header instanceof RtfHeaderFooter) {
+ this.header = new RtfHeaderFooterGroup(this.document, (RtfHeaderFooter) header, RtfHeaderFooter.TYPE_HEADER);
+ } else {
+ this.header = new RtfHeaderFooterGroup(this.document, header, RtfHeaderFooter.TYPE_HEADER);
+ }
+ } else {
+ this.header = new RtfHeaderFooterGroup(this.document, RtfHeaderFooter.TYPE_HEADER);
+ }
+ }
+
+ /**
+ * Sets the current footer to use
+ *
+ * @param footer The HeaderFooter to use as footer
+ */
+ public void setFooter(HeaderFooter footer) {
+ if(footer != null) {
+ if(footer instanceof RtfHeaderFooterGroup) {
+ this.footer = new RtfHeaderFooterGroup(this.document, (RtfHeaderFooterGroup) footer, RtfHeaderFooter.TYPE_FOOTER);
+ } else if(footer instanceof RtfHeaderFooter) {
+ this.footer = new RtfHeaderFooterGroup(this.document, (RtfHeaderFooter) footer, RtfHeaderFooter.TYPE_FOOTER);
+ } else {
+ this.footer = new RtfHeaderFooterGroup(this.document, footer, RtfHeaderFooter.TYPE_FOOTER);
+ }
+ } else {
+ this.footer = new RtfHeaderFooterGroup(this.document, RtfHeaderFooter.TYPE_FOOTER);
+ }
+ }
+
+ /**
+ * Registers the RtfParagraphStyle for further use in the document.
+ *
+ * @param rtfParagraphStyle The RtfParagraphStyle to register.
+ */
+ public void registerParagraphStyle(RtfParagraphStyle rtfParagraphStyle) {
+ this.stylesheetList.registerParagraphStyle(rtfParagraphStyle);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/document/RtfDocumentSettings.java b/src/main/java/com/lowagie/text/rtf/document/RtfDocumentSettings.java
new file mode 100644
index 0000000..15b5b92
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/document/RtfDocumentSettings.java
@@ -0,0 +1,182 @@
+/*
+ * $Id: RtfDocumentSettings.java,v 1.4 2005/12/24 13:14:59 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2003, 2004, 2005 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.document;
+
+import com.lowagie.text.rtf.style.RtfParagraphStyle;
+
+
+/**
+ * The RtfDocumentSettings contains output specific settings. These settings modify
+ * how the actual document is then generated and some settings may mean that some
+ * RTF readers can't read the document or render it wrongly.
+ *
+ * @version $Revision: 1.4 $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfDocumentSettings {
+
+ /**
+ * The RtfDocument this RtfDocumentSettings belongs to.
+ */
+ private RtfDocument document = null;
+ /**
+ * Whether to also output the table row definition after the cell content.
+ */
+ private boolean outputTableRowDefinitionAfter = true;
+ /**
+ * Whether to output the line breaks that make the rtf document source more readable.
+ */
+ private boolean outputDebugLineBreaks = true;
+ /**
+ * Whether to always generate soft linebreaks for \n in Chunks.
+ */
+ private boolean alwaysGenerateSoftLinebreaks = false;
+ /**
+ * Whether to always translate characters past 'z' into unicode representations.
+ */
+ private boolean alwaysUseUnicode = true;
+
+
+ /**
+ * Constructs a new RtfDocumentSettings object.
+ *
+ * @param document The RtfDocument this RtfDocumentSettings belong to.
+ */
+ public RtfDocumentSettings(RtfDocument document) {
+ this.document = document;
+ }
+
+ /**
+ * Gets whether to output the line breaks for increased rtf document readability.
+ *
+ * @return Whether to output line breaks.
+ */
+ public boolean isOutputDebugLineBreaks() {
+ return outputDebugLineBreaks;
+ }
+
+ /**
+ * Sets whether to output the line breaks for increased rtf document readability.
+ * Some line breaks may be added where the rtf specification demands it.
+ *
+ * @param outputDebugLineBreaks The outputDebugLineBreaks to set.
+ */
+ public void setOutputDebugLineBreaks(boolean outputDebugLineBreaks) {
+ this.outputDebugLineBreaks = outputDebugLineBreaks;
+ }
+
+ /**
+ * Gets whether the table row definition should also be written after the cell content.
+ *
+ * @return Returns the outputTableRowDefinitionAfter.
+ */
+ public boolean isOutputTableRowDefinitionAfter() {
+ return outputTableRowDefinitionAfter;
+ }
+
+ /**
+ * Sets whether the table row definition should also be written after the cell content.
+ * This is recommended to be set to true
if you need Word2000 compatiblity and
+ * false
if the document should be opened in OpenOffice.org Writer.
+ *
+ * @param outputTableRowDefinitionAfter The outputTableRowDefinitionAfter to set.
+ */
+ public void setOutputTableRowDefinitionAfter(
+ boolean outputTableRowDefinitionAfter) {
+ this.outputTableRowDefinitionAfter = outputTableRowDefinitionAfter;
+ }
+
+ /**
+ * Gets whether all linebreaks inside Chunks are generated as soft linebreaks.
+ *
+ * @return True
if soft linebreaks are generated, false
for hard linebreaks.
+ */
+ public boolean isAlwaysGenerateSoftLinebreaks() {
+ return this.alwaysGenerateSoftLinebreaks;
+ }
+
+ /**
+ * Sets whether to always generate soft linebreaks.
+ *
+ * @param alwaysGenerateSoftLinebreaks Whether to always generate soft linebreaks.
+ */
+ public void setAlwaysGenerateSoftLinebreaks(boolean alwaysGenerateSoftLinebreaks) {
+ this.alwaysGenerateSoftLinebreaks = alwaysGenerateSoftLinebreaks;
+ }
+
+ /**
+ * Gets whether all characters bigger than 'z' are represented as unicode.
+ *
+ * @return True
if unicode representation is used, false
otherwise.
+ */
+ public boolean isAlwaysUseUnicode() {
+ return this.alwaysUseUnicode;
+ }
+
+ /**
+ * Sets whether to represent all characters bigger than 'z' as unicode.
+ *
+ * @param alwaysUseUnicode True
to use unicode representation, false
otherwise.
+ */
+ public void setAlwaysUseUnicode(boolean alwaysUseUnicode) {
+ this.alwaysUseUnicode = alwaysUseUnicode;
+ }
+
+ /**
+ * Registers the RtfParagraphStyle for further use in the document. This does not need to be
+ * done for the default styles in the RtfParagraphStyle object. Those are added automatically.
+ *
+ * @param rtfParagraphStyle The RtfParagraphStyle to register.
+ */
+ public void registerParagraphStyle(RtfParagraphStyle rtfParagraphStyle) {
+ this.document.getDocumentHeader().registerParagraphStyle(rtfParagraphStyle);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/document/RtfInfoElement.java b/src/main/java/com/lowagie/text/rtf/document/RtfInfoElement.java
new file mode 100644
index 0000000..5549684
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/document/RtfInfoElement.java
@@ -0,0 +1,181 @@
+/*
+ * $Id: RtfInfoElement.java,v 1.16 2005/05/04 14:33:53 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.document;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.lowagie.text.Meta;
+import com.lowagie.text.rtf.RtfElement;
+
+
+/**
+ * Stores one information group element. Valid elements are
+ * author, title, subject, keywords, producer and creationdate.
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfInfoElement extends RtfElement {
+
+ /**
+ * Constant for the author element
+ */
+ private static final byte[] INFO_AUTHOR = "\\author".getBytes();
+ /**
+ * Constant for the subject element
+ */
+ private static final byte[] INFO_SUBJECT = "\\subject".getBytes();
+ /**
+ * Constant for the keywords element
+ */
+ private static final byte[] INFO_KEYWORDS = "\\keywords".getBytes();
+ /**
+ * Constant for the title element
+ */
+ private static final byte[] INFO_TITLE = "\\title".getBytes();
+ /**
+ * Constant for the producer element
+ */
+ private static final byte[] INFO_PRODUCER = "\\operator".getBytes();
+ /**
+ * Constant for the creationdate element
+ */
+ private static final byte[] INFO_CREATION_DATE = "\\creationdate".getBytes();
+
+ /**
+ * The type of this RtfInfoElement. The values from Element.INFO_ELEMENT_NAME are used.
+ */
+ private int infoType = -1;
+ /**
+ * The content of this RtfInfoElement
+ */
+ private String content = "";
+
+ /**
+ * Constructs a RtfInfoElement based on the given Meta object
+ *
+ * @param doc The RtfDocument this RtfInfoElement belongs to
+ * @param meta The Meta object this RtfInfoElement is based on
+ */
+ public RtfInfoElement(RtfDocument doc, Meta meta) {
+ super(doc);
+ infoType = meta.type();
+ content = meta.content();
+ }
+
+ /**
+ * Writes this RtfInfoElement
+ *
+ * @return A byte array containing the RtfInfoElement data
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(OPEN_GROUP);
+ switch(infoType) {
+ case Meta.AUTHOR:
+ result.write(INFO_AUTHOR);
+ break;
+ case Meta.SUBJECT:
+ result.write(INFO_SUBJECT);
+ break;
+ case Meta.KEYWORDS:
+ result.write(INFO_KEYWORDS);
+ break;
+ case Meta.TITLE:
+ result.write(INFO_TITLE);
+ break;
+ case Meta.PRODUCER:
+ result.write(INFO_PRODUCER);
+ break;
+ case Meta.CREATIONDATE:
+ result.write(INFO_CREATION_DATE);
+ break;
+ default:
+ result.write(INFO_AUTHOR);
+ break;
+ }
+ result.write(DELIMITER);
+ if(infoType == Meta.CREATIONDATE) {
+ result.write(convertDate(content).getBytes());
+ } else {
+ result.write(content.getBytes());
+ }
+ result.write(CLOSE_GROUP);
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Converts a date from the format used by iText to the format required by
+ * rtf.
iText: EEE MMM dd HH:mm:ss zzz yyyy - rtf: \\'yr'yyyy\\'mo'MM\\'dy'dd\\'hr'HH\\'min'mm\\'sec'ss
+ *
+ * @param date The date formated by iText
+ * @return The date formated for rtf
+ */
+ private String convertDate(String date) {
+ SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
+ try {
+ Date creationDate = sdf.parse(date);
+ sdf = new SimpleDateFormat("\\'yr'yyyy\\'mo'MM\\'dy'dd\\'hr'HH\\'min'mm\\'sec'ss");
+ return sdf.format(creationDate);
+ } catch(ParseException pe) {
+ pe.printStackTrace();
+ return "";
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/document/RtfInfoGroup.java b/src/main/java/com/lowagie/text/rtf/document/RtfInfoGroup.java
new file mode 100644
index 0000000..fc6c46a
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/document/RtfInfoGroup.java
@@ -0,0 +1,117 @@
+/*
+ * $Id: RtfInfoGroup.java,v 1.16 2005/05/04 14:33:52 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.document;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import com.lowagie.text.rtf.RtfElement;
+
+
+/**
+ * The RtfInfoGroup stores information group elements.
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfInfoGroup extends RtfElement {
+ /**
+ * Information group starting tag
+ */
+ private static final byte[] INFO_GROUP = "\\info".getBytes();
+
+ /**
+ * The RtfInfoElements that belong to this RtfInfoGroup
+ */
+ ArrayList infoElements = null;
+
+ /**
+ * Constructs a RtfInfoGroup belonging to a RtfDocument
+ *
+ * @param doc The RtfDocument this RtfInfoGroup belongs to
+ */
+ public RtfInfoGroup(RtfDocument doc) {
+ super(doc);
+ infoElements = new ArrayList();
+ }
+
+ /**
+ * Adds an RtfInfoElement to the RtfInfoGroup
+ *
+ * @param infoElement The RtfInfoElement to add
+ */
+ public void add(RtfInfoElement infoElement) {
+ this.infoElements.add(infoElement);
+ }
+
+ /**
+ * Writes the RtfInfoGroup and its RtfInfoElement elements.
+ *
+ * @return A byte array containing the group and its elements
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(OPEN_GROUP);
+ result.write(INFO_GROUP);
+ for(int i = 0; i < infoElements.size(); i++) {
+ RtfInfoElement infoElement = (RtfInfoElement) infoElements.get(i);
+ result.write(infoElement.write());
+ }
+ result.write(CLOSE_GROUP);
+ result.write((byte)'\n');
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/document/RtfPageSetting.java b/src/main/java/com/lowagie/text/rtf/document/RtfPageSetting.java
new file mode 100644
index 0000000..fd456e4
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/document/RtfPageSetting.java
@@ -0,0 +1,435 @@
+/*
+ * $Id: RtfPageSetting.java,v 1.15 2005/02/23 16:57:45 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.document;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.PageSize;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.RtfExtendedElement;
+
+
+/**
+ * The RtfPageSetting stores the page size / page margins for a RtfDocument.
+ * INTERNAL CLASS - NOT TO BE USED DIRECTLY
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfPageSetting extends RtfElement implements RtfExtendedElement {
+
+ /**
+ * Constant for the page height
+ */
+ private static final byte[] PAGE_WIDTH = "\\paperw".getBytes();
+ /**
+ * Constant for the page width
+ */
+ private static final byte[] PAGE_HEIGHT = "\\paperh".getBytes();
+ /**
+ * Constant for the left margin
+ */
+ private static final byte[] MARGIN_LEFT = "\\margl".getBytes();
+ /**
+ * Constant for the right margin
+ */
+ private static final byte[] MARGIN_RIGHT = "\\margr".getBytes();
+ /**
+ * Constant for the top margin
+ */
+ private static final byte[] MARGIN_TOP = "\\margt".getBytes();
+ /**
+ * Constant for the bottom margin
+ */
+ private static final byte[] MARGIN_BOTTOM = "\\margb".getBytes();
+ /**
+ * Constant for landscape
+ */
+ private static final byte[] LANDSCAPE = "\\lndscpsxn".getBytes();
+ /**
+ * Constant for the section page width
+ */
+ private static final byte[] SECTION_PAGE_WIDTH = "\\pgwsxn".getBytes();
+ /**
+ * Constant for the section page height
+ */
+ private static final byte[] SECTION_PAGE_HEIGHT = "\\pghsxn".getBytes();
+ /**
+ * Constant for the section left margin
+ */
+ private static final byte[] SECTION_MARGIN_LEFT = "\\marglsxn".getBytes();
+ /**
+ * Constant for the section right margin
+ */
+ private static final byte[] SECTION_MARGIN_RIGHT = "\\margrsxn".getBytes();
+ /**
+ * Constant for the section top margin
+ */
+ private static final byte[] SECTION_MARGIN_TOP = "\\margtsxn".getBytes();
+ /**
+ * Constant for the section bottom margin
+ */
+ private static final byte[] SECTION_MARGIN_BOTTOM = "\\margbsxn".getBytes();
+
+ /**
+ * The page width to use
+ */
+ private int pageWidth = 11906;
+ /**
+ * The page height to use
+ */
+ private int pageHeight = 16840;
+ /**
+ * The left margin to use
+ */
+ private int marginLeft = 1800;
+ /**
+ * The right margin to use
+ */
+ private int marginRight = 1800;
+ /**
+ * The top margin to use
+ */
+ private int marginTop = 1440;
+ /**
+ * The bottom margin to use
+ */
+ private int marginBottom = 1440;
+ /**
+ * Whether the page is portrait or landscape
+ */
+ private boolean landscape = false;
+
+ /**
+ * Constructs a new RtfPageSetting object belonging to a RtfDocument.
+ *
+ * @param doc The RtfDocument this RtfPageSetting belongs to
+ */
+ public RtfPageSetting(RtfDocument doc) {
+ super(doc);
+ }
+
+ /**
+ * Writes the page size / page margin definition
+ *
+ * @return A byte array with the page size / page margin definition
+ */
+ public byte[] writeDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(PAGE_WIDTH);
+ result.write(intToByteArray(pageWidth));
+ result.write(PAGE_HEIGHT);
+ result.write(intToByteArray(pageHeight));
+ result.write(MARGIN_LEFT);
+ result.write(intToByteArray(marginLeft));
+ result.write(MARGIN_RIGHT);
+ result.write(intToByteArray(marginRight));
+ result.write(MARGIN_TOP);
+ result.write(intToByteArray(marginTop));
+ result.write(MARGIN_BOTTOM);
+ result.write(intToByteArray(marginBottom));
+ result.write((byte)'\n');
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the definition part for a new section
+ *
+ * @return A byte array containing the definition for a new section
+ */
+ public byte[] writeSectionDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ if(landscape) {
+ result.write(LANDSCAPE);
+ result.write(SECTION_PAGE_WIDTH);
+ result.write(intToByteArray(pageWidth));
+ result.write(SECTION_PAGE_HEIGHT);
+ result.write(intToByteArray(pageHeight));
+ result.write((byte)'\n');
+ } else {
+ result.write(SECTION_PAGE_WIDTH);
+ result.write(intToByteArray(pageWidth));
+ result.write(SECTION_PAGE_HEIGHT);
+ result.write(intToByteArray(pageHeight));
+ result.write((byte)'\n');
+ }
+ result.write(SECTION_MARGIN_LEFT);
+ result.write(intToByteArray(marginLeft));
+ result.write(SECTION_MARGIN_RIGHT);
+ result.write(intToByteArray(marginRight));
+ result.write(SECTION_MARGIN_TOP);
+ result.write(intToByteArray(marginTop));
+ result.write(SECTION_MARGIN_BOTTOM);
+ result.write(intToByteArray(marginBottom));
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Gets the bottom margin
+ *
+ * @return Returns the bottom margin
+ */
+ public int getMarginBottom() {
+ return marginBottom;
+ }
+
+ /**
+ * Sets the bottom margin
+ *
+ * @param marginBottom The bottom margin to use
+ */
+ public void setMarginBottom(int marginBottom) {
+ this.marginBottom = marginBottom;
+ }
+
+ /**
+ * Gets the left margin
+ *
+ * @return Returns the left margin
+ */
+ public int getMarginLeft() {
+ return marginLeft;
+ }
+
+ /**
+ * Sets the left margin to use
+ *
+ * @param marginLeft The left margin to use
+ */
+ public void setMarginLeft(int marginLeft) {
+ this.marginLeft = marginLeft;
+ }
+
+ /**
+ * Gets the right margin
+ *
+ * @return Returns the right margin
+ */
+ public int getMarginRight() {
+ return marginRight;
+ }
+
+ /**
+ * Sets the right margin to use
+ *
+ * @param marginRight The right margin to use
+ */
+ public void setMarginRight(int marginRight) {
+ this.marginRight = marginRight;
+ }
+
+ /**
+ * Gets the top margin
+ *
+ * @return Returns the top margin
+ */
+ public int getMarginTop() {
+ return marginTop;
+ }
+
+ /**
+ * Sets the top margin to use
+ *
+ * @param marginTop The top margin to use
+ */
+ public void setMarginTop(int marginTop) {
+ this.marginTop = marginTop;
+ }
+
+ /**
+ * Gets the page height
+ *
+ * @return Returns the page height
+ */
+ public int getPageHeight() {
+ return pageHeight;
+ }
+
+ /**
+ * Sets the page height to use
+ *
+ * @param pageHeight The page height to use
+ */
+ public void setPageHeight(int pageHeight) {
+ this.pageHeight = pageHeight;
+ }
+
+ /**
+ * Gets the page width
+ *
+ * @return Returns the page width
+ */
+ public int getPageWidth() {
+ return pageWidth;
+ }
+
+ /**
+ * Sets the page width to use
+ *
+ * @param pageWidth The page width to use
+ */
+ public void setPageWidth(int pageWidth) {
+ this.pageWidth = pageWidth;
+ }
+
+ /**
+ * Set the page size to use. This method will use guessFormat to try to guess the correct
+ * page format. If no format could be guessed, the sizes from the pageSize are used and
+ * the landscape setting is determined by comparing width and height;
+ *
+ * @param pageSize The pageSize to use
+ */
+ public void setPageSize(Rectangle pageSize) {
+ if(!guessFormat(pageSize, false)) {
+ this.pageWidth = (int) (pageSize.width() * RtfElement.TWIPS_FACTOR);
+ this.pageHeight = (int) (pageSize.height() * RtfElement.TWIPS_FACTOR);
+ this.landscape = pageWidth > pageHeight;
+ }
+ }
+
+ /**
+ * This method tries to fit the Rectangle pageSize
to one of the predefined PageSize rectangles.
+ * If a match is found the pageWidth and pageHeight will be set according to values determined from files
+ * generated by MS Word2000 and OpenOffice 641. If no match is found the method will try to match the rotated
+ * Rectangle by calling itself with the parameter rotate set to true.
+ *
+ * @param pageSize the page size for which to guess the correct format
+ * @param rotate Whether we should try to rotate the size befor guessing the format
+ * @return True
if the format was guessed, false/
and otherwise
+ */
+ private boolean guessFormat(Rectangle pageSize, boolean rotate) {
+ if (rotate) {
+ pageSize = pageSize.rotate();
+ }
+ if (rectEquals(pageSize, PageSize.A3)) {
+ pageWidth = 16837;
+ pageHeight = 23811;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.A4)) {
+ pageWidth = 11907;
+ pageHeight = 16840;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.A5)) {
+ pageWidth = 8391;
+ pageHeight = 11907;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.A6)) {
+ pageWidth = 5959;
+ pageHeight = 8420;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.B4)) {
+ pageWidth = 14570;
+ pageHeight = 20636;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.B5)) {
+ pageWidth = 10319;
+ pageHeight = 14572;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.HALFLETTER)) {
+ pageWidth = 7927;
+ pageHeight = 12247;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.LETTER)) {
+ pageWidth = 12242;
+ pageHeight = 15842;
+ landscape = rotate;
+ return true;
+ }
+ if (rectEquals(pageSize, PageSize.LEGAL)) {
+ pageWidth = 12252;
+ pageHeight = 20163;
+ landscape = rotate;
+ return true;
+ }
+ if (!rotate && guessFormat(pageSize, true)) {
+ int x = pageWidth;
+ pageWidth = pageHeight;
+ pageHeight = x;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This method compares to Rectangles. They are considered equal if width and height are the same
+ *
+ * @param rect1 The first Rectangle to compare
+ * @param rect2 The second Rectangle to compare
+ * @return
+ *
+ */
+
+public class XmlWriter extends DocWriter implements DocListener {
+
+ // static membervariables (tags)
+
+/** This is the first line of the XML page. */
+ public static final byte[] PROLOG = getISOBytes("\n");
+
+/** This is the reference to the DTD. */
+ public static final byte[] DOCTYPE = getISOBytes("\n";
+ xmlCode['\"'] = """; // double quote
+ xmlCode['\''] = "'"; // single quote
+ xmlCode['&'] = "&"; // ampersand
+ xmlCode['<'] = "<"; // lower than
+ xmlCode['>'] = ">"; // greater than
+
+ for (int i = 128; i < 256; i++) {
+ xmlCode[i] = "" + i + ";";
+ }
+ }
+ // membervariables
+
+/** This is the meta information of the document. */
+ private TreeMap itext = new TreeMap(new com.lowagie.text.StringCompare());
+
+ // constructors
+
+/**
+ * Constructs an True
if the Rectangles equal, false
otherwise
+ */
+ private boolean rectEquals(Rectangle rect1, Rectangle rect2) {
+ return (rect1.width() == rect2.width()) && (rect1.height() == rect2.height());
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/document/output/RtfDataCache.java b/src/main/java/com/lowagie/text/rtf/document/output/RtfDataCache.java
new file mode 100644
index 0000000..f9b3e9d
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/document/output/RtfDataCache.java
@@ -0,0 +1,86 @@
+/*
+ * $Id: RtfDataCache.java,v 1.1 2005/02/02 18:09:32 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.document.output;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * The RtfDataCache interface must be implemented by classes wishing to
+ * act as caches for the rtf document data.
+ *
+ * @version $Revision: 1.1 $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public interface RtfDataCache {
+ /**
+ * Constant for caching into memory.
+ */
+ public static final int CACHE_MEMORY = 2;
+ /**
+ * Constant for caching to the disk.
+ */
+ public static final int CACHE_DISK = 1;
+ /**
+ * Get the OutputStream that the RtfDocument can write to.
+ *
+ * @return The OutputStream the RtfDocument can use.
+ */
+ public OutputStream getOutputStream();
+ /**
+ * Write the content of the cache into the OutputStream.
+ *
+ * @param target The OutputStream to write the content into.
+ * @throws IOException If an error occurs reading/writing.
+ */
+ public void writeTo(OutputStream target) throws IOException;
+}
diff --git a/src/main/java/com/lowagie/text/rtf/document/output/RtfDiskCache.java b/src/main/java/com/lowagie/text/rtf/document/output/RtfDiskCache.java
new file mode 100644
index 0000000..77053ac
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/document/output/RtfDiskCache.java
@@ -0,0 +1,113 @@
+/*
+ * $Id: RtfDiskCache.java,v 1.1 2005/02/02 18:09:32 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.document.output;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * The RtfFileCache is a RtfDataCache that uses a temporary file
+ * to store the rtf document data. Not so fast, but doesn't use any
+ * memory (just disk space).
+ *
+ * @version $Revision: 1.1 $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfDiskCache implements RtfDataCache {
+
+ /**
+ * The BufferedOutputStream that stores the cache data.
+ */
+ private BufferedOutputStream data = null;
+ /**
+ * The temporary file to store the data in.
+ */
+ private File tempFile = null;
+
+ /**
+ * Constructs a RtfFileCache. Creates the temp file.
+ *
+ * @throws IOException If the temporary file could not be created.
+ */
+ public RtfDiskCache() throws IOException {
+ this.tempFile = File.createTempFile("iText", null);
+ this.data = new BufferedOutputStream(new FileOutputStream(tempFile));
+ }
+
+ /**
+ * Gets the BufferedOutputStream to write to.
+ */
+ public OutputStream getOutputStream() {
+ return this.data;
+ }
+
+ /**
+ * Writes the content of the temporary file into the OutputStream.
+ */
+ public void writeTo(OutputStream target) throws IOException {
+ this.data.close();
+ BufferedInputStream tempIn = new BufferedInputStream(new FileInputStream(this.tempFile));
+ byte[] buffer = new byte[8192];
+ int bytesRead = -1;
+ while((bytesRead = tempIn.read(buffer)) >= 0) {
+ target.write(buffer, 0, bytesRead);
+ }
+ tempIn.close();
+ this.tempFile.delete();
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/rtf/document/output/RtfMemoryCache.java b/src/main/java/com/lowagie/text/rtf/document/output/RtfMemoryCache.java
new file mode 100644
index 0000000..002355f
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/document/output/RtfMemoryCache.java
@@ -0,0 +1,93 @@
+/*
+ * $Id: RtfMemoryCache.java,v 1.1 2005/02/02 18:09:31 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.document.output;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * The RtfMemoryCache is an RtfDataCache that keeps the whole rtf document
+ * data in memory. Fast but memory intensive.
+ *
+ * @version $Revision: 1.1 $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfMemoryCache implements RtfDataCache {
+
+ /**
+ * The buffer for the rtf document data.
+ */
+ private ByteArrayOutputStream data = null;
+
+ /**
+ * Constructs a RtfMemoryCache.
+ */
+ public RtfMemoryCache() {
+ this.data = new ByteArrayOutputStream();
+ }
+
+ /**
+ * Gets the ByteArrayOutputStream.
+ */
+ public OutputStream getOutputStream() {
+ return this.data;
+ }
+
+ /**
+ * Writes the content of the ByteArrayOutputStream into the OutputStream.
+ */
+ public void writeTo(OutputStream target) throws IOException {
+ this.data.writeTo(target);
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/rtf/field/RtfAnchor.java b/src/main/java/com/lowagie/text/rtf/field/RtfAnchor.java
new file mode 100644
index 0000000..3823c3d
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/field/RtfAnchor.java
@@ -0,0 +1,122 @@
+/*
+ * $Id: RtfAnchor.java,v 1.9 2005/07/07 14:44:47 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.field;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.Anchor;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.text.RtfPhrase;
+
+
+/**
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ * @author Werner Daehn (Werner.Daehn@BusinessObjects.com)
+ */
+public class RtfAnchor extends RtfField {
+
+ /**
+ * Constant for a hyperlink
+ */
+ private static final byte[] HYPERLINK = "HYPERLINK".getBytes();
+
+ /**
+ * The url of this RtfAnchor
+ */
+ private String url = "";
+ /**
+ * The RtfPhrase to display for the url
+ */
+ private RtfPhrase content = null;
+
+ /**
+ * Constructs a RtfAnchor based on a RtfField
+ *
+ * @param doc The RtfDocument this RtfAnchor belongs to
+ * @param anchor The Anchor this RtfAnchor is based on
+ */
+ public RtfAnchor(RtfDocument doc, Anchor anchor) {
+ super(doc);
+ this.url = anchor.reference();
+ this.content = new RtfPhrase(doc, anchor);
+ }
+
+ /**
+ * Write the field instructions for this RtfAnchor. Sets the field
+ * type to HYPERLINK and then writes the url.
+ *
+ * @return The field instructions for this RtfAnchor
+ * @throws IOException
+ */
+ protected byte[] writeFieldInstContent() throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+ result.write(HYPERLINK);
+ result.write(DELIMITER);
+ result.write(url.getBytes());
+
+ return result.toByteArray();
+ }
+
+ /**
+ * Write the field result for this RtfAnchor. Writes the content
+ * of the RtfPhrase.
+ *
+ * @return The field result for this RtfAnchor
+ * @throws IOException
+ */
+ protected byte[] writeFieldResultContent() throws IOException {
+ return content.write();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/field/RtfField.java b/src/main/java/com/lowagie/text/rtf/field/RtfField.java
new file mode 100644
index 0000000..6eaa525
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/field/RtfField.java
@@ -0,0 +1,445 @@
+/*
+ * $Id: RtfField.java,v 1.11 2005/09/05 16:02:40 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2004 by Mark Hall
+ * Uses code Copyright 2002
+ * SMB
+ * Dirk.Weigenand@smb-tec.com
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.field;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.Chunk;
+import com.lowagie.text.Font;
+import com.lowagie.text.rtf.RtfBasicElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.style.RtfFont;
+
+
+/**
+ * The RtfField class is an abstract base class for all rtf field functionality.
+ * Subclasses only need to implement the two abstract methods writeFieldInstContent
+ * and writeFieldResultContent. All other field functionality is handled by the
+ * RtfField class.
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ * @author Dirk Weigenand
+ */
+public abstract class RtfField extends Chunk implements RtfBasicElement {
+
+ /**
+ * Constant for a rtf field
+ */
+ private static final byte[] FIELD = "\\field".getBytes();
+ /**
+ * Constant for a dirty field
+ */
+ private static final byte[] FIELD_DIRTY = "\\flddirty".getBytes();
+ /**
+ * Constant for a private field
+ */
+ private static final byte[] FIELD_PRIVATE = "\\fldpriv".getBytes();
+ /**
+ * Constant for a locked field
+ */
+ private static final byte[] FIELD_LOCKED = "\\fldlock".getBytes();
+ /**
+ * Constant for a edited field
+ */
+ private static final byte[] FIELD_EDIT = "\\fldedit".getBytes();
+ /**
+ * Constant for an alt field
+ */
+ private static final byte[] FIELD_ALT = "\\fldalt".getBytes();
+ /**
+ * Constant for the field instructions
+ */
+ private static final byte[] FIELD_INSTRUCTIONS = "\\*\\fldinst".getBytes();
+ /**
+ * Constant for the field result
+ */
+ private static final byte[] FIELD_RESULT = "\\fldrslt".getBytes();
+
+ /**
+ * Is the field dirty
+ */
+ private boolean fieldDirty = false;
+ /**
+ * Is the field edited
+ */
+ private boolean fieldEdit = false;
+ /**
+ * Is the field locked
+ */
+ private boolean fieldLocked = false;
+ /**
+ * Is the field private
+ */
+ private boolean fieldPrivate = false;
+ /**
+ * Is it an alt field
+ */
+ private boolean fieldAlt = false;
+ /**
+ * Whether this RtfField is in a table
+ */
+ private boolean inTable = false;
+ /**
+ * Whether this RtfElement is in a header
+ */
+ private boolean inHeader = false;
+ /**
+ * The RtfDocument this RtfField belongs to
+ */
+ protected RtfDocument document = null;
+ /**
+ * The RtfFont of this RtfField
+ */
+ private RtfFont font = null;
+
+ /**
+ * Constructs a RtfField for a RtfDocument. This is not very usefull,
+ * since the RtfField by itself does not do anything. Use one of the
+ * subclasses instead.
+ *
+ * @param doc The RtfDocument this RtfField belongs to.
+ */
+ protected RtfField(RtfDocument doc) {
+ this(doc, new Font());
+ }
+
+ /**
+ * Constructs a RtfField for a RtfDocument. This is not very usefull,
+ * since the RtfField by itself does not do anything. Use one of the
+ * subclasses instead.
+ *
+ * @param doc The RtfDocument this RtfField belongs to.
+ * @param font The Font this RtfField should use
+ */
+ protected RtfField(RtfDocument doc, Font font) {
+ super("", font);
+ this.document = doc;
+ this.font = new RtfFont(this.document, font);
+ }
+
+ /**
+ * Sets the RtfDocument this RtfElement belongs to
+ *
+ * @param doc The RtfDocument to use
+ */
+ public void setRtfDocument(RtfDocument doc) {
+ this.document = doc;
+ this.font.setRtfDocument(this.document);
+ }
+
+ /**
+ * Writes the field beginning. Also writes field properties.
+ *
+ * @return A byte array with the field beginning.
+ * @throws IOException
+ */
+ private byte[] writeFieldBegin() throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+ result.write(OPEN_GROUP);
+ result.write(FIELD);
+
+ if (fieldDirty) {
+ result.write(FIELD_DIRTY);
+ }
+ if (fieldEdit) {
+ result.write(FIELD_EDIT);
+ }
+ if (fieldLocked) {
+ result.write(FIELD_LOCKED);
+ }
+ if (fieldPrivate) {
+ result.write(FIELD_PRIVATE);
+ }
+
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the beginning of the field instruction area.
+ *
+ * @return The beginning of the field instruction area
+ * @throws IOException
+ */
+ private byte[] writeFieldInstBegin() throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+ result.write(OPEN_GROUP);
+ result.write(FIELD_INSTRUCTIONS);
+ result.write(DELIMITER);
+
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the content of the field instruction area. Override this
+ * method in your subclasses.
+ *
+ * @return The content of the field instruction area
+ * @throws IOException If an error occurs.
+ */
+ protected abstract byte[] writeFieldInstContent() throws IOException;
+
+ /**
+ * Writes the end of the field instruction area.
+ *
+ * @return A byte array containing the end of the field instruction area
+ * @throws IOException
+ */
+ private byte[] writeFieldInstEnd() throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+ if (fieldAlt) {
+ result.write(DELIMITER);
+ result.write(FIELD_ALT);
+ }
+ result.write(CLOSE_GROUP);
+
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the beginning of the field result area
+ *
+ * @return A byte array containing the beginning of the field result area
+ * @throws IOException
+ */
+ private byte[] writeFieldResultBegin() throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+ result.write(OPEN_GROUP);
+ result.write(FIELD_RESULT);
+ result.write(DELIMITER);
+
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the content of the pre-calculated field result. Override this
+ * method in your subclasses.
+ *
+ * @return A byte array containing the field result
+ * @throws IOException If an error occurs
+ */
+ protected abstract byte[] writeFieldResultContent() throws IOException;
+
+ /**
+ * Writes the end of the field result area
+ *
+ * @return A byte array containing the end of the field result area
+ * @throws IOException
+ */
+ private byte[] writeFieldResultEnd() throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+ result.write(DELIMITER);
+ result.write(CLOSE_GROUP);
+
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the end of the field
+ *
+ * @return A byte array containing the end of the field
+ * @throws IOException
+ */
+ private byte[] writeFieldEnd() throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+ result.write(CLOSE_GROUP);
+
+ return result.toByteArray();
+ }
+
+ /**
+ * Write the content of this RtfField.
+ *
+ * @return A byte array containing the content of this RtfField
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(this.font.writeBegin());
+ result.write(writeFieldBegin());
+ result.write(writeFieldInstBegin());
+ result.write(writeFieldInstContent());
+ result.write(writeFieldInstEnd());
+ result.write(writeFieldResultBegin());
+ result.write(writeFieldResultContent());
+ result.write(writeFieldResultEnd());
+ result.write(writeFieldEnd());
+ result.write(this.font.writeEnd());
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Get whether this field is an alt field
+ *
+ * @return Returns whether this field is an alt field
+ */
+ public boolean isFieldAlt() {
+ return fieldAlt;
+ }
+
+ /**
+ * Set whether this field is an alt field
+ *
+ * @param fieldAlt The value to use
+ */
+ public void setFieldAlt(boolean fieldAlt) {
+ this.fieldAlt = fieldAlt;
+ }
+
+ /**
+ * Get whether this field is dirty
+ *
+ * @return Returns whether this field is dirty
+ */
+ public boolean isFieldDirty() {
+ return fieldDirty;
+ }
+
+ /**
+ * Set whether this field is dirty
+ *
+ * @param fieldDirty The value to use
+ */
+ public void setFieldDirty(boolean fieldDirty) {
+ this.fieldDirty = fieldDirty;
+ }
+
+ /**
+ * Get whether this field is edited
+ *
+ * @return Returns whether this field is edited
+ */
+ public boolean isFieldEdit() {
+ return fieldEdit;
+ }
+
+ /**
+ * Set whether this field is edited.
+ *
+ * @param fieldEdit The value to use
+ */
+ public void setFieldEdit(boolean fieldEdit) {
+ this.fieldEdit = fieldEdit;
+ }
+
+ /**
+ * Get whether this field is locked
+ *
+ * @return Returns the fieldLocked.
+ */
+ public boolean isFieldLocked() {
+ return fieldLocked;
+ }
+
+ /**
+ * Set whether this field is locked
+ * @param fieldLocked The value to use
+ */
+ public void setFieldLocked(boolean fieldLocked) {
+ this.fieldLocked = fieldLocked;
+ }
+
+ /**
+ * Get whether this field is private
+ *
+ * @return Returns the fieldPrivate.
+ */
+ public boolean isFieldPrivate() {
+ return fieldPrivate;
+ }
+
+ /**
+ * Set whether this field is private
+ *
+ * @param fieldPrivate The value to use
+ */
+ public void setFieldPrivate(boolean fieldPrivate) {
+ this.fieldPrivate = fieldPrivate;
+ }
+
+ /**
+ * Sets whether this RtfField is in a table
+ *
+ * @param inTable True
if this RtfField is in a table, false
otherwise
+ */
+ public void setInTable(boolean inTable) {
+ this.inTable = inTable;
+ }
+
+ /**
+ * Sets whether this RtfField is in a header
+ *
+ * @param inHeader True
if this RtfField is in a header, false
otherwise
+ */
+ public void setInHeader(boolean inHeader) {
+ this.inHeader = inHeader;
+ }
+
+ /**
+ * An RtfField is never empty.
+ */
+ public boolean isEmpty() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/field/RtfPageNumber.java b/src/main/java/com/lowagie/text/rtf/field/RtfPageNumber.java
new file mode 100644
index 0000000..3b69295
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/field/RtfPageNumber.java
@@ -0,0 +1,83 @@
+/*
+ * Created on Aug 10, 2004
+ *
+ * To change the template for this generated file go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+package com.lowagie.text.rtf.field;
+
+import java.io.IOException;
+
+import com.lowagie.text.Font;
+import com.lowagie.text.rtf.document.RtfDocument;
+
+
+/**
+ * The RtfPageNumber provides the page number field in rtf documents.
+ *
+ * @version $Revision: 1.7 $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ * @author Steffen.Stundzig@smb-tec.com
+ */
+public class RtfPageNumber extends RtfField {
+
+ /**
+ * Constant for the page number
+ */
+ private static final byte[] PAGE_NUMBER = "PAGE".getBytes();
+
+ /**
+ * Constructs a RtfPageNumber. This can be added anywhere to add a page number field.
+ */
+ public RtfPageNumber() {
+ super(null);
+ }
+
+ /**
+ * Constructs a RtfPageNumber with a specified Font. This can be added anywhere to
+ * add a page number field.
+ * @param font
+ */
+ public RtfPageNumber(Font font) {
+ super(null, font);
+ }
+
+ /**
+ * Constructs a RtfPageNumber object.
+ *
+ * @param doc The RtfDocument this RtfPageNumber belongs to
+ */
+ public RtfPageNumber(RtfDocument doc) {
+ super(doc);
+ }
+
+ /**
+ * Constructs a RtfPageNumber object with a specific font.
+ *
+ * @param doc The RtfDocument this RtfPageNumber belongs to
+ * @param font The Font to use
+ */
+ public RtfPageNumber(RtfDocument doc, Font font) {
+ super(doc, font);
+ }
+
+ /**
+ * Writes the field instruction content
+ *
+ * @return A byte array containing "PAGE"
+ * @throws IOException
+ */
+ protected byte[] writeFieldInstContent() throws IOException {
+ return PAGE_NUMBER;
+ }
+
+ /**
+ * Writes the field result content
+ *
+ * @return An empty byte array
+ * @throws IOException
+ */
+ protected byte[] writeFieldResultContent() throws IOException {
+ return new byte[0];
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/field/RtfTOCEntry.java b/src/main/java/com/lowagie/text/rtf/field/RtfTOCEntry.java
new file mode 100644
index 0000000..f65e424
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/field/RtfTOCEntry.java
@@ -0,0 +1,163 @@
+/*
+ * $Id: RtfTOCEntry.java,v 1.9 2004/12/25 10:18:11 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2004 by Mark Hall
+ * Uses code Copyright 2002
+ * Steffen.Stundzig@smb-tec.com
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.field;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.Font;
+
+
+/**
+ * The RtfTOCEntry is used together with the RtfTableOfContents to generate a table of
+ * contents. Add the RtfTOCEntry in those locations in the document where table of
+ * contents entries should link to
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ * @author Steffen.Stundzig@smb-tec.com
+ */
+public class RtfTOCEntry extends RtfField {
+
+ /**
+ * Constant for the beginning of hidden text
+ */
+ private static final byte[] TEXT_HIDDEN_ON = "\\v".getBytes();
+ /**
+ * Constant for the end of hidden text
+ */
+ private static final byte[] TEXT_HIDDEN_OFF = "\\v0".getBytes();
+ /**
+ * Constant for a TOC entry with page numbers
+ */
+ private static final byte[] TOC_ENTRY_PAGE_NUMBER = "\\tc".getBytes();
+ /**
+ * Constant for a TOC entry without page numbers
+ */
+ private static final byte[] TOC_ENTRY_NO_PAGE_NUMBER = "\\tcn".getBytes();
+
+ /**
+ * The entry text of this RtfTOCEntry
+ */
+ private String entry = "";
+ /**
+ * Whether to show page numbers in the table of contents
+ */
+ private boolean showPageNumber = true;
+
+ /**
+ * Constructs a RtfTOCEntry with a certain entry text.
+ *
+ * @param entry The entry text to display
+ * @param font The Font to use
+ */
+ public RtfTOCEntry(String entry, Font font) {
+ super(null, font);
+ if(entry != null) {
+ this.entry = entry;
+ }
+ }
+
+ /**
+ * Writes the content of the RtfTOCEntry
+ *
+ * @return A byte array with the contents of the RtfTOCEntry
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(TEXT_HIDDEN_ON);
+ result.write(OPEN_GROUP);
+ if(this.showPageNumber) {
+ result.write(TOC_ENTRY_PAGE_NUMBER);
+ } else {
+ result.write(TOC_ENTRY_NO_PAGE_NUMBER);
+ }
+ result.write(DELIMITER);
+ result.write(this.document.filterSpecialChar(this.entry, true, false).getBytes());
+ result.write(CLOSE_GROUP);
+ result.write(TEXT_HIDDEN_OFF);
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Sets whether to display a page number in the table of contents, or not
+ *
+ * @param showPageNumber Whether to display a page number or not
+ */
+ public void setShowPageNumber(boolean showPageNumber) {
+ this.showPageNumber = showPageNumber;
+ }
+
+ /**
+ * UNUSED
+ * @return null
+ * @throws IOException never thrown
+ */
+ protected byte[] writeFieldInstContent() throws IOException {
+ return null;
+ }
+
+ /**
+ * UNUSED
+ * @return null
+ * @throws IOException never thrown
+ */
+ protected byte[] writeFieldResultContent() throws IOException {
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/rtf/field/RtfTableOfContents.java b/src/main/java/com/lowagie/text/rtf/field/RtfTableOfContents.java
new file mode 100644
index 0000000..b6738c1
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/field/RtfTableOfContents.java
@@ -0,0 +1,109 @@
+/*
+ * $Id: RtfTableOfContents.java,v 1.8 2004/12/14 12:51:54 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2004 by Mark Hall
+ * Uses code Copyright 2002
+ * Steffen.Stundzig@smb-tec.com
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.field;
+
+import java.io.IOException;
+
+import com.lowagie.text.Font;
+
+
+
+/**
+ * The RtfTableOfContents together with multiple RtfTOCEntry objects generates a table
+ * of contents. The table of contents will display no entries in the viewing program
+ * and the user will have to update it first. A text to inform the user of this is
+ * displayed instead.
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ * @author Steffen.Stundzig@smb-tec.com
+ */
+public class RtfTableOfContents extends RtfField {
+
+ /**
+ * The default text to display
+ */
+ private String defaultText = "Table of Contents - Click to update";
+
+ /**
+ * Constructs a RtfTableOfContents. The default text is the text that is displayed
+ * before the user updates the table of contents
+ *
+ * @param defaultText The default text to display
+ * @param font The Font to use
+ */
+ public RtfTableOfContents(String defaultText, Font font) {
+ super(null, font);
+ this.defaultText = defaultText;
+ }
+
+ /**
+ * Writes the field instruction content
+ *
+ * @return A byte array containing with the field instructions
+ * @throws IOException
+ */
+ protected byte[] writeFieldInstContent() throws IOException {
+ return "TOC \\\\f \\\\h \\\\u \\\\o \"1-5\" ".getBytes();
+ }
+
+ /**
+ * Writes the field result content
+ *
+ * @return An byte array containing the default text
+ * @throws IOException
+ */
+ protected byte[] writeFieldResultContent() throws IOException {
+ return defaultText.getBytes();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/field/RtfTotalPageNumber.java b/src/main/java/com/lowagie/text/rtf/field/RtfTotalPageNumber.java
new file mode 100644
index 0000000..a776fd6
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/field/RtfTotalPageNumber.java
@@ -0,0 +1,128 @@
+/*
+ * $Id: RtfTotalPageNumber.java,v 1.3 2005/05/13 12:09:14 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2005 Jose Hurtado jose.hurtado@gft.com
+ * Parts Copyright 2005 Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.field;
+
+import java.io.IOException;
+
+import com.lowagie.text.Font;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.field.RtfField;
+
+/**
+ * The RtfTotalPageNumber provides the total number of pages field in rtf documents.
+ *
+ * @version $Version:$
+ * @author Jose Hurtado (jose.hurtado@gft.com)
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfTotalPageNumber extends RtfField {
+
+ /**
+ * Constant for arabic total page numbers.
+ */
+ private static final byte[] ARABIC_TOTAL_PAGES = "NUMPAGES \\\\* Arabic".getBytes();
+
+ /**
+ * Constructs a RtfTotalPageNumber. This can be added anywhere to add a total number of pages field.
+ */
+ public RtfTotalPageNumber() {
+ super(null);
+ }
+
+ /**
+ * Constructs a RtfTotalPageNumber with a specified Font. This can be added anywhere
+ * to add a total number of pages field.
+ * @param font
+ */
+ public RtfTotalPageNumber(Font font) {
+ super(null, font);
+ }
+
+ /**
+ * Constructs a RtfTotalPageNumber object.
+ *
+ * @param doc The RtfDocument this RtfTotalPageNumber belongs to
+ */
+ public RtfTotalPageNumber(RtfDocument doc) {
+ super(doc);
+ }
+
+ /**
+ * Constructs a RtfTotalPageNumber object with a specific font.
+ *
+ * @param doc The RtfDocument this RtfTotalPageNumber belongs to
+ * @param font The Font to use
+ */
+ public RtfTotalPageNumber(RtfDocument doc, Font font) {
+ super(doc, font);
+ }
+
+ /**
+ * Writes the field NUMPAGES instruction with Arabic format
+ *
+ * @return A byte array containing "NUMPAGES \\\\* Arabic".
+ * @throws IOException
+ */
+ protected byte[] writeFieldInstContent() throws IOException {
+ return ARABIC_TOTAL_PAGES;
+ }
+
+ /**
+ * Writes the field result content
+ *
+ * @return An byte array containing "1".
+ * @throws IOException
+ */
+ protected byte[] writeFieldResultContent() throws IOException {
+ return "1".getBytes();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/graphic/RtfImage.java b/src/main/java/com/lowagie/text/rtf/graphic/RtfImage.java
new file mode 100644
index 0000000..c0446f8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/graphic/RtfImage.java
@@ -0,0 +1,306 @@
+/*
+ * $Id: RtfImage.java,v 1.23 2005/12/24 13:14:28 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.graphic;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.Image;
+import com.lowagie.text.pdf.codec.wmf.MetaDo;
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.style.RtfParagraphStyle;
+import com.lowagie.text.rtf.text.RtfParagraph;
+
+
+/**
+ * The RtfImage contains one image. Supported image types are jpeg, png, wmf, bmp.
+ *
+ * @version $Revision: 1.23 $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ * @author Paulo Soares
+ */
+public class RtfImage extends RtfElement {
+
+ /**
+ * Constant for the shape/picture group
+ */
+ private static final byte[] PICTURE_GROUP = "\\*\\shppict".getBytes();
+ /**
+ * Constant for a picture
+ */
+ private static final byte[] PICTURE = "\\pict".getBytes();
+ /**
+ * Constant for a jpeg image
+ */
+ private static final byte[] PICTURE_JPEG = "\\jpegblip".getBytes();
+ /**
+ * Constant for a png image
+ */
+ private static final byte[] PICTURE_PNG = "\\pngblip".getBytes();
+ /**
+ * Constant for a bmp image
+ */
+ private static final byte[] PICTURE_BMP = "\\dibitmap0".getBytes();
+ /**
+ * Constant for a wmf image
+ */
+ private static final byte[] PICTURE_WMF = "\\wmetafile8".getBytes();
+ /**
+ * Constant for the picture width
+ */
+ private static final byte[] PICTURE_WIDTH = "\\picw".getBytes();
+ /**
+ * Constant for the picture height
+ */
+ private static final byte[] PICTURE_HEIGHT = "\\pich".getBytes();
+ /**
+ * Constant for the picture width scale
+ */
+ private static final byte[] PICTURE_SCALED_WIDTH = "\\picwgoal".getBytes();
+ /**
+ * Constant for the picture height scale
+ */
+ private static final byte[] PICTURE_SCALED_HEIGHT = "\\pichgoal".getBytes();
+
+ /**
+ * The type of image this is.
+ */
+ private int imageType = Image.ORIGINAL_NONE;
+ /**
+ * The actual image. Already formated for direct inclusion in the rtf document
+ */
+ private byte[] image = new byte[0];
+ /**
+ * The alignment of this picture
+ */
+ private int alignment = Element.ALIGN_LEFT;
+ /**
+ * The width of this picture
+ */
+ private float width = 0;
+ /**
+ * The height of this picutre
+ */
+ private float height = 0;
+ /**
+ * The intended display width of this picture
+ */
+ private float plainWidth = 0;
+ /**
+ * The intended display height of this picture
+ */
+ private float plainHeight = 0;
+ /**
+ * Whether this RtfImage is a top level element and should
+ * be an extra paragraph.
+ */
+ private boolean topLevelElement = false;
+
+ /**
+ * Constructs a RtfImage for an Image.
+ *
+ * @param doc The RtfDocument this RtfImage belongs to
+ * @param image The Image that this RtfImage wraps
+ * @throws DocumentException If an error occured accessing the image content
+ */
+ public RtfImage(RtfDocument doc, Image image) throws DocumentException {
+ super(doc);
+ imageType = image.getOriginalType();
+ if (!(imageType == Image.ORIGINAL_JPEG || imageType == Image.ORIGINAL_BMP
+ || imageType == Image.ORIGINAL_PNG || imageType == Image.ORIGINAL_WMF)) {
+ throw new DocumentException("Only BMP, PNG, WMF and JPEG images are supported by the RTF Writer");
+ }
+ alignment = image.alignment();
+ width = image.width();
+ height = image.height();
+ plainWidth = image.plainWidth();
+ plainHeight = image.plainHeight();
+ this.image = getImage(image);
+ }
+
+ /**
+ * Extracts the image data from the Image. The data is formated for direct inclusion
+ * in a rtf document
+ *
+ * @param image The Image for which to extract the content
+ * @return The image data formated for the rtf document
+ * @throws DocumentException If an error occurs accessing the image content
+ */
+ private byte[] getImage(Image image) throws DocumentException {
+ ByteArrayOutputStream imageTemp = new ByteArrayOutputStream();
+ try {
+ InputStream imageIn;
+ if (imageType == Image.ORIGINAL_BMP) {
+ imageIn = new ByteArrayInputStream(MetaDo.wrapBMP(image));
+ } else {
+ if (image.getOriginalData() == null) {
+ imageIn = image.url().openStream();
+ } else {
+ imageIn = new ByteArrayInputStream(image.getOriginalData());
+ }
+ if (imageType == Image.ORIGINAL_WMF) { //remove the placeable header
+ long skipLength = 22;
+ while(skipLength > 0) {
+ skipLength = skipLength - imageIn.skip(skipLength);
+ }
+ }
+ }
+ int buffer = 0;
+ int count = 0;
+ while((buffer = imageIn.read()) != -1) {
+ String helperStr = Integer.toHexString(buffer);
+ if (helperStr.length() < 2) helperStr = "0" + helperStr;
+ imageTemp.write(helperStr.getBytes());
+ count++;
+ if (count == 64) {
+ imageTemp.write((byte) '\n');
+ count = 0;
+ }
+ }
+ } catch(IOException ioe) {
+ throw new DocumentException(ioe.getMessage());
+ }
+ return imageTemp.toByteArray();
+ }
+
+ /**
+ * Writes the RtfImage content
+ *
+ * @return the RtfImage content
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ if(this.topLevelElement) {
+ result.write(RtfParagraph.PARAGRAPH_DEFAULTS);
+ }
+ switch(alignment) {
+ case Element.ALIGN_LEFT:
+ result.write(RtfParagraphStyle.ALIGN_LEFT);
+ break;
+ case Element.ALIGN_RIGHT:
+ result.write(RtfParagraphStyle.ALIGN_RIGHT);
+ break;
+ case Element.ALIGN_CENTER:
+ result.write(RtfParagraphStyle.ALIGN_CENTER);
+ break;
+ case Element.ALIGN_JUSTIFIED:
+ result.write(RtfParagraphStyle.ALIGN_JUSTIFY);
+ break;
+ }
+ result.write(OPEN_GROUP);
+ result.write(PICTURE_GROUP);
+ result.write(OPEN_GROUP);
+ result.write(PICTURE);
+ switch(imageType) {
+ case Image.ORIGINAL_JPEG:
+ result.write(PICTURE_JPEG);
+ break;
+ case Image.ORIGINAL_PNG:
+ result.write(PICTURE_PNG);
+ break;
+ case Image.ORIGINAL_WMF:
+ case Image.ORIGINAL_BMP:
+ result.write(PICTURE_WMF);
+ break;
+ }
+ result.write(PICTURE_WIDTH);
+ result.write(intToByteArray((int) width));
+ result.write(PICTURE_HEIGHT);
+ result.write(intToByteArray((int) height));
+ if(width != plainWidth || this.imageType == Image.ORIGINAL_BMP) {
+ result.write(PICTURE_SCALED_WIDTH);
+ result.write(intToByteArray((int) (plainWidth * RtfElement.TWIPS_FACTOR)));
+ }
+ if(height != plainHeight || this.imageType == Image.ORIGINAL_BMP) {
+ result.write(PICTURE_SCALED_HEIGHT);
+ result.write(intToByteArray((int) (plainHeight * RtfElement.TWIPS_FACTOR)));
+ }
+ result.write(DELIMITER);
+ result.write((byte) '\n');
+ result.write(image);
+ result.write(CLOSE_GROUP);
+ result.write(CLOSE_GROUP);
+ if(this.topLevelElement) {
+ result.write(RtfParagraph.PARAGRAPH);
+ result.write(RtfParagraph.PARAGRAPH);
+ }
+ result.write((byte) '\n');
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Sets the alignment of this RtfImage. Uses the alignments from com.lowagie.text.Element.
+ *
+ * @param alignment The alignment to use.
+ */
+ public void setAlignment(int alignment) {
+ this.alignment = alignment;
+ }
+
+ /**
+ * Set whether this RtfImage should behave like a top level element
+ * and enclose itself in a paragraph.
+ *
+ * @param topLevelElement Whether to behave like a top level element.
+ */
+ public void setTopLevelElement(boolean topLevelElement) {
+ this.topLevelElement = topLevelElement;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/headerfooter/RtfHeaderFooter.java b/src/main/java/com/lowagie/text/rtf/headerfooter/RtfHeaderFooter.java
new file mode 100644
index 0000000..d338f71
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/headerfooter/RtfHeaderFooter.java
@@ -0,0 +1,326 @@
+/*
+ * Created on Aug 10, 2004
+ *
+ * To change the template for this generated file go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+package com.lowagie.text.rtf.headerfooter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.HeaderFooter;
+import com.lowagie.text.Image;
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Table;
+import com.lowagie.text.rtf.RtfBasicElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.field.RtfPageNumber;
+
+
+/**
+ * The RtfHeaderFooter represents one header or footer. This class can be used
+ * directly.
+ *
+ * @version $Id: RtfHeaderFooter.java,v 1.9 2005/09/21 15:26:01 hallm Exp $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfHeaderFooter extends HeaderFooter implements RtfBasicElement {
+
+ /**
+ * Constant for the header type
+ */
+ public static final int TYPE_HEADER = 1;
+ /**
+ * Constant for the footer type
+ */
+ public static final int TYPE_FOOTER = 2;
+ /**
+ * Constant for displaying the header/footer on the first page
+ */
+ public static final int DISPLAY_FIRST_PAGE = 0;
+ /**
+ * Constant for displaying the header/footer on all pages
+ */
+ public static final int DISPLAY_ALL_PAGES = 1;
+ /**
+ * Constant for displaying the header/footer on all left hand pages
+ */
+ public static final int DISPLAY_LEFT_PAGES = 2;
+ /**
+ * Constant for displaying the header/footer on all right hand pages
+ */
+ public static final int DISPLAY_RIGHT_PAGES = 4;
+
+ /**
+ * Constant for a header on all pages
+ */
+ private static final byte[] HEADER_ALL = "\\header".getBytes();
+ /**
+ * Constant for a header on the first page
+ */
+ private static final byte[] HEADER_FIRST = "\\headerf".getBytes();
+ /**
+ * Constant for a header on all left hand pages
+ */
+ private static final byte[] HEADER_LEFT = "\\headerl".getBytes();
+ /**
+ * Constant for a header on all right hand pages
+ */
+ private static final byte[] HEADER_RIGHT = "\\headerr".getBytes();
+ /**
+ * Constant for a footer on all pages
+ */
+ private static final byte[] FOOTER_ALL = "\\footer".getBytes();
+ /**
+ * Constant for a footer on the first page
+ */
+ private static final byte[] FOOTER_FIRST = "\\footerf".getBytes();
+ /**
+ * Constnat for a footer on the left hand pages
+ */
+ private static final byte[] FOOTER_LEFT = "\\footerl".getBytes();
+ /**
+ * Constant for a footer on the right hand pages
+ */
+ private static final byte[] FOOTER_RIGHT = "\\footerr".getBytes();
+
+ /**
+ * The RtfDocument this RtfHeaderFooter belongs to
+ */
+ private RtfDocument document = null;
+ /**
+ * The content of this RtfHeaderFooter
+ */
+ private Object content = null;
+ /**
+ * The display type of this RtfHeaderFooter. TYPE_HEADER or TYPE_FOOTER
+ */
+ private int type = TYPE_HEADER;
+ /**
+ * The display location of this RtfHeaderFooter. DISPLAY_FIRST_PAGE,
+ * DISPLAY_LEFT_PAGES, DISPLAY_RIGHT_PAGES or DISPLAY_ALL_PAGES
+ */
+ private int displayAt = DISPLAY_ALL_PAGES;
+
+ /**
+ * Constructs a RtfHeaderFooter based on a HeaderFooter with a certain type and displayAt
+ * location. For internal use only.
+ *
+ * @param doc The RtfDocument this RtfHeaderFooter belongs to
+ * @param headerFooter The HeaderFooter to base this RtfHeaderFooter on
+ * @param type The type of RtfHeaderFooter
+ * @param displayAt The display location of this RtfHeaderFooter
+ */
+ protected RtfHeaderFooter(RtfDocument doc, HeaderFooter headerFooter, int type, int displayAt) {
+ super(new Phrase(""), false);
+ this.document = doc;
+ this.type = type;
+ this.displayAt = displayAt;
+ Paragraph par = new Paragraph();
+ par.setAlignment(headerFooter.alignment());
+ if (headerFooter.getBefore() != null) {
+ par.add(headerFooter.getBefore());
+ }
+ if (headerFooter.isNumbered()) {
+ par.add(new RtfPageNumber(this.document));
+ }
+ if (headerFooter.getAfter() != null) {
+ par.add(headerFooter.getAfter());
+ }
+ try {
+ if(this.document != null) {
+ content = this.document.getMapper().mapElement(par);
+ ((RtfBasicElement) this.content).setInHeader(true);
+ } else {
+ content = par;
+ }
+ } catch(DocumentException de) {
+ de.printStackTrace();
+ }
+ }
+
+ /**
+ * Constructs a RtfHeaderFooter as a copy of an existing RtfHeaderFooter.
+ * For internal use only.
+ *
+ * @param doc The RtfDocument this RtfHeaderFooter belongs to
+ * @param headerFooter The RtfHeaderFooter to copy
+ * @param displayAt The display location of this RtfHeaderFooter
+ */
+ protected RtfHeaderFooter(RtfDocument doc, RtfHeaderFooter headerFooter, int displayAt) {
+ super(new Phrase(""), false);
+ this.document = doc;
+ this.content = headerFooter.getContent();
+ this.displayAt = displayAt;
+ if(this.content instanceof Element) {
+ try {
+ this.content = this.document.getMapper().mapElement((Element) this.content);
+ } catch(DocumentException de) {
+ de.printStackTrace();
+ }
+ }
+ ((RtfBasicElement) this.content).setInHeader(true);
+ }
+
+ /**
+ * Constructs a RtfHeaderFooter for a HeaderFooter.
+ *
+ * @param doc The RtfDocument this RtfHeaderFooter belongs to
+ * @param headerFooter The HeaderFooter to base this RtfHeaderFooter on
+ */
+ protected RtfHeaderFooter(RtfDocument doc, HeaderFooter headerFooter) {
+ super(new Phrase(""), false);
+ this.document = doc;
+ Paragraph par = new Paragraph();
+ par.setAlignment(headerFooter.alignment());
+ if (headerFooter.getBefore() != null) {
+ par.add(headerFooter.getBefore());
+ }
+ if (headerFooter.isNumbered()) {
+ par.add(new RtfPageNumber(this.document));
+ }
+ if (headerFooter.getAfter() != null) {
+ par.add(headerFooter.getAfter());
+ }
+ try {
+ content = doc.getMapper().mapElement(par);
+ ((RtfBasicElement) this.content).setInHeader(true);
+ } catch(DocumentException de) {
+ de.printStackTrace();
+ }
+ }
+
+ /**
+ * Constructs a RtfHeaderFooter for any Element.
+ *
+ * @param element The Element to display as content of this RtfHeaderFooter
+ */
+ public RtfHeaderFooter(Element element) {
+ super(new Phrase(""), false);
+ this.content = element;
+ }
+
+ /**
+ * Sets the RtfDocument this RtfElement belongs to
+ *
+ * @param doc The RtfDocument to use
+ */
+ public void setRtfDocument(RtfDocument doc) {
+ this.document = doc;
+ if(this.document != null) {
+ try {
+ if(this.content instanceof Element) {
+ this.content = this.document.getMapper().mapElement((Element) this.content);
+ ((RtfBasicElement) this.content).setInHeader(true);
+ } else if(this.content instanceof RtfBasicElement){
+ ((RtfBasicElement) this.content).setRtfDocument(this.document);
+ ((RtfBasicElement) this.content).setInHeader(true);
+ }
+ } catch(DocumentException de) {
+ de.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Writes the content of this RtfHeaderFooter
+ *
+ * @return A byte array with the content of this RtfHeaderFooter
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(OPEN_GROUP);
+ if(this.type == TYPE_HEADER) {
+ if(this.displayAt == DISPLAY_ALL_PAGES) {
+ result.write(HEADER_ALL);
+ } else if(this.displayAt == DISPLAY_FIRST_PAGE) {
+ result.write(HEADER_FIRST);
+ } else if(this.displayAt == DISPLAY_LEFT_PAGES) {
+ result.write(HEADER_LEFT);
+ } else if(this.displayAt == DISPLAY_RIGHT_PAGES) {
+ result.write(HEADER_RIGHT);
+ }
+ } else {
+ if(this.displayAt == DISPLAY_ALL_PAGES) {
+ result.write(FOOTER_ALL);
+ } else if(this.displayAt == DISPLAY_FIRST_PAGE) {
+ result.write(FOOTER_FIRST);
+ } else if(this.displayAt == DISPLAY_LEFT_PAGES) {
+ result.write(FOOTER_LEFT);
+ } else if(this.displayAt == DISPLAY_RIGHT_PAGES) {
+ result.write(FOOTER_RIGHT);
+ }
+ }
+ result.write(DELIMITER);
+ if(content instanceof RtfBasicElement) {
+ result.write(((RtfBasicElement) this.content).write());
+ }
+ result.write(CLOSE_GROUP);
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+
+ /**
+ * Sets the display location of this RtfHeaderFooter
+ *
+ * @param displayAt The display location to use.
+ */
+ public void setDisplayAt(int displayAt) {
+ this.displayAt = displayAt;
+ }
+
+ /**
+ * Sets the type of this RtfHeaderFooter
+ *
+ * @param type The type to use.
+ */
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ /**
+ * Gets the content of this RtfHeaderFooter
+ *
+ * @return The content of this RtfHeaderFooter
+ */
+ private Object getContent() {
+ return this.content;
+ }
+
+ /**
+ * Unused
+ * @param inTable
+ */
+ public void setInTable(boolean inTable) {
+ }
+
+ /**
+ * Unused
+ * @param inHeader
+ */
+ public void setInHeader(boolean inHeader) {
+ }
+
+ /**
+ * Set the alignment of this RtfHeaderFooter. Passes the setting
+ * on to the contained element.
+ */
+ public void setAlignment(int alignment) {
+ super.setAlignment(alignment);
+ if(this.content instanceof Paragraph) {
+ ((Paragraph) this.content).setAlignment(alignment);
+ } else if(this.content instanceof Table) {
+ ((Table) this.content).setAlignment(alignment);
+ } else if(this.content instanceof Image) {
+ ((Image) this.content).setAlignment(alignment);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/headerfooter/RtfHeaderFooterGroup.java b/src/main/java/com/lowagie/text/rtf/headerfooter/RtfHeaderFooterGroup.java
new file mode 100644
index 0000000..e0934bf
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/headerfooter/RtfHeaderFooterGroup.java
@@ -0,0 +1,393 @@
+/*
+ * Created on Aug 6, 2004
+ *
+ * To change the template for this generated file go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+package com.lowagie.text.rtf.headerfooter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.HeaderFooter;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.rtf.RtfBasicElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+
+
+/**
+ * The RtfHeaderFooterGroup holds 0 - 3 RtfHeaderFooters that create a group
+ * of headers or footers.
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfHeaderFooterGroup extends HeaderFooter implements RtfBasicElement {
+
+ /**
+ * This RtfHeaderFooterGroup contains no RtfHeaderFooter objects
+ */
+ private static final int MODE_NONE = 0;
+ /**
+ * This RtfHeaderFooterGroup contains one RtfHeaderFooter object
+ */
+ private static final int MODE_SINGLE = 1;
+ /**
+ * This RtfHeaderFooterGroup contains two or three RtfHeaderFooter objects
+ */
+ private static final int MODE_MULTIPLE = 2;
+
+ /**
+ * The current mode of this RtfHeaderFooterGroup. Defaults to MODE_NONE
+ */
+ private int mode = MODE_NONE;
+ /**
+ * The current type of this RtfHeaderFooterGroup. Defaults to RtfHeaderFooter.TYPE_HEADER
+ */
+ private int type = RtfHeaderFooter.TYPE_HEADER;
+
+ /**
+ * The RtfHeaderFooter for all pages
+ */
+ private RtfHeaderFooter headerAll = null;
+ /**
+ * The RtfHeaderFooter for the first page
+ */
+ private RtfHeaderFooter headerFirst = null;
+ /**
+ * The RtfHeaderFooter for the left hand pages
+ */
+ private RtfHeaderFooter headerLeft = null;
+ /**
+ * The RtfHeaderFooter for the right hand pages
+ */
+ private RtfHeaderFooter headerRight = null;
+ /**
+ * The RtfDocument this RtfHeaderFooterGroup belongs to
+ */
+ private RtfDocument document = null;
+
+ /**
+ * Constructs a RtfHeaderGroup to which you add headers/footers using
+ * via the setHeaderFooter method.
+ *
+ */
+ public RtfHeaderFooterGroup() {
+ super(new Phrase(""), false);
+ this.mode = MODE_NONE;
+ }
+
+ /**
+ * Constructs a certain type of RtfHeaderFooterGroup. RtfHeaderFooter.TYPE_HEADER
+ * and RtfHeaderFooter.TYPE_FOOTER are valid values for type.
+ *
+ * @param doc The RtfDocument this RtfHeaderFooter belongs to
+ * @param type The type of RtfHeaderFooterGroup to create
+ */
+ public RtfHeaderFooterGroup(RtfDocument doc, int type) {
+ super(new Phrase(""), false);
+ this.document = doc;
+ this.type = type;
+ }
+
+ /**
+ * Constructs a RtfHeaderFooterGroup by copying the content of the original
+ * RtfHeaderFooterGroup
+ *
+ * @param doc The RtfDocument this RtfHeaderFooter belongs to
+ * @param headerFooter The RtfHeaderFooterGroup to copy
+ * @param type The type of RtfHeaderFooterGroup to create
+ */
+ public RtfHeaderFooterGroup(RtfDocument doc, RtfHeaderFooterGroup headerFooter, int type) {
+ super(new Phrase(""), false);
+ this.document = doc;
+ this.mode = headerFooter.getMode();
+ this.type = type;
+ if(headerFooter.getHeaderAll() != null) {
+ this.headerAll = new RtfHeaderFooter(this.document, headerFooter.getHeaderAll(), RtfHeaderFooter.DISPLAY_ALL_PAGES);
+ }
+ if(headerFooter.getHeaderFirst() != null) {
+ this.headerFirst = new RtfHeaderFooter(this.document, headerFooter.getHeaderFirst(), RtfHeaderFooter.DISPLAY_FIRST_PAGE);
+ }
+ if(headerFooter.getHeaderLeft() != null) {
+ this.headerLeft = new RtfHeaderFooter(this.document, headerFooter.getHeaderLeft(), RtfHeaderFooter.DISPLAY_LEFT_PAGES);
+ }
+ if(headerFooter.getHeaderRight() != null) {
+ this.headerRight = new RtfHeaderFooter(this.document, headerFooter.getHeaderRight(), RtfHeaderFooter.DISPLAY_RIGHT_PAGES);
+ }
+ setType(this.type);
+ }
+
+ /**
+ * Constructs a RtfHeaderFooterGroup for a certain RtfHeaderFooter.
+ *
+ * @param doc The RtfDocument this RtfHeaderFooter belongs to
+ * @param headerFooter The RtfHeaderFooter to display
+ * @param type The typ of RtfHeaderFooterGroup to create
+ */
+ public RtfHeaderFooterGroup(RtfDocument doc, RtfHeaderFooter headerFooter, int type) {
+ super(new Phrase(""), false);
+ this.document = doc;
+ this.type = type;
+ this.mode = MODE_SINGLE;
+ headerAll = new RtfHeaderFooter(doc, headerFooter, RtfHeaderFooter.DISPLAY_ALL_PAGES);
+ headerAll.setType(this.type);
+ }
+
+ /**
+ * Constructs a RtfHeaderGroup for a certain HeaderFooter
+ *
+ * @param doc The RtfDocument this RtfHeaderFooter belongs to
+ * @param headerFooter The HeaderFooter to display
+ * @param type The typ of RtfHeaderFooterGroup to create
+ */
+ public RtfHeaderFooterGroup(RtfDocument doc, HeaderFooter headerFooter, int type) {
+ super(new Phrase(""), false);
+ this.document = doc;
+ this.type = type;
+ this.mode = MODE_SINGLE;
+ headerAll = new RtfHeaderFooter(doc, headerFooter, type, RtfHeaderFooter.DISPLAY_ALL_PAGES);
+ headerAll.setType(this.type);
+ }
+
+ /**
+ * Sets the RtfDocument this RtfElement belongs to
+ *
+ * @param doc The RtfDocument to use
+ */
+ public void setRtfDocument(RtfDocument doc) {
+ this.document = doc;
+ if(headerAll != null) {
+ headerAll.setRtfDocument(this.document);
+ }
+ if(headerFirst != null) {
+ headerFirst.setRtfDocument(this.document);
+ }
+ if(headerLeft != null) {
+ headerLeft.setRtfDocument(this.document);
+ }
+ if(headerRight != null) {
+ headerRight.setRtfDocument(this.document);
+ }
+ }
+
+ /**
+ * Write the content of this RtfHeaderFooterGroup.
+ *
+ * @return A byte array with the content of this RtfHeaderFooterGroup
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ if(this.mode == MODE_SINGLE) {
+ result.write(headerAll.write());
+ } else if(this.mode == MODE_MULTIPLE) {
+ if(headerFirst != null) {
+ result.write(headerFirst.write());
+ }
+ if(headerLeft != null) {
+ result.write(headerLeft.write());
+ }
+ if(headerRight != null) {
+ result.write(headerRight.write());
+ }
+ if(headerAll != null) {
+ result.write(headerAll.write());
+ }
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Set a RtfHeaderFooter to be displayed at a certain position
+ *
+ * @param headerFooter The RtfHeaderFooter to display
+ * @param displayAt The display location to use
+ */
+ public void setHeaderFooter(RtfHeaderFooter headerFooter, int displayAt) {
+ this.mode = MODE_MULTIPLE;
+ headerFooter.setRtfDocument(this.document);
+ headerFooter.setType(this.type);
+ headerFooter.setDisplayAt(displayAt);
+ switch(displayAt) {
+ case RtfHeaderFooter.DISPLAY_ALL_PAGES:
+ headerAll = headerFooter;
+ break;
+ case RtfHeaderFooter.DISPLAY_FIRST_PAGE:
+ headerFirst = headerFooter;
+ break;
+ case RtfHeaderFooter.DISPLAY_LEFT_PAGES:
+ headerLeft = headerFooter;
+ break;
+ case RtfHeaderFooter.DISPLAY_RIGHT_PAGES:
+ headerRight = headerFooter;
+ break;
+ }
+ }
+
+ /**
+ * Set a HeaderFooter to be displayed at a certain position
+ *
+ * @param headerFooter The HeaderFooter to set
+ * @param displayAt The display location to use
+ */
+ public void setHeaderFooter(HeaderFooter headerFooter, int displayAt) {
+ this.mode = MODE_MULTIPLE;
+ switch(displayAt) {
+ case RtfHeaderFooter.DISPLAY_ALL_PAGES:
+ headerAll = new RtfHeaderFooter(this.document, headerFooter, this.type, displayAt);
+ break;
+ case RtfHeaderFooter.DISPLAY_FIRST_PAGE:
+ headerFirst = new RtfHeaderFooter(this.document, headerFooter, this.type, displayAt);
+ break;
+ case RtfHeaderFooter.DISPLAY_LEFT_PAGES:
+ headerLeft = new RtfHeaderFooter(this.document, headerFooter, this.type, displayAt);
+ break;
+ case RtfHeaderFooter.DISPLAY_RIGHT_PAGES:
+ headerRight = new RtfHeaderFooter(this.document, headerFooter, this.type, displayAt);
+ break;
+ }
+ }
+
+ /**
+ * Set that this RtfHeaderFooterGroup should have a title page. If only
+ * a header / footer for all pages exists, then it will be copied to the
+ * first page aswell.
+ */
+ public void setHasTitlePage() {
+ if(this.mode == MODE_SINGLE) {
+ this.mode = MODE_MULTIPLE;
+ headerFirst = new RtfHeaderFooter(this.document, headerAll, RtfHeaderFooter.DISPLAY_FIRST_PAGE);
+ headerFirst.setType(this.type);
+ }
+ }
+
+ /**
+ * Set that this RtfHeaderFooterGroup should have facing pages. If only
+ * a header / footer for all pages exists, then it will be copied to the left
+ * and right pages aswell.
+ */
+ public void setHasFacingPages() {
+ if(this.mode == MODE_SINGLE) {
+ this.mode = MODE_MULTIPLE;
+ this.headerLeft = new RtfHeaderFooter(this.document, this.headerAll, RtfHeaderFooter.DISPLAY_LEFT_PAGES);
+ this.headerLeft.setType(this.type);
+ this.headerRight = new RtfHeaderFooter(this.document, this.headerAll, RtfHeaderFooter.DISPLAY_RIGHT_PAGES);
+ this.headerRight.setType(this.type);
+ this.headerAll = null;
+ } else if(this.mode == MODE_MULTIPLE) {
+ if(this.headerLeft == null && this.headerAll != null) {
+ this.headerLeft = new RtfHeaderFooter(this.document, this.headerAll, RtfHeaderFooter.DISPLAY_LEFT_PAGES);
+ this.headerLeft.setType(this.type);
+ }
+ if(this.headerRight == null && this.headerAll != null) {
+ this.headerRight = new RtfHeaderFooter(this.document, this.headerAll, RtfHeaderFooter.DISPLAY_RIGHT_PAGES);
+ this.headerRight.setType(this.type);
+ }
+ this.headerAll = null;
+ }
+ }
+
+ /**
+ * Get whether this RtfHeaderFooterGroup has a titlepage
+ *
+ * @return Whether this RtfHeaderFooterGroup has a titlepage
+ */
+ public boolean hasTitlePage() {
+ return (headerFirst != null);
+ }
+
+ /**
+ * Get whether this RtfHeaderFooterGroup has facing pages
+ *
+ * @return Whether this RtfHeaderFooterGroup has facing pages
+ */
+ public boolean hasFacingPages() {
+ return (headerLeft != null || headerRight != null);
+ }
+
+ /**
+ * Unused
+ * @param inTable
+ */
+ public void setInTable(boolean inTable) {
+ }
+
+ /**
+ * Unused
+ * @param inHeader
+ */
+ public void setInHeader(boolean inHeader) {
+ }
+
+ /**
+ * Set the type of this RtfHeaderFooterGroup. RtfHeaderFooter.TYPE_HEADER
+ * or RtfHeaderFooter.TYPE_FOOTER. Also sets the type for all RtfHeaderFooters
+ * of this RtfHeaderFooterGroup.
+ *
+ * @param type The type to use
+ */
+ public void setType(int type) {
+ this.type = type;
+ if(headerAll != null) {
+ headerAll.setType(this.type);
+ }
+ if(headerFirst != null) {
+ headerFirst.setType(this.type);
+ }
+ if(headerLeft != null) {
+ headerLeft.setType(this.type);
+ }
+ if(headerRight != null) {
+ headerRight.setType(this.type);
+ }
+ }
+
+ /**
+ * Gets the mode of this RtfHeaderFooterGroup
+ *
+ * @return The mode of this RtfHeaderFooterGroup
+ */
+ protected int getMode() {
+ return this.mode;
+ }
+
+ /**
+ * Gets the RtfHeaderFooter for all pages
+ *
+ * @return The RtfHeaderFooter for all pages
+ */
+ protected RtfHeaderFooter getHeaderAll() {
+ return headerAll;
+ }
+
+ /**
+ * Gets the RtfHeaderFooter for the title page
+ *
+ * @return The RtfHeaderFooter for the title page
+ */
+ protected RtfHeaderFooter getHeaderFirst() {
+ return headerFirst;
+ }
+
+ /**
+ * Gets the RtfHeaderFooter for all left hand pages
+ *
+ * @return The RtfHeaderFooter for all left hand pages
+ */
+ protected RtfHeaderFooter getHeaderLeft() {
+ return headerLeft;
+ }
+
+ /**
+ * Gets the RtfHeaderFooter for all right hand pages
+ *
+ * @return The RtfHeaderFooter for all right hand pages
+ */
+ protected RtfHeaderFooter getHeaderRight() {
+ return headerRight;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/list/RtfList.java b/src/main/java/com/lowagie/text/rtf/list/RtfList.java
new file mode 100644
index 0000000..cfa5c33
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/list/RtfList.java
@@ -0,0 +1,605 @@
+/*
+ * $Id: RtfList.java,v 1.19 2006/04/05 09:30:47 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004, 2005 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.list;
+
+import java.awt.Color;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import com.lowagie.text.Chunk;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.Font;
+import com.lowagie.text.List;
+import com.lowagie.text.ListItem;
+import com.lowagie.text.rtf.RtfBasicElement;
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.RtfExtendedElement;
+import com.lowagie.text.rtf.style.RtfFont;
+import com.lowagie.text.rtf.style.RtfFontList;
+import com.lowagie.text.rtf.style.RtfParagraphStyle;
+import com.lowagie.text.rtf.text.RtfParagraph;
+import com.lowagie.text.rtf.document.RtfDocument;
+
+
+/**
+ * The RtfList stores one List. It also provides the methods to write the
+ * list declaration and the list data.
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfList extends RtfElement implements RtfExtendedElement {
+
+ /**
+ * Constant for list level
+ */
+ private static final byte[] LIST_LEVEL = "\\listlevel".getBytes();
+ /**
+ * Constant for list level style old
+ */
+ private static final byte[] LIST_LEVEL_TYPE = "\\levelnfc".getBytes();
+ /**
+ * Constant for list level style new
+ */
+ private static final byte[] LIST_LEVEL_TYPE_NEW = "\\levelnfcn".getBytes();
+ /**
+ * Constant for list level alignment old
+ */
+ private static final byte[] LIST_LEVEL_ALIGNMENT = "\\leveljc".getBytes();
+ /**
+ * Constant for list level alignment new
+ */
+ private static final byte[] LIST_LEVEL_ALIGNMENT_NEW = "\\leveljcn".getBytes();
+ /**
+ * Constant for list level start at
+ */
+ private static final byte[] LIST_LEVEL_START_AT = "\\levelstartat".getBytes();
+ /**
+ * Constant for list level text
+ */
+ private static final byte[] LIST_LEVEL_TEXT = "\\leveltext".getBytes();
+ /**
+ * Constant for the beginning of the list level numbered style
+ */
+ private static final byte[] LIST_LEVEL_STYLE_NUMBERED_BEGIN = "\\\'02\\\'".getBytes();
+ /**
+ * Constant for the end of the list level numbered style
+ */
+ private static final byte[] LIST_LEVEL_STYLE_NUMBERED_END = ".;".getBytes();
+ /**
+ * Constant for the list level bulleted style
+ */
+ private static final byte[] LIST_LEVEL_STYLE_BULLETED = "\\\'01\\u-3913 ?;".getBytes();
+ /**
+ * Constant for the beginning of the list level numbers
+ */
+ private static final byte[] LIST_LEVEL_NUMBERS_BEGIN = "\\levelnumbers".getBytes();
+ /**
+ * Constant for the list level numbers
+ */
+ private static final byte[] LIST_LEVEL_NUMBERS_NUMBERED = "\\\'01".getBytes();
+ /**
+ * Constant for the end of the list level numbers
+ */
+ private static final byte[] LIST_LEVEL_NUMBERS_END = ";".getBytes();
+ /**
+ * Constant for the first indentation
+ */
+ private static final byte[] LIST_LEVEL_FIRST_INDENT = "\\fi".getBytes();
+ /**
+ * Constant for the symbol indentation
+ */
+ private static final byte[] LIST_LEVEL_SYMBOL_INDENT = "\\tx".getBytes();
+ /**
+ * Constant for the list level value
+ */
+ private static final byte[] LIST_LEVEL_NUMBER = "\\ilvl".getBytes();
+ /**
+ * Constant for a tab character
+ */
+ private static final byte[] TAB = "\\tab".getBytes();
+ /**
+ * Constant for the old list text
+ */
+ private static final byte[] LIST_TEXT = "\\listtext".getBytes();
+ /**
+ * Constant for the old list number end
+ */
+ private static final byte[] LIST_NUMBER_END = ".".getBytes();
+ /**
+ * Constant for the old bulleted list
+ */
+ private static final byte[] LIST_BULLET = "\\\'b7".getBytes();
+
+ /**
+ * The subitems of this RtfList
+ */
+ private ArrayList items;
+ /**
+ * The level of this RtfList
+ */
+ private int listLevel = 0;
+ /**
+ * The first indentation of this RtfList
+ */
+ private int firstIndent = 0;
+ /**
+ * The left indentation of this RtfList
+ */
+ private int leftIndent = 0;
+ /**
+ * The right indentation of this RtfList
+ */
+ private int rightIndent = 0;
+ /**
+ * The symbol indentation of this RtfList
+ */
+ private int symbolIndent = 0;
+ /**
+ * The list number of this RtfList
+ */
+ private int listNumber = 1;
+ /**
+ * Whether this RtfList is numbered
+ */
+ private boolean numbered = true;
+ /**
+ * The RtfFont for numbered lists
+ */
+ private RtfFont fontNumber;
+ /**
+ * The RtfFont for bulleted lists
+ */
+ private RtfFont fontBullet;
+ /**
+ * The alignment of this RtfList
+ */
+ private int alignment = Element.ALIGN_LEFT;
+ /**
+ * The parent List in multi-level lists.
+ */
+ private RtfList parentList = null;
+
+ /**
+ * Constructs a new RtfList for the specified List.
+ *
+ * @param doc The RtfDocument this RtfList belongs to
+ * @param list The List this RtfList is based on
+ */
+ public RtfList(RtfDocument doc, List list) {
+ super(doc);
+
+ this.listNumber = document.getDocumentHeader().getListNumber(this);
+
+ this.items = new ArrayList();
+ if(list.symbolIndent() > 0 && list.indentationLeft() > 0) {
+ this.firstIndent = (int) (list.symbolIndent() * RtfElement.TWIPS_FACTOR * -1);
+ this.leftIndent = (int) ((list.indentationLeft() + list.symbolIndent()) * RtfElement.TWIPS_FACTOR);
+ } else if(list.symbolIndent() > 0) {
+ this.firstIndent = (int) (list.symbolIndent() * RtfElement.TWIPS_FACTOR * -1);
+ this.leftIndent = (int) (list.symbolIndent() * RtfElement.TWIPS_FACTOR);
+ } else if(list.indentationLeft() > 0) {
+ this.firstIndent = 0;
+ this.leftIndent = (int) (list.indentationLeft() * RtfElement.TWIPS_FACTOR);
+ } else {
+ this.firstIndent = 0;
+ this.leftIndent = 0;
+ }
+ this.rightIndent = (int) (list.indentationRight() * RtfElement.TWIPS_FACTOR);
+ this.symbolIndent = (int) ((list.symbolIndent() + list.indentationLeft()) * RtfElement.TWIPS_FACTOR);
+ this.numbered = list.isNumbered();
+
+ for(int i = 0; i < list.getItems().size(); i++) {
+ try {
+ Element element = (Element) list.getItems().get(i);
+ if(element.type() == Element.CHUNK) {
+ element = new ListItem((Chunk) element);
+ }
+ if(element instanceof ListItem) {
+ this.alignment = ((ListItem) element).alignment();
+ }
+ RtfBasicElement rtfElement = doc.getMapper().mapElement(element);
+ if(rtfElement instanceof RtfList) {
+ ((RtfList) rtfElement).setListNumber(listNumber);
+ ((RtfList) rtfElement).setListLevel(listLevel + 1);
+ ((RtfList) rtfElement).setParent(this);
+ } else if(rtfElement instanceof RtfListItem) {
+ ((RtfListItem) rtfElement).setParent(this);
+ ((RtfListItem) rtfElement).inheritListSettings(listNumber, listLevel + 1);
+ }
+ items.add(rtfElement);
+ } catch(DocumentException de) {
+ de.printStackTrace();
+ }
+ }
+
+ if(this.listLevel == 0) {
+ correctIndentation();
+ }
+
+ fontNumber = new RtfFont(document, new Font(Font.TIMES_ROMAN, 10, Font.NORMAL, new Color(0, 0, 0)));
+ fontBullet = new RtfFont(document, new Font(Font.SYMBOL, 10, Font.NORMAL, new Color(0, 0, 0)));
+ }
+
+ private byte[] writeIndentations() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(LIST_LEVEL_FIRST_INDENT);
+ result.write(intToByteArray(firstIndent));
+ result.write(RtfParagraphStyle.INDENT_LEFT);
+ result.write(intToByteArray(leftIndent));
+ result.write(RtfParagraphStyle.INDENT_RIGHT);
+ result.write(intToByteArray(rightIndent));
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the definition part of this list level
+ *
+ * @return A byte array containing the definition of this list level
+ */
+ public byte[] writeDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(OPEN_GROUP);
+ result.write(LIST_LEVEL);
+ result.write(LIST_LEVEL_TYPE);
+ if(numbered) {
+ result.write(intToByteArray(0));
+ } else {
+ result.write(intToByteArray(23));
+ }
+ result.write(LIST_LEVEL_TYPE_NEW);
+ if(numbered) {
+ result.write(intToByteArray(0));
+ } else {
+ result.write(intToByteArray(23));
+ }
+ result.write(LIST_LEVEL_ALIGNMENT);
+ result.write(intToByteArray(0));
+ result.write(LIST_LEVEL_ALIGNMENT_NEW);
+ result.write(intToByteArray(0));
+ result.write(LIST_LEVEL_START_AT);
+ result.write(intToByteArray(1));
+ result.write(OPEN_GROUP);
+ result.write(LIST_LEVEL_TEXT);
+ if(numbered) {
+ result.write(LIST_LEVEL_STYLE_NUMBERED_BEGIN);
+ if(listLevel < 10) {
+ result.write(intToByteArray(0));
+ }
+ result.write(intToByteArray(listLevel));
+ result.write(LIST_LEVEL_STYLE_NUMBERED_END);
+ } else {
+ result.write(LIST_LEVEL_STYLE_BULLETED);
+ }
+ result.write(CLOSE_GROUP);
+ result.write(OPEN_GROUP);
+ result.write(LIST_LEVEL_NUMBERS_BEGIN);
+ if(numbered) {
+ result.write(LIST_LEVEL_NUMBERS_NUMBERED);
+ }
+ result.write(LIST_LEVEL_NUMBERS_END);
+ result.write(CLOSE_GROUP);
+ result.write(RtfFontList.FONT_NUMBER);
+ if(numbered) {
+ result.write(intToByteArray(fontNumber.getFontNumber()));
+ } else {
+ result.write(intToByteArray(fontBullet.getFontNumber()));
+ }
+ result.write(writeIndentations());
+ result.write(LIST_LEVEL_SYMBOL_INDENT);
+ result.write(intToByteArray(this.leftIndent));
+ result.write(CLOSE_GROUP);
+ result.write("\n".getBytes());
+ for(int i = 0; i < items.size(); i++) {
+ RtfElement rtfElement = (RtfElement) items.get(i);
+ if(rtfElement instanceof RtfList) {
+ result.write(((RtfList) rtfElement).writeDefinition());
+ break;
+ } else if(rtfElement instanceof RtfListItem) {
+ byte[] data = ((RtfListItem) rtfElement).writeDefinition();
+ if(data.length > 0) {
+ result.write(data);
+ break;
+ }
+ }
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the initialisation part of the RtfList
+ *
+ * @return A byte array containing the initialisation part
+ */
+ protected byte[] writeListBeginning() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(RtfParagraph.PARAGRAPH_DEFAULTS);
+ if(this.inTable) {
+ result.write(RtfParagraph.IN_TABLE);
+ }
+ switch (this.alignment) {
+ case Element.ALIGN_LEFT:
+ result.write(RtfParagraphStyle.ALIGN_LEFT);
+ break;
+ case Element.ALIGN_RIGHT:
+ result.write(RtfParagraphStyle.ALIGN_RIGHT);
+ break;
+ case Element.ALIGN_CENTER:
+ result.write(RtfParagraphStyle.ALIGN_CENTER);
+ break;
+ case Element.ALIGN_JUSTIFIED:
+ case Element.ALIGN_JUSTIFIED_ALL:
+ result.write(RtfParagraphStyle.ALIGN_JUSTIFY);
+ break;
+ }
+ result.write(writeIndentations());
+ result.write(RtfFont.FONT_SIZE);
+ result.write(intToByteArray(fontNumber.getFontSize() * 2));
+ if(this.symbolIndent > 0) { // TODO This is a slight hack. Replace with a call to tab support when implemented.
+ result.write("\\tx".getBytes());
+ result.write(intToByteArray(this.leftIndent));
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes only the list number and list level number.
+ *
+ * @return The list number and list level number of this RtfList.
+ */
+ protected byte[] writeListNumbers() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(RtfListTable.LIST_NUMBER);
+ result.write(intToByteArray(listNumber));
+ if(listLevel > 0) {
+ result.write(LIST_LEVEL_NUMBER);
+ result.write(intToByteArray(listLevel));
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the content of the RtfList
+ *
+ * @return A byte array containing the actual content of the RtfList
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(writeListBeginning());
+ result.write(writeListNumbers());
+ result.write(OPEN_GROUP);
+ int itemNr = 0;
+ for(int i = 0; i < items.size(); i++) {
+ RtfElement rtfElement = (RtfElement) items.get(i);
+ if(rtfElement instanceof RtfListItem) {
+ itemNr++;
+ result.write(OPEN_GROUP);
+ result.write(LIST_TEXT);
+ result.write(RtfParagraph.PARAGRAPH_DEFAULTS);
+ if(this.inTable) {
+ result.write(RtfParagraph.IN_TABLE);
+ }
+ result.write(RtfFontList.FONT_NUMBER);
+ if(numbered) {
+ result.write(intToByteArray(fontNumber.getFontNumber()));
+ } else {
+ result.write(intToByteArray(fontBullet.getFontNumber()));
+ }
+ result.write(writeIndentations());
+ result.write(DELIMITER);
+ if(numbered) {
+ result.write(this.intToByteArray(itemNr));
+ result.write(LIST_NUMBER_END);
+ } else {
+ result.write(LIST_BULLET);
+ }
+ result.write(TAB);
+ result.write(CLOSE_GROUP);
+ result.write(rtfElement.write());
+ result.write(RtfParagraph.PARAGRAPH);
+ if(((RtfListItem) rtfElement).isContainsInnerList()) {
+ result.write(writeListNumbers());
+ }
+ result.write("\n".getBytes());
+ } else if(rtfElement instanceof RtfList) {
+ result.write(rtfElement.write());
+ result.write(writeListBeginning());
+ result.write("\n".getBytes());
+ }
+ }
+ result.write(CLOSE_GROUP);
+ if(!this.inTable) {
+ result.write(RtfParagraph.PARAGRAPH_DEFAULTS);
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+
+ /**
+ * Gets the list level of this RtfList
+ *
+ * @return Returns the list level.
+ */
+ public int getListLevel() {
+ return listLevel;
+ }
+
+ /**
+ * Sets the list level of this RtfList. A list level > 0 will
+ * unregister this RtfList from the RtfListTable
+ *
+ * @param listLevel The list level to set.
+ */
+ public void setListLevel(int listLevel) {
+ this.listLevel = listLevel;
+ if(this.listLevel != 0) {
+ document.getDocumentHeader().freeListNumber(this);
+ for(int i = 0; i < this.items.size(); i++) {
+ if(this.items.get(i) instanceof RtfList) {
+ ((RtfList) this.items.get(i)).setListNumber(this.listNumber);
+ ((RtfList) this.items.get(i)).setListLevel(this.listLevel + 1);
+ }
+ }
+ } else {
+ this.listNumber = document.getDocumentHeader().getListNumber(this);
+ }
+ }
+
+ /**
+ * Sets the parent RtfList of this RtfList
+ *
+ * @param parent The parent RtfList to use.
+ */
+ protected void setParent(RtfList parent) {
+ this.parentList = parent;
+ }
+
+ /**
+ * Gets the id of this list
+ *
+ * @return Returns the list number.
+ */
+ public int getListNumber() {
+ return listNumber;
+ }
+
+ /**
+ * Sets the id of this list
+ *
+ * @param listNumber The list number to set.
+ */
+ public void setListNumber(int listNumber) {
+ this.listNumber = listNumber;
+ }
+
+ /**
+ * Sets whether this RtfList is in a table. Sets the correct inTable setting for all
+ * child elements.
+ *
+ * @param inTable True
if this RtfList is in a table, false
otherwise
+ */
+ public void setInTable(boolean inTable) {
+ super.setInTable(inTable);
+ for(int i = 0; i < this.items.size(); i++) {
+ ((RtfBasicElement) this.items.get(i)).setInTable(inTable);
+ }
+ }
+
+ /**
+ * Sets whether this RtfList is in a header. Sets the correct inTable setting for all
+ * child elements.
+ *
+ * @param inHeader True
if this RtfList is in a header, false
otherwise
+ */
+ public void setInHeader(boolean inHeader) {
+ super.setInHeader(inHeader);
+ for(int i = 0; i < this.items.size(); i++) {
+ ((RtfBasicElement) this.items.get(i)).setInHeader(inHeader);
+ }
+ }
+
+ /**
+ * Correct the indentation of this RtfList by adding left/first line indentation
+ * from the parent RtfList. Also calls correctIndentation on all child RtfLists.
+ */
+ protected void correctIndentation() {
+ if(this.parentList != null) {
+ this.leftIndent = this.leftIndent + this.parentList.getLeftIndent() + this.parentList.getFirstIndent();
+ }
+ for(int i = 0; i < this.items.size(); i++) {
+ if(this.items.get(i) instanceof RtfList) {
+ ((RtfList) this.items.get(i)).correctIndentation();
+ } else if(this.items.get(i) instanceof RtfListItem) {
+ ((RtfListItem) this.items.get(i)).correctIndentation();
+ }
+ }
+ }
+
+ /**
+ * Get the left indentation of this RtfList.
+ *
+ * @return The left indentation.
+ */
+ private int getLeftIndent() {
+ return this.leftIndent;
+ }
+
+ /**
+ * Get the first line indentation of this RtfList.
+ *
+ * @return The first line indentation.
+ */
+ private int getFirstIndent() {
+ return this.firstIndent;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/list/RtfListItem.java b/src/main/java/com/lowagie/text/rtf/list/RtfListItem.java
new file mode 100644
index 0000000..9e5b184
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/list/RtfListItem.java
@@ -0,0 +1,182 @@
+/*
+ * $Id: RtfListItem.java,v 1.11 2006/04/05 09:30:47 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004, 2005 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.list;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.ListItem;
+import com.lowagie.text.rtf.RtfBasicElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.text.RtfChunk;
+import com.lowagie.text.rtf.text.RtfParagraph;
+
+
+/**
+ * The RtfListItem acts as a wrapper for a ListItem.
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfListItem extends RtfParagraph {
+
+ /**
+ * The RtfList this RtfListItem belongs to.
+ */
+ private RtfList parentList = null;
+ /**
+ * Whether this RtfListItem contains further RtfLists.
+ */
+ private boolean containsInnerList = false;
+
+ /**
+ * Constructs a RtfListItem for a ListItem belonging to a RtfDocument.
+ *
+ * @param doc The RtfDocument this RtfListItem belongs to.
+ * @param listItem The ListItem this RtfListItem is based on.
+ */
+ public RtfListItem(RtfDocument doc, ListItem listItem) {
+ super(doc, listItem);
+ }
+
+ /**
+ * Writes the content of this RtfListItem.
+ *
+ * @return A byte array with the content of this RtfListItem.
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ for(int i = 0; i < chunks.size(); i++) {
+ RtfBasicElement rtfElement = (RtfBasicElement) chunks.get(i);
+ if(rtfElement instanceof RtfChunk) {
+ ((RtfChunk) rtfElement).setSoftLineBreaks(true);
+ } else if(rtfElement instanceof RtfList) {
+ result.write(RtfParagraph.PARAGRAPH);
+ this.containsInnerList = true;
+ }
+ result.write(rtfElement.write());
+ if(rtfElement instanceof RtfList) {
+ result.write(this.parentList.writeListBeginning());
+ result.write("\\tab".getBytes());
+ }
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Returns the definition of the first list contained in this RtfListItem or
+ * an empty byte array if no inner RtfLists exist.
+ *
+ * @return The definition of the first inner RtfList or an empty byte array.
+ */
+ public byte[] writeDefinition() {
+ for(int i = 0; i < chunks.size(); i++) {
+ RtfBasicElement rtfElement = (RtfBasicElement) chunks.get(i);
+ if(rtfElement instanceof RtfList) {
+ return ((RtfList) rtfElement).writeDefinition();
+ }
+ }
+ return new byte[0];
+ }
+
+ /**
+ * Inherit the list settings from the parent list to RtfLists that
+ * are contained in this RtfListItem.
+ *
+ * @param listNumber The list number to inherit.
+ * @param listLevel The list level to inherit.
+ */
+ public void inheritListSettings(int listNumber, int listLevel) {
+ for(int i = 0; i < chunks.size(); i++) {
+ RtfBasicElement rtfElement = (RtfBasicElement) chunks.get(i);
+ if(rtfElement instanceof RtfList) {
+ ((RtfList) rtfElement).setListNumber(listNumber);
+ ((RtfList) rtfElement).setListLevel(listLevel);
+ ((RtfList) rtfElement).setParent(this.parentList);
+ }
+ }
+ }
+
+ /**
+ * Correct the indentation of RtfLists in this RtfListItem by adding left/first line indentation
+ * from the parent RtfList. Also calls correctIndentation on all child RtfLists.
+ */
+ protected void correctIndentation() {
+ for(int i = 0; i < chunks.size(); i++) {
+ RtfBasicElement rtfElement = (RtfBasicElement) chunks.get(i);
+ if(rtfElement instanceof RtfList) {
+ ((RtfList) rtfElement).correctIndentation();
+ }
+ }
+ }
+
+ /**
+ * Set the parent RtfList.
+ *
+ * @param parentList The parent RtfList to use.
+ */
+ public void setParent(RtfList parentList) {
+ this.parentList = parentList;
+ }
+
+ /**
+ * Gets whether this RtfListItem contains further RtfLists.
+ *
+ * @return Whether this RtfListItem contains further RtfLists.
+ */
+ public boolean isContainsInnerList() {
+ return this.containsInnerList;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/list/RtfListTable.java b/src/main/java/com/lowagie/text/rtf/list/RtfListTable.java
new file mode 100644
index 0000000..ee10c94
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/list/RtfListTable.java
@@ -0,0 +1,202 @@
+/*
+ * $Id: RtfListTable.java,v 1.16 2005/05/04 14:33:45 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.list;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.RtfExtendedElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+
+
+/**
+ * The RtfListTable manages all RtfLists in one RtfDocument. It also generates
+ * the list and list override tables in the document header.
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfListTable extends RtfElement implements RtfExtendedElement {
+
+ /**
+ * Constant for the list number
+ */
+ protected static final byte[] LIST_NUMBER = "\\ls".getBytes();
+ /**
+ * Constant for the list table
+ */
+ private static final byte[] LIST_TABLE = "\\*\\listtable".getBytes();
+ /**
+ * Constant for the list
+ */
+ private static final byte[] LIST = "\\list".getBytes();
+ /**
+ * Constant for the list template id
+ */
+ private static final byte[] LIST_TEMPLATE_ID = "\\listtemplateid".getBytes();
+ /**
+ * Constant for the hybrid list
+ */
+ private static final byte[] LIST_HYBRID = "\\hybrid".getBytes();
+ /**
+ * Constant for the list id
+ */
+ private static final byte[] LIST_ID = "\\listid".getBytes();
+ /**
+ * Constant for the list override table
+ */
+ private static final byte[] LIST_OVERRIDE_TABLE = "\\*\\listoverridetable".getBytes();
+ /**
+ * Constant for the list override
+ */
+ private static final byte[] LIST_OVERRIDE = "\\listoverride".getBytes();
+ /**
+ * Constant for the list override count
+ */
+ private static final byte[] LIST_OVERRIDE_COUNT = "\\listoverridecount".getBytes();
+
+ /**
+ * The RtfLists managed by this RtfListTable
+ */
+ private ArrayList lists;
+
+ /**
+ * Constructs a RtfListTable for a RtfDocument
+ *
+ * @param doc The RtfDocument this RtfListTable belongs to
+ */
+ public RtfListTable(RtfDocument doc) {
+ super(doc);
+
+ this.lists = new ArrayList();
+ }
+
+ /**
+ * Writes the list and list override tables.
+ *
+ * @return A byte array with the list and list override tables.
+ */
+ public byte[] writeDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ int[] listIds = new int[lists.size()];
+ try {
+ result.write(OPEN_GROUP);
+ result.write(LIST_TABLE);
+ result.write("\n".getBytes());
+ for(int i = 0; i < lists.size(); i++) {
+ result.write(OPEN_GROUP);
+ result.write(LIST);
+ result.write(LIST_TEMPLATE_ID);
+ result.write(intToByteArray(document.getRandomInt()));
+ result.write(LIST_HYBRID);
+ result.write("\n".getBytes());
+ result.write(((RtfList) lists.get(i)).writeDefinition());
+ result.write(LIST_ID);
+ listIds[i] = document.getRandomInt();
+ result.write(intToByteArray(listIds[i]));
+ result.write(CLOSE_GROUP);
+ result.write("\n".getBytes());
+ }
+ result.write(CLOSE_GROUP);
+ result.write("\n".getBytes());
+ result.write(OPEN_GROUP);
+ result.write(LIST_OVERRIDE_TABLE);
+ result.write("\n".getBytes());
+ for(int i = 0; i < lists.size(); i++) {
+ result.write(OPEN_GROUP);
+ result.write(LIST_OVERRIDE);
+ result.write(LIST_ID);
+ result.write(intToByteArray(listIds[i]));
+ result.write(LIST_OVERRIDE_COUNT);
+ result.write(intToByteArray(0));
+ result.write(LIST_NUMBER);
+ result.write(intToByteArray(((RtfList) lists.get(i)).getListNumber()));
+ result.write(CLOSE_GROUP);
+ result.write("\n".getBytes());
+ }
+ result.write(CLOSE_GROUP);
+ result.write("\n".getBytes());
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Gets the id of the specified RtfList. If the RtfList is not yet in the
+ * list of RtfLists, then it is added.
+ *
+ * @param list The RtfList for which to get the id.
+ * @return The id of the RtfList.
+ */
+ public int getListNumber(RtfList list) {
+ if(lists.contains(list)) {
+ return lists.indexOf(list);
+ } else {
+ lists.add(list);
+ return lists.size();
+ }
+ }
+
+ /**
+ * Remove a RtfList from the list of RtfLists
+ *
+ * @param list The RtfList to remove.
+ */
+ public void freeListNumber(RtfList list) {
+ int i = lists.indexOf(list);
+ if(i >= 0) {
+ lists.remove(i);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/style/RtfColor.java b/src/main/java/com/lowagie/text/rtf/style/RtfColor.java
new file mode 100644
index 0000000..aca4ea2
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/style/RtfColor.java
@@ -0,0 +1,302 @@
+/*
+ * $Id: RtfColor.java,v 1.7 2004/12/14 12:51:59 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.style;
+
+import java.awt.Color;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.RtfExtendedElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+
+
+/**
+ * The RtfColor stores one rtf color value for a rtf document
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfColor extends RtfElement implements RtfExtendedElement {
+
+ /**
+ * Constant for RED value
+ */
+ private static final byte[] COLOR_RED = "\\red".getBytes();
+ /**
+ * Constant for GREEN value
+ */
+ private static final byte[] COLOR_GREEN = "\\green".getBytes();
+ /**
+ * Constant for BLUE value
+ */
+ private static final byte[] COLOR_BLUE = "\\blue".getBytes();
+ /**
+ * Constant for the end of one color entry
+ */
+ private static final byte COLON = (byte) ';';
+ /**
+ * Constant for the number of the colour in the list of colours
+ */
+ private static final byte[] COLOR_NUMBER = "\\cf".getBytes();
+
+ /**
+ * The number of the colour in the list of colours
+ */
+ private int colorNumber = 0;
+ /**
+ * The red value
+ */
+ private int red = 0;
+ /**
+ * The green value
+ */
+ private int green = 0;
+ /**
+ * The blue value
+ */
+ private int blue = 0;
+
+ /**
+ * Constructor only for use when initializing the RtfColorList
+ *
+ * @param doc The RtfDocument this RtfColor belongs to
+ * @param red The red value to use
+ * @param green The green value to use
+ * @param blue The blue value to use
+ * @param colorNumber The number of the colour in the colour list
+ */
+ protected RtfColor(RtfDocument doc, int red, int green, int blue, int colorNumber) {
+ super(doc);
+ this.red = red;
+ this.blue = blue;
+ this.green = green;
+ this.colorNumber = colorNumber;
+ }
+
+ /**
+ * Constructs a RtfColor as a clone of an existing RtfColor
+ *
+ * @param doc The RtfDocument this RtfColor belongs to
+ * @param col The RtfColor to use as a base
+ */
+ public RtfColor(RtfDocument doc, RtfColor col) {
+ super(doc);
+ if(col != null) {
+ this.red = col.getRed();
+ this.green = col.getGreen();
+ this.blue = col.getBlue();
+ }
+ if(this.document != null) {
+ this.colorNumber = this.document.getDocumentHeader().getColorNumber(this);
+ }
+ }
+
+ /**
+ * Constructs a RtfColor based on the Color
+ *
+ * @param doc The RtfDocument this RtfColor belongs to
+ * @param col The Color to base this RtfColor on
+ */
+ public RtfColor(RtfDocument doc, Color col) {
+ super(doc);
+ if(col != null) {
+ this.red = col.getRed();
+ this.blue = col.getBlue();
+ this.green = col.getGreen();
+ }
+ if(this.document != null) {
+ this.colorNumber = this.document.getDocumentHeader().getColorNumber(this);
+ }
+ }
+
+ /**
+ * Constructs a RtfColor based on the red/green/blue values
+ *
+ * @param doc The RtfDocument this RtfColor belongs to
+ * @param red The red value to use
+ * @param green The green value to use
+ * @param blue The blue value to use
+ */
+ public RtfColor(RtfDocument doc, int red, int green, int blue) {
+ super(doc);
+ this.red = red;
+ this.blue = blue;
+ this.green = green;
+ if(this.document != null) {
+ this.colorNumber = this.document.getDocumentHeader().getColorNumber(this);
+ }
+ }
+
+ /**
+ * Write the definition part of this RtfColor.
+ *
+ * @return A byte array with the definition of this colour
+ */
+ public byte[] writeDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(COLOR_RED);
+ result.write(intToByteArray(red));
+ result.write(COLOR_GREEN);
+ result.write(intToByteArray(green));
+ result.write(COLOR_BLUE);
+ result.write(intToByteArray(blue));
+ result.write(COLON);
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the beginning of this RtfColor
+ *
+ * @return A byte array with the colour start data
+ */
+ public byte[] writeBegin() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(COLOR_NUMBER);
+ result.write(intToByteArray(colorNumber));
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Unused
+ *
+ * @return An empty (byte[0]
) byte array
+ */
+ public byte[] writeEnd() {
+ return new byte[0];
+ }
+
+ /**
+ * Tests if this RtfColor is equal to another RtfColor.
+ *
+ * @param obj another RtfColor
+ * @return True
if red, green and blue values of the two colours match,
+ * false
otherwise.
+ */
+ public boolean equals(Object obj) {
+ if(!(obj instanceof RtfColor)) {
+ return false;
+ }
+ RtfColor color = (RtfColor) obj;
+ if(this.red == color.getRed() && this.green == color.getGreen() && this.blue == color.getBlue()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the hash code of this RtfColor. The hash code is
+ * an integer with the lowest three bytes containing the values
+ * of red, green and blue.
+ *
+ * @return The hash code of this RtfColor
+ */
+ public int hashCode() {
+ return (this.red << 16) | (this.green << 8) | this.blue;
+ }
+
+ /**
+ * Get the blue value of this RtfColor
+ *
+ * @return The blue value
+ */
+ public int getBlue() {
+ return blue;
+ }
+
+ /**
+ * Get the green value of this RtfColor
+ *
+ * @return The green value
+ */
+ public int getGreen() {
+ return green;
+ }
+
+ /**
+ * Get the red value of this RtfColor
+ *
+ * @return The red value
+ */
+ public int getRed() {
+ return red;
+ }
+
+ /**
+ * Gets the number of this RtfColor in the list of colours
+ *
+ * @return Returns the colorNumber.
+ */
+ public int getColorNumber() {
+ return colorNumber;
+ }
+
+ /**
+ * Sets the RtfDocument this RtfColor belongs to
+ *
+ * @param doc The RtfDocument to use
+ */
+ public void setRtfDocument(RtfDocument doc) {
+ super.setRtfDocument(doc);
+ if(document != null) {
+ this.colorNumber = document.getDocumentHeader().getColorNumber(this);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/style/RtfColorList.java b/src/main/java/com/lowagie/text/rtf/style/RtfColorList.java
new file mode 100644
index 0000000..ddb71d1
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/style/RtfColorList.java
@@ -0,0 +1,137 @@
+/*
+ * $Id: RtfColorList.java,v 1.16 2005/05/04 14:33:52 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.style;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.RtfExtendedElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+
+
+/**
+ * The RtfColorList stores all colours that appear in the document. Black
+ * and White are always added
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfColorList extends RtfElement implements RtfExtendedElement {
+
+ /**
+ * Constant for the beginning of the colour table
+ */
+ private static final byte[] COLOR_TABLE = "\\colortbl".getBytes();
+
+ /**
+ * ArrayList containing all colours of this RtfColorList
+ */
+ ArrayList colorList = new ArrayList();
+
+ /**
+ * Constructs a new RtfColorList for the RtfDocument. Will add the default
+ * black and white colours.
+ *
+ * @param doc The RtfDocument this RtfColorList belongs to
+ */
+ public RtfColorList(RtfDocument doc) {
+ super(doc);
+ colorList.add(new RtfColor(doc, 0, 0, 0, 0));
+ colorList.add(new RtfColor(doc, 255, 255, 255, 1));
+ }
+
+ /**
+ * Returns the index of the given RtfColor in the colour list. If the RtfColor
+ * is not in the list of colours, then it is added.
+ *
+ * @param color The RtfColor for which to get the index
+ * @return The index of the RtfColor
+ */
+ public int getColorNumber(RtfColor color) {
+ int colorIndex = -1;
+ for(int i = 0; i < colorList.size(); i++) {
+ if(colorList.get(i).equals(color)) {
+ colorIndex = i;
+ }
+ }
+ if(colorIndex == -1) {
+ colorIndex = colorList.size();
+ colorList.add(color);
+ }
+ return colorIndex;
+ }
+
+ /**
+ * Write the definition part of the colour list. Calls the writeDefinition
+ * methods of the RtfColors in the colour list.
+ *
+ * @return A byte array with the definition colour list
+ */
+ public byte[] writeDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(OPEN_GROUP);
+ result.write(COLOR_TABLE);
+ for(int i = 0; i < colorList.size(); i++) {
+ RtfColor color = (RtfColor) colorList.get(i);
+ result.write(color.writeDefinition());
+ }
+ result.write(CLOSE_GROUP);
+ result.write((byte)'\n');
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+}
diff --git a/src/main/java/com/lowagie/text/rtf/style/RtfFont.java b/src/main/java/com/lowagie/text/rtf/style/RtfFont.java
new file mode 100644
index 0000000..150b0b2
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/style/RtfFont.java
@@ -0,0 +1,681 @@
+/*
+ * $Id: RtfFont.java,v 1.23 2006/02/09 17:11:31 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.style;
+
+import com.lowagie.text.rtf.RtfExtendedElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.Font;
+
+import java.awt.Color;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * The RtfFont class stores one font for an rtf document. It extends Font,
+ * so can be set as a font, to allow adding of fonts with arbitrary names.
+ * BaseFont fontname handling contributed by Craig Fleming. Various fixes
+ * Renaud Michel, Werner Daehn.
+ *
+ * Version: $Id: RtfFont.java,v 1.23 2006/02/09 17:11:31 hallm Exp $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ * @author Craig Fleming (rythos@rhana.dhs.org)
+ * @author Renaud Michel (r.michel@immedia.be)
+ * @author Werner Daehn (Werner.Daehn@BusinessObjects.com)
+ */
+public class RtfFont extends Font implements RtfExtendedElement {
+ /**
+ * Constant for the font family to use ("froman")
+ */
+ private static final byte[] FONT_FAMILY = "\\froman".getBytes();
+ /**
+ * Constant for the charset
+ */
+ private static final byte[] FONT_CHARSET = "\\fcharset".getBytes();
+ /**
+ * Constant for the font size
+ */
+ public static final byte[] FONT_SIZE = "\\fs".getBytes();
+ /**
+ * Constant for the bold flag
+ */
+ private static final byte[] FONT_BOLD = "\\b".getBytes();
+ /**
+ * Constant for the italic flag
+ */
+ private static final byte[] FONT_ITALIC = "\\i".getBytes();
+ /**
+ * Constant for the underline flag
+ */
+ private static final byte[] FONT_UNDERLINE = "\\ul".getBytes();
+ /**
+ * Constant for the strikethrough flag
+ */
+ private static final byte[] FONT_STRIKETHROUGH = "\\strike".getBytes();
+ /**
+ * Constant for the double strikethrough flag
+ */
+ private static final byte[] FONT_DOUBLE_STRIKETHROUGH = "\\striked".getBytes();
+ /**
+ * Constant for the shadow flag
+ */
+ private static final byte[] FONT_SHADOW = "\\shad".getBytes();
+ /**
+ * Constant for the outline flag
+ */
+ private static final byte[] FONT_OUTLINE = "\\outl".getBytes();
+ /**
+ * Constant for the embossed flag
+ */
+ private static final byte[] FONT_EMBOSSED = "\\embo".getBytes();
+ /**
+ * Constant for the engraved flag
+ */
+ private static final byte[] FONT_ENGRAVED = "\\impr".getBytes();
+ /**
+ * Constant for hidden text flag
+ */
+ private static final byte[] FONT_HIDDEN = "\\v".getBytes();
+
+ /**
+ * Constant for a plain font
+ */
+ public static final int STYLE_NONE = 0;
+ /**
+ * Constant for a bold font
+ */
+ public static final int STYLE_BOLD = 1;
+ /**
+ * Constant for an italic font
+ */
+ public static final int STYLE_ITALIC = 2;
+ /**
+ * Constant for an underlined font
+ */
+ public static final int STYLE_UNDERLINE = 4;
+ /**
+ * Constant for a strikethrough font
+ */
+ public static final int STYLE_STRIKETHROUGH = 8;
+ /**
+ * Constant for a double strikethrough font
+ */
+ public static final int STYLE_DOUBLE_STRIKETHROUGH = 16;
+ /**
+ * Constant for a shadowed font
+ */
+ public static final int STYLE_SHADOW = 32;
+ /**
+ * Constant for an outlined font
+ */
+ public static final int STYLE_OUTLINE = 64;
+ /**
+ * Constant for an embossed font
+ */
+ public static final int STYLE_EMBOSSED = 128;
+ /**
+ * Constant for an engraved font
+ */
+ public static final int STYLE_ENGRAVED = 256;
+ /**
+ * Constant for a font that hides the actual text.
+ */
+ public static final int STYLE_HIDDEN = 512;
+
+ /**
+ * The font name. Defaults to "Times New Roman"
+ */
+ private String fontName = "Times New Roman";
+ /**
+ * The font size. Defaults to 10
+ */
+ private int fontSize = 10;
+ /**
+ * The font style. Defaults to STYLE_NONE
+ */
+ private int fontStyle = STYLE_NONE;
+ /**
+ * The number of this font
+ */
+ private int fontNumber = 0;
+ /**
+ * The colour of this font
+ */
+ private RtfColor color = null;
+ /**
+ * The character set to use for this font
+ */
+ private int charset = 0;
+ /**
+ * The RtfDocument this RtfFont belongs to.
+ */
+ protected RtfDocument document = null;
+
+ /**
+ * Constructs a RtfFont with the given font name and all other properties
+ * at their default values.
+ *
+ * @param fontName The font name to use
+ */
+ public RtfFont(String fontName) {
+ super(Font.UNDEFINED, Font.UNDEFINED, Font.UNDEFINED, null);
+ this.fontName = fontName;
+ }
+
+ /**
+ * Constructs a RtfFont with the given font name and font size and all other
+ * properties at their default values.
+ *
+ * @param fontName The font name to use
+ * @param size The font size to use
+ */
+ public RtfFont(String fontName, float size) {
+ super(Font.UNDEFINED, size, Font.UNDEFINED, null);
+ this.fontName = fontName;
+ }
+
+ /**
+ * Constructs a RtfFont with the given font name, font size and font style and the
+ * default color.
+ *
+ * @param fontName The font name to use
+ * @param size The font size to use
+ * @param style The font style to use
+ */
+ public RtfFont(String fontName, float size, int style) {
+ super(Font.UNDEFINED, size, style, null);
+ this.fontName = fontName;
+ }
+
+ /**
+ * Constructs a RtfFont with the given font name, font size, font style and
+ * color.
+ *
+ * @param fontName The font name to use
+ * @param size the font size to use
+ * @param style The font style to use
+ * @param color The font color to use
+ */
+ public RtfFont(String fontName, float size, int style, Color color) {
+ super(Font.UNDEFINED, size, style, color);
+ this.fontName = fontName;
+ }
+
+ /**
+ * Special constructor for the default font
+ *
+ * @param doc The RtfDocument this font appears in
+ * @param fontNumber The id of this font
+ */
+ protected RtfFont(RtfDocument doc, int fontNumber) {
+ this.document = doc;
+ this.fontNumber = fontNumber;
+ color = new RtfColor(doc, 0, 0, 0);
+ }
+
+ /**
+ * Constructs a RtfFont from a com.lowagie.text.Font
+ * @param doc The RtfDocument this font appears in
+ * @param font The Font to use as a base
+ */
+ public RtfFont(RtfDocument doc, Font font) {
+ this.document = doc;
+ if(font != null) {
+ if(font instanceof RtfFont) {
+ this.fontName = ((RtfFont) font).getFontName();
+ } else {
+ setToDefaultFamily(font.getFamilyname());
+ }
+ if(font.getBaseFont() != null) {
+ String[][] fontNames = font.getBaseFont().getFullFontName();
+ for(int i = 0; i < fontNames.length; i++) {
+ if(fontNames[i][2].equals("0")) {
+ this.fontName = fontNames[i][3];
+ break;
+ } else if(fontNames[i][2].equals("1033") || fontNames[i][2].equals("")) {
+ this.fontName = fontNames[i][3];
+ }
+ }
+ }
+
+ setSize(font.size());
+ setStyle(font.style());
+ setColor(font.color());
+ }
+
+ if(this.fontName.equalsIgnoreCase("unknown")) {
+ return;
+ }
+
+ if(document != null) {
+ setRtfDocument(document);
+ }
+ }
+
+ /**
+ * Writes the font definition
+ *
+ * @return A byte array with the font definition
+ */
+ public byte[] writeDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(FONT_FAMILY);
+ result.write(FONT_CHARSET);
+ result.write(intToByteArray(charset));
+ result.write(DELIMITER);
+ result.write(document.filterSpecialChar(fontName, true, false).getBytes());
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the font beginning
+ *
+ * @return A byte array with the font start data
+ */
+ public byte[] writeBegin() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ if(this.fontNumber != Font.UNDEFINED) {
+ result.write(RtfFontList.FONT_NUMBER);
+ result.write(intToByteArray(fontNumber));
+ }
+ if(this.fontSize != Font.UNDEFINED) {
+ result.write(FONT_SIZE);
+ result.write(intToByteArray(fontSize * 2));
+ }
+ if(this.fontStyle != UNDEFINED) {
+ if((fontStyle & STYLE_BOLD) == STYLE_BOLD) {
+ result.write(FONT_BOLD);
+ }
+ if((fontStyle & STYLE_ITALIC) == STYLE_ITALIC) {
+ result.write(FONT_ITALIC);
+ }
+ if((fontStyle & STYLE_UNDERLINE) == STYLE_UNDERLINE) {
+ result.write(FONT_UNDERLINE);
+ }
+ if((fontStyle & STYLE_STRIKETHROUGH) == STYLE_STRIKETHROUGH) {
+ result.write(FONT_STRIKETHROUGH);
+ }
+ if((fontStyle & STYLE_HIDDEN) == STYLE_HIDDEN) {
+ result.write(FONT_HIDDEN);
+ }
+ if((fontStyle & STYLE_DOUBLE_STRIKETHROUGH) == STYLE_DOUBLE_STRIKETHROUGH) {
+ result.write(FONT_DOUBLE_STRIKETHROUGH);
+ result.write(intToByteArray(1));
+ }
+ if((fontStyle & STYLE_SHADOW) == STYLE_SHADOW) {
+ result.write(FONT_SHADOW);
+ }
+ if((fontStyle & STYLE_OUTLINE) == STYLE_OUTLINE) {
+ result.write(FONT_OUTLINE);
+ }
+ if((fontStyle & STYLE_EMBOSSED) == STYLE_EMBOSSED) {
+ result.write(FONT_EMBOSSED);
+ }
+ if((fontStyle & STYLE_ENGRAVED) == STYLE_ENGRAVED) {
+ result.write(FONT_ENGRAVED);
+ }
+ }
+ if(color != null) {
+ result.write(color.writeBegin());
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Write the font end
+ *
+ * @return A byte array with the end of font data
+ */
+ public byte[] writeEnd() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ if(this.fontStyle != UNDEFINED) {
+ if((fontStyle & STYLE_BOLD) == STYLE_BOLD) {
+ result.write(FONT_BOLD);
+ result.write(intToByteArray(0));
+ }
+ if((fontStyle & STYLE_ITALIC) == STYLE_ITALIC) {
+ result.write(FONT_ITALIC);
+ result.write(intToByteArray(0));
+ }
+ if((fontStyle & STYLE_UNDERLINE) == STYLE_UNDERLINE) {
+ result.write(FONT_UNDERLINE);
+ result.write(intToByteArray(0));
+ }
+ if((fontStyle & STYLE_STRIKETHROUGH) == STYLE_STRIKETHROUGH) {
+ result.write(FONT_STRIKETHROUGH);
+ result.write(intToByteArray(0));
+ }
+ if((fontStyle & STYLE_HIDDEN) == STYLE_HIDDEN) {
+ result.write(FONT_HIDDEN);
+ result.write(intToByteArray(0));
+ }
+ if((fontStyle & STYLE_DOUBLE_STRIKETHROUGH) == STYLE_DOUBLE_STRIKETHROUGH) {
+ result.write(FONT_DOUBLE_STRIKETHROUGH);
+ result.write(intToByteArray(0));
+ }
+ if((fontStyle & STYLE_SHADOW) == STYLE_SHADOW) {
+ result.write(FONT_SHADOW);
+ result.write(intToByteArray(0));
+ }
+ if((fontStyle & STYLE_OUTLINE) == STYLE_OUTLINE) {
+ result.write(FONT_OUTLINE);
+ result.write(intToByteArray(0));
+ }
+ if((fontStyle & STYLE_EMBOSSED) == STYLE_EMBOSSED) {
+ result.write(FONT_EMBOSSED);
+ result.write(intToByteArray(0));
+ }
+ if((fontStyle & STYLE_ENGRAVED) == STYLE_ENGRAVED) {
+ result.write(FONT_ENGRAVED);
+ result.write(intToByteArray(0));
+ }
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Unused
+ * @return an empty byte array
+ */
+ public byte[] write() {
+ return new byte[0];
+ }
+
+ /**
+ * Tests for equality of RtfFonts. RtfFonts are equal if their fontName,
+ * fontSize, fontStyle and fontSuperSubscript are equal
+ *
+ * @param obj The RtfFont to compare with this RtfFont
+ * @return True
if the RtfFonts are equal, false
otherwise
+ */
+ public boolean equals(Object obj) {
+ if(!(obj instanceof RtfFont)) {
+ return false;
+ }
+ RtfFont font = (RtfFont) obj;
+ boolean result = true;
+ result = result & this.fontName.equals(font.getFontName());
+
+ return result;
+ }
+
+ /**
+ * Returns the hash code of this RtfFont. The hash code is the hash code of the
+ * string containing the font name + font size + "-" + the font style + "-" + the
+ * font super/supscript value.
+ *
+ * @return The hash code of this RtfFont
+ */
+ public int hashCode() {
+ return (this.fontName + this.fontSize + "-" + this.fontStyle).hashCode();
+ }
+
+ /**
+ * Gets the font name of this RtfFont
+ *
+ * @return The font name
+ */
+ public String getFontName() {
+ return this.fontName;
+ }
+
+ /**
+ * Sets the font name of this RtfFont.
+ *
+ * @param fontName The font name to use
+ */
+ protected void setFontName(String fontName) {
+ this.fontName = fontName;
+ if(document != null) {
+ this.fontNumber = document.getDocumentHeader().getFontNumber(this);
+ }
+ }
+
+ /**
+ * @see com.lowagie.text.Font#getFamilyname()
+ */
+ public String getFamilyname() {
+ return this.fontName;
+ }
+
+ /**
+ * @see com.lowagie.text.Font#setFamily(String)
+ */
+ public void setFamily(String family){
+ super.setFamily(family);
+ setToDefaultFamily(family);
+ }
+
+ /**
+ * Sets the correct font name from the family name.
+ *
+ * @param familyname The family name to set the name to.
+ */
+ private void setToDefaultFamily(String familyname){
+ switch (Font.getFamilyIndex(familyname)) {
+ case Font.COURIER:
+ this.fontName = "Courier";
+ break;
+ case Font.HELVETICA:
+ this.fontName = "Arial";
+ break;
+ case Font.SYMBOL:
+ this.fontName = "Symbol";
+ this.charset = 2;
+ break;
+ case Font.TIMES_ROMAN:
+ this.fontName = "Times New Roman";
+ break;
+ case Font.ZAPFDINGBATS:
+ this.fontName = "Windings";
+ break;
+ default:
+ this.fontName = familyname;
+ }
+ }
+
+ /**
+ * Gets the font size of this RtfFont
+ *
+ * @return The font size
+ */
+ public int getFontSize() {
+ return this.fontSize;
+ }
+
+ /**
+ * @see com.lowagie.text.Font#setSize(float)
+ */
+ public void setSize(float size){
+ super.setSize(size);
+ this.fontSize = (int) size();
+ }
+
+ /**
+ * Gets the font style of this RtfFont
+ *
+ * @return The font style
+ */
+ public int getFontStyle() {
+ return this.fontStyle;
+ }
+
+ /**
+ * @see com.lowagie.text.Font#setStyle(int)
+ */
+ public void setStyle(int style){
+ super.setStyle(style);
+ this.fontStyle = style();
+ }
+
+ /**
+ * @see com.lowagie.text.Font#setStyle(String)
+ */
+ public void setStyle(String style) {
+ super.setStyle(style);
+ fontStyle = style();
+ }
+
+ /**
+ * Gets the font number of this RtfFont
+ *
+ * @return The font number
+ */
+ public int getFontNumber() {
+ return fontNumber;
+ }
+
+ /**
+ * Sets the RtfDocument this RtfFont belongs to
+ *
+ * @param doc The RtfDocument to use
+ */
+ public void setRtfDocument(RtfDocument doc) {
+ this.document = doc;
+ if(document != null) {
+ this.fontNumber = document.getDocumentHeader().getFontNumber(this);
+ }
+ if(this.color != null) {
+ this.color.setRtfDocument(this.document);
+ }
+ }
+
+ /**
+ * Unused
+ * @param inTable
+ */
+ public void setInTable(boolean inTable) {
+ }
+
+ /**
+ * Unused
+ * @param inHeader
+ */
+ public void setInHeader(boolean inHeader) {
+ }
+
+ /**
+ * @see com.lowagie.text.Font#setColor(Color)
+ */
+ public void setColor(Color color) {
+ super.setColor(color);
+ if(color != null) {
+ this.color = new RtfColor(document, color);
+ } else {
+ this.color = null;
+ }
+ }
+
+ /**
+ * @see com.lowagie.text.Font#setColor(int, int, int)
+ */
+ public void setColor(int red, int green, int blue) {
+ super.setColor(red,green,blue);
+ this.color = new RtfColor(document, red, green, blue);
+ }
+
+ /**
+ * Transforms an integer into its String representation and then returns the bytes
+ * of that string.
+ *
+ * @param i The integer to convert
+ * @return A byte array representing the integer
+ */
+ protected byte[] intToByteArray(int i) {
+ return Integer.toString(i).getBytes();
+ }
+
+ /**
+ * Replaces the attributes that are equal to null with
+ * the attributes of a given font.
+ *
+ * @param font The surrounding font
+ * @return A RtfFont
+ */
+ public Font difference(Font font) {
+ String dFamilyname = font.getFamilyname();
+ if(dFamilyname == null || dFamilyname.trim().equals("") || dFamilyname.trim().equalsIgnoreCase("unknown")) {
+ dFamilyname = this.fontName;
+ }
+
+ float dSize = font.size();
+ if(dSize == Font.UNDEFINED) {
+ dSize = this.size();
+ }
+
+ int dStyle = Font.UNDEFINED;
+ if(this.style() != Font.UNDEFINED && font.style() != Font.UNDEFINED) {
+ dStyle = this.style() | font.style();
+ } else if(this.style() != Font.UNDEFINED) {
+ dStyle = this.style();
+ } else if(font.style() != Font.UNDEFINED) {
+ dStyle = font.style();
+ }
+
+ Color dColor = font.color();
+ if(dColor == null) {
+ dColor = this.color();
+ }
+
+ return new RtfFont(dFamilyname, dSize, dStyle, dColor);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/style/RtfFontList.java b/src/main/java/com/lowagie/text/rtf/style/RtfFontList.java
new file mode 100644
index 0000000..6e90371
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/style/RtfFontList.java
@@ -0,0 +1,149 @@
+/*
+ * $Id: RtfFontList.java,v 1.17 2005/12/24 13:10:37 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.style;
+
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.RtfExtendedElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+
+import java.util.ArrayList;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * The RtfFontList stores the list of fonts used in the rtf document. It also
+ * has methods for writing this list to the document
+ *
+ * Version: $Id: RtfFontList.java,v 1.17 2005/12/24 13:10:37 hallm Exp $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfFontList extends RtfElement implements RtfExtendedElement {
+
+ /**
+ * Constant for the default font
+ */
+ private static final byte[] DEFAULT_FONT = "\\deff".getBytes();
+ /**
+ * Constant for the font table
+ */
+ private static final byte[] FONT_TABLE = "\\fonttbl".getBytes();
+ /**
+ * Constant for the font number
+ */
+ public static final byte[] FONT_NUMBER = "\\f".getBytes();
+
+ /**
+ * The list of fonts
+ */
+ private ArrayList fontList = new ArrayList();
+
+ /**
+ * Creates a RtfFontList
+ *
+ * @param doc The RtfDocument this RtfFontList belongs to
+ */
+ public RtfFontList(RtfDocument doc) {
+ super(doc);
+ fontList.add(new RtfFont(document, 0));
+ }
+
+ /**
+ * Gets the index of the font in the list of fonts. If the font does not
+ * exist in the list, it is added.
+ *
+ * @param font The font to get the id for
+ * @return The index of the font
+ */
+ public int getFontNumber(RtfFont font) {
+ if(font instanceof RtfParagraphStyle) {
+ font = new RtfFont(this.document, (RtfParagraphStyle) font);
+ }
+ int fontIndex = -1;
+ for(int i = 0; i < fontList.size(); i++) {
+ if(fontList.get(i).equals(font)) {
+ fontIndex = i;
+ }
+ }
+ if(fontIndex == -1) {
+ fontIndex = fontList.size();
+ fontList.add(font);
+ }
+ return fontIndex;
+ }
+
+ /**
+ * Writes the definition of the font list
+ *
+ * @return A byte array with the definition of the font list
+ */
+ public byte[] writeDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(DEFAULT_FONT);
+ result.write(intToByteArray(0));
+ result.write(OPEN_GROUP);
+ result.write(FONT_TABLE);
+ for(int i = 0; i < fontList.size(); i++) {
+ result.write(OPEN_GROUP);
+ result.write(FONT_NUMBER);
+ result.write(intToByteArray(i));
+ result.write(((RtfFont) fontList.get(i)).writeDefinition());
+ result.write(COMMA_DELIMITER);
+ result.write(CLOSE_GROUP);
+ }
+ result.write(CLOSE_GROUP);
+ result.write((byte)'\n');
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/style/RtfParagraphStyle.java b/src/main/java/com/lowagie/text/rtf/style/RtfParagraphStyle.java
new file mode 100644
index 0000000..d8c582c
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/style/RtfParagraphStyle.java
@@ -0,0 +1,665 @@
+package com.lowagie.text.rtf.style;
+
+import java.awt.Color;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.Element;
+import com.lowagie.text.Font;
+import com.lowagie.text.rtf.RtfBasicElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.text.RtfParagraph;
+
+/**
+ * The RtfParagraphStyle stores all style/formatting attributes of a RtfParagraph.
+ * Additionally it also supports the style name system available in RTF. The RtfParagraphStyle
+ * is a Font and can thus be used as such. To use the stylesheet functionality
+ * it needs to be set as the font of a Paragraph. Otherwise it will work like a
+ * RtfFont. It also supports inheritance of styles.
+ *
+ * @version $Revision: 1.4 $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfParagraphStyle extends RtfFont {
+
+ /**
+ * Constant for left alignment
+ */
+ public static final byte[] ALIGN_LEFT = "\\ql".getBytes();
+ /**
+ * Constant for right alignment
+ */
+ public static final byte[] ALIGN_RIGHT = "\\qr".getBytes();
+ /**
+ * Constant for center alignment
+ */
+ public static final byte[] ALIGN_CENTER = "\\qc".getBytes();
+ /**
+ * Constant for justified alignment
+ */
+ public static final byte[] ALIGN_JUSTIFY = "\\qj".getBytes();
+ /**
+ * Constant for left indentation
+ */
+ public static final byte[] INDENT_LEFT = "\\li".getBytes();
+ /**
+ * Constant for right indentation
+ */
+ public static final byte[] INDENT_RIGHT = "\\ri".getBytes();
+ /**
+ * Constant for keeping the paragraph together on one page
+ */
+ public static final byte[] KEEP_TOGETHER = "\\keep".getBytes();
+ /**
+ * Constant for keeping the paragraph toghether with the next one on one page
+ */
+ public static final byte[] KEEP_TOGETHER_WITH_NEXT = "\\keepn".getBytes();
+ /**
+ * Constant for the space after the paragraph.
+ */
+ public static final byte[] SPACING_AFTER = "\\sa".getBytes();
+ /**
+ * Constant for the space before the paragraph.
+ */
+ public static final byte[] SPACING_BEFORE = "\\sb".getBytes();
+
+ /**
+ * The NORMAL/STANDARD style.
+ */
+ public static final RtfParagraphStyle STYLE_NORMAL = new RtfParagraphStyle("Normal", "Arial", 12, Font.NORMAL, Color.black);
+ /**
+ * The style for level 1 headings.
+ */
+ public static final RtfParagraphStyle STYLE_HEADING_1 = new RtfParagraphStyle("heading 1", "Normal");
+ /**
+ * The style for level 2 headings.
+ */
+ public static final RtfParagraphStyle STYLE_HEADING_2 = new RtfParagraphStyle("heading 2", "Normal");
+ /**
+ * The style for level 3 headings.
+ */
+ public static final RtfParagraphStyle STYLE_HEADING_3 = new RtfParagraphStyle("heading 3", "Normal");
+
+ /**
+ * Initialises the properties of the styles.
+ */
+ static {
+ STYLE_HEADING_1.setSize(16);
+ STYLE_HEADING_1.setStyle(Font.BOLD);
+ STYLE_HEADING_2.setSize(14);
+ STYLE_HEADING_2.setStyle(Font.BOLDITALIC);
+ STYLE_HEADING_3.setSize(13);
+ STYLE_HEADING_3.setStyle(Font.BOLD);
+ }
+
+ /**
+ * No modification has taken place when compared to the RtfParagraphStyle this RtfParagraphStyle
+ * is based on. These modification markers are used to determine what needs to be
+ * inherited and what not from the parent RtfParagraphStyle.
+ */
+ private static final int MODIFIED_NONE = 0;
+ /**
+ * The alignment has been modified.
+ */
+ private static final int MODIFIED_ALIGNMENT = 1;
+ /**
+ * The left indentation has been modified.
+ */
+ private static final int MODIFIED_INDENT_LEFT = 2;
+ /**
+ * The right indentation has been modified.
+ */
+ private static final int MODIFIED_INDENT_RIGHT = 4;
+ /**
+ * The spacing before a paragraph has been modified.
+ */
+ private static final int MODIFIED_SPACING_BEFORE = 8;
+ /**
+ * The spacing after a paragraph has been modified.
+ */
+ private static final int MODIFIED_SPACING_AFTER = 16;
+ /**
+ * The font name has been modified.
+ */
+ private static final int MODIFIED_FONT_NAME = 32;
+ /**
+ * The font style has been modified.
+ */
+ private static final int MODIFIED_FONT_SIZE = 64;
+ /**
+ * The font size has been modified.
+ */
+ private static final int MODIFIED_FONT_STYLE = 128;
+ /**
+ * The font colour has been modified.
+ */
+ private static final int MODIFIED_FONT_COLOR = 256;
+ /**
+ * The line leading has been modified.
+ */
+ private static final int MODIFIED_LINE_LEADING = 512;
+ /**
+ * The paragraph keep together setting has been modified.
+ */
+ private static final int MODIFIED_KEEP_TOGETHER = 1024;
+ /**
+ * The paragraph keep together with next setting has been modified.
+ */
+ private static final int MODIFIED_KEEP_TOGETHER_WITH_NEXT = 2048;
+
+ /**
+ * The alignment of the paragraph.
+ */
+ private int alignment = Element.ALIGN_LEFT;
+ /**
+ * The left indentation of the paragraph.
+ */
+ private int indentLeft = 0;
+ /**
+ * The right indentation of the paragraph.
+ */
+ private int indentRight = 0;
+ /**
+ * The spacing before a paragraph.
+ */
+ private int spacingBefore = 0;
+ /**
+ * The spacing after a paragraph.
+ */
+ private int spacingAfter = 0;
+ /**
+ * The line leading of the paragraph.
+ */
+ private int lineLeading = 0;
+ /**
+ * Whether this RtfParagraph must stay on one page.
+ */
+ private boolean keepTogether = false;
+ /**
+ * Whether this RtfParagraph must stay on the same page as the next paragraph.
+ */
+ private boolean keepTogetherWithNext = false;
+ /**
+ * The name of this RtfParagraphStyle.
+ */
+ private String styleName = "";
+ /**
+ * The name of the RtfParagraphStyle this RtfParagraphStyle is based on.
+ */
+ private String basedOnName = null;
+ /**
+ * The RtfParagraphStyle this RtfParagraphStyle is based on.
+ */
+ private RtfParagraphStyle baseStyle = null;
+ /**
+ * Which properties have been modified when compared to the base style.
+ */
+ private int modified = MODIFIED_NONE;
+ /**
+ * The number of this RtfParagraphStyle in the stylesheet list.
+ */
+ private int styleNumber = -1;
+
+ /**
+ * Constructs a new RtfParagraphStyle with the given attributes.
+ *
+ * @param styleName The name of this RtfParagraphStyle.
+ * @param fontName The name of the font to use for this RtfParagraphStyle.
+ * @param fontSize The size of the font to use for this RtfParagraphStyle.
+ * @param fontStyle The style of the font to use for this RtfParagraphStyle.
+ * @param fontColor The colour of the font to use for this RtfParagraphStyle.
+ */
+ public RtfParagraphStyle(String styleName, String fontName, int fontSize, int fontStyle, Color fontColor) {
+ super(null, new RtfFont(fontName, fontSize, fontStyle, fontColor));
+ this.styleName = styleName;
+ }
+
+ /**
+ * Constructs a new RtfParagraphStyle that is based on an existing RtfParagraphStyle.
+ *
+ * @param styleName The name of this RtfParagraphStyle.
+ * @param basedOnName The name of the RtfParagraphStyle this RtfParagraphStyle is based on.
+ */
+ public RtfParagraphStyle(String styleName, String basedOnName) {
+ super(null, new Font());
+ this.styleName = styleName;
+ this.basedOnName = basedOnName;
+ }
+
+ /**
+ * Constructs a RtfParagraphStyle from another RtfParagraphStyle.
+ *
+ * INTERNAL USE ONLY
+ *
+ * @param doc The RtfDocument this RtfParagraphStyle belongs to.
+ * @param style The RtfParagraphStyle to copy settings from.
+ */
+ public RtfParagraphStyle(RtfDocument doc, RtfParagraphStyle style) {
+ super(doc, style);
+ this.document = doc;
+ this.styleName = style.getStyleName();
+ this.alignment = style.getAlignment();
+ this.indentLeft = (int) (style.getIndentLeft() * RtfBasicElement.TWIPS_FACTOR);
+ this.indentRight = (int) (style.getIndentRight() * RtfBasicElement.TWIPS_FACTOR);
+ this.spacingBefore = (int) (style.getSpacingBefore() * RtfBasicElement.TWIPS_FACTOR);
+ this.spacingAfter = (int) (style.getSpacingAfter() * RtfBasicElement.TWIPS_FACTOR);
+ this.lineLeading = (int) (style.getLineLeading() * RtfBasicElement.TWIPS_FACTOR);
+ this.keepTogether = style.getKeepTogether();
+ this.keepTogetherWithNext = style.getKeepTogetherWithNext();
+ this.basedOnName = style.basedOnName;
+ this.modified = style.modified;
+ this.styleNumber = style.getStyleNumber();
+
+ if(this.document != null) {
+ setRtfDocument(this.document);
+ }
+ }
+
+ /**
+ * Gets the name of this RtfParagraphStyle.
+ *
+ * @return The name of this RtfParagraphStyle.
+ */
+ public String getStyleName() {
+ return this.styleName;
+ }
+
+ /**
+ * Gets the name of the RtfParagraphStyle this RtfParagraphStyle is based on.
+ *
+ * @return The name of the base RtfParagraphStyle.
+ */
+ public String getBasedOnName() {
+ return this.basedOnName;
+ }
+
+ /**
+ * Gets the alignment of this RtfParagraphStyle.
+ *
+ * @return The alignment of this RtfParagraphStyle.
+ */
+ public int getAlignment() {
+ return this.alignment;
+ }
+
+ /**
+ * Sets the alignment of this RtfParagraphStyle.
+ *
+ * @param alignment The alignment to use.
+ */
+ public void setAlignment(int alignment) {
+ this.modified = this.modified | MODIFIED_ALIGNMENT;
+ this.alignment = alignment;
+ }
+
+ /**
+ * Gets the left indentation of this RtfParagraphStyle.
+ *
+ * @return The left indentation of this RtfParagraphStyle.
+ */
+ public int getIndentLeft() {
+ return this.indentLeft;
+ }
+
+ /**
+ * Sets the left indentation of this RtfParagraphStyle.
+ *
+ * @param indentLeft The left indentation to use.
+ */
+ public void setIndentLeft(int indentLeft) {
+ this.modified = this.modified | MODIFIED_INDENT_LEFT;
+ this.indentLeft = indentLeft;
+ }
+
+ /**
+ * Gets the right indentation of this RtfParagraphStyle.
+ *
+ * @return The right indentation of this RtfParagraphStyle.
+ */
+ public int getIndentRight() {
+ return this.indentRight;
+ }
+
+ /**
+ * Sets the right indentation of this RtfParagraphStyle.
+ *
+ * @param indentRight The right indentation to use.
+ */
+ public void setIndentRight(int indentRight) {
+ this.modified = this.modified | MODIFIED_INDENT_RIGHT;
+ this.indentRight = indentRight;
+ }
+
+ /**
+ * Gets the space before the paragraph of this RtfParagraphStyle..
+ *
+ * @return The space before the paragraph.
+ */
+ public int getSpacingBefore() {
+ return this.spacingBefore;
+ }
+
+ /**
+ * Sets the space before the paragraph of this RtfParagraphStyle.
+ *
+ * @param spacingBefore The space before to use.
+ */
+ public void setSpacingBefore(int spacingBefore) {
+ this.modified = this.modified | MODIFIED_SPACING_BEFORE;
+ this.spacingBefore = spacingBefore;
+ }
+
+ /**
+ * Gets the space after the paragraph of this RtfParagraphStyle.
+ *
+ * @return The space after the paragraph.
+ */
+ public int getSpacingAfter() {
+ return this.spacingAfter;
+ }
+
+ /**
+ * Sets the space after the paragraph of this RtfParagraphStyle.
+ *
+ * @param spacingAfter The space after to use.
+ */
+ public void setSpacingAfter(int spacingAfter) {
+ this.modified = this.modified | MODIFIED_SPACING_AFTER;
+ this.spacingAfter = spacingAfter;
+ }
+
+ /**
+ * Sets the font name of this RtfParagraphStyle.
+ *
+ * @param fontName The font name to use
+ */
+ public void setFontName(String fontName) {
+ this.modified = this.modified | MODIFIED_FONT_NAME;
+ super.setFontName(fontName);
+ }
+
+ /**
+ * Sets the font size of this RtfParagraphStyle.
+ *
+ * @param fontSize The font size to use.
+ */
+ public void setSize(float fontSize) {
+ this.modified = this.modified | MODIFIED_FONT_SIZE;
+ super.setSize(fontSize);
+ }
+
+ /**
+ * Sets the font style of this RtfParagraphStyle.
+ *
+ * @param fontStyle The font style to use.
+ */
+ public void setStyle(int fontStyle) {
+ this.modified = this.modified | MODIFIED_FONT_STYLE;
+ super.setStyle(fontStyle);
+ }
+
+ /**
+ * Sets the colour of this RtfParagraphStyle.
+ *
+ * @param color The Color to use.
+ */
+ public void setColor(Color color) {
+ this.modified = this.modified | MODIFIED_FONT_COLOR;
+ super.setColor(color);
+ }
+
+ /**
+ * Gets the line leading of this RtfParagraphStyle.
+ *
+ * @return The line leading of this RtfParagraphStyle.
+ */
+ public int getLineLeading() {
+ return this.lineLeading;
+ }
+
+ /**
+ * Sets the line leading of this RtfParagraphStyle.
+ *
+ * @param lineLeading The line leading to use.
+ */
+ public void setLineLeading(int lineLeading) {
+ this.lineLeading = lineLeading;
+ this.modified = this.modified | MODIFIED_LINE_LEADING;
+ }
+
+ /**
+ * Gets whether the lines in the paragraph should be kept together in
+ * this RtfParagraphStyle.
+ *
+ * @return Whether the lines in the paragraph should be kept together.
+ */
+ public boolean getKeepTogether() {
+ return this.keepTogether;
+ }
+
+ /**
+ * Sets whether the lines in the paragraph should be kept together in
+ * this RtfParagraphStyle.
+ *
+ * @param keepTogether Whether the lines in the paragraph should be kept together.
+ */
+ public void setKeepTogether(boolean keepTogether) {
+ this.keepTogether = keepTogether;
+ this.modified = this.modified | MODIFIED_KEEP_TOGETHER;
+ }
+
+ /**
+ * Gets whether the paragraph should be kept toggether with the next in
+ * this RtfParagraphStyle.
+ *
+ * @return Whether the paragraph should be kept together with the next.
+ */
+ public boolean getKeepTogetherWithNext() {
+ return this.keepTogetherWithNext;
+ }
+
+ /**
+ * Sets whether the paragraph should be kept together with the next in
+ * this RtfParagraphStyle.
+ *
+ * @param keepTogetherWithNext Whether the paragraph should be kept together with the next.
+ */
+ public void setKeepTogetherWithNext(boolean keepTogetherWithNext) {
+ this.keepTogetherWithNext = keepTogetherWithNext;
+ this.modified = this.modified | MODIFIED_KEEP_TOGETHER_WITH_NEXT;
+ }
+
+ /**
+ * Handles the inheritance of paragraph style settings. All settings that
+ * have not been modified will be inherited from the base RtfParagraphStyle.
+ * If this RtfParagraphStyle is not based on another one, then nothing happens.
+ */
+ public void handleInheritance() {
+ if(this.basedOnName != null && this.document.getDocumentHeader().getRtfParagraphStyle(this.basedOnName) != null) {
+ this.baseStyle = this.document.getDocumentHeader().getRtfParagraphStyle(this.basedOnName);
+ this.baseStyle.handleInheritance();
+ if(!((this.modified & MODIFIED_ALIGNMENT) == MODIFIED_ALIGNMENT)) {
+ this.alignment = this.baseStyle.getAlignment();
+ }
+ if(!((this.modified & MODIFIED_INDENT_LEFT) == MODIFIED_INDENT_LEFT)) {
+ this.indentLeft = this.baseStyle.getIndentLeft();
+ }
+ if(!((this.modified & MODIFIED_INDENT_RIGHT) == MODIFIED_INDENT_RIGHT)) {
+ this.indentRight = this.baseStyle.getIndentRight();
+ }
+ if(!((this.modified & MODIFIED_SPACING_BEFORE) == MODIFIED_SPACING_BEFORE)) {
+ this.spacingBefore = this.baseStyle.getSpacingBefore();
+ }
+ if(!((this.modified & MODIFIED_SPACING_AFTER) == MODIFIED_SPACING_AFTER)) {
+ this.spacingAfter = this.baseStyle.getSpacingAfter();
+ }
+ if(!((this.modified & MODIFIED_FONT_NAME) == MODIFIED_FONT_NAME)) {
+ setFontName(this.baseStyle.getFontName());
+ }
+ if(!((this.modified & MODIFIED_FONT_SIZE) == MODIFIED_FONT_SIZE)) {
+ setSize(this.baseStyle.getFontSize());
+ }
+ if(!((this.modified & MODIFIED_FONT_STYLE) == MODIFIED_FONT_STYLE)) {
+ setStyle(this.baseStyle.getFontStyle());
+ }
+ if(!((this.modified & MODIFIED_FONT_COLOR) == MODIFIED_FONT_COLOR)) {
+ setColor(this.baseStyle.color());
+ }
+ if(!((this.modified & MODIFIED_LINE_LEADING) == MODIFIED_LINE_LEADING)) {
+ setLineLeading(this.baseStyle.getLineLeading());
+ }
+ if(!((this.modified & MODIFIED_KEEP_TOGETHER) == MODIFIED_KEEP_TOGETHER)) {
+ setKeepTogether(this.baseStyle.getKeepTogether());
+ }
+ if(!((this.modified & MODIFIED_KEEP_TOGETHER_WITH_NEXT) == MODIFIED_KEEP_TOGETHER_WITH_NEXT)) {
+ setKeepTogetherWithNext(this.baseStyle.getKeepTogetherWithNext());
+ }
+ }
+ }
+
+ /**
+ * Writes the settings of this RtfParagraphStyle.
+ *
+ * @return A byte array with the settings of this RtfParagraphStyle.
+ */
+ private byte[] writeParagraphSettings() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ if(this.keepTogether) {
+ result.write(RtfParagraphStyle.KEEP_TOGETHER);
+ }
+ if(this.keepTogetherWithNext) {
+ result.write(RtfParagraphStyle.KEEP_TOGETHER_WITH_NEXT);
+ }
+ switch (alignment) {
+ case Element.ALIGN_LEFT:
+ result.write(RtfParagraphStyle.ALIGN_LEFT);
+ break;
+ case Element.ALIGN_RIGHT:
+ result.write(RtfParagraphStyle.ALIGN_RIGHT);
+ break;
+ case Element.ALIGN_CENTER:
+ result.write(RtfParagraphStyle.ALIGN_CENTER);
+ break;
+ case Element.ALIGN_JUSTIFIED:
+ case Element.ALIGN_JUSTIFIED_ALL:
+ result.write(RtfParagraphStyle.ALIGN_JUSTIFY);
+ break;
+ }
+ result.write(RtfParagraphStyle.INDENT_LEFT);
+ result.write(intToByteArray(indentLeft));
+ result.write(RtfParagraphStyle.INDENT_RIGHT);
+ result.write(intToByteArray(indentRight));
+ if(this.spacingBefore > 0) {
+ result.write(RtfParagraphStyle.SPACING_BEFORE);
+ result.write(intToByteArray(this.spacingBefore));
+ }
+ if(this.spacingAfter > 0) {
+ result.write(RtfParagraphStyle.SPACING_AFTER);
+ result.write(intToByteArray(this.spacingAfter));
+ }
+ if(this.lineLeading > 0) {
+ result.write(RtfParagraph.LINE_SPACING);
+ result.write(intToByteArray(this.lineLeading));
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the definition of this RtfParagraphStyle for the stylesheet list.
+ */
+ public byte[] writeDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write("{".getBytes());
+ result.write("\\style".getBytes());
+ result.write("\\s".getBytes());
+ result.write(intToByteArray(this.styleNumber));
+ result.write(RtfBasicElement.DELIMITER);
+ result.write(writeParagraphSettings());
+ result.write(super.writeBegin());
+ result.write(RtfBasicElement.DELIMITER);
+ result.write(this.styleName.getBytes());
+ result.write(";".getBytes());
+ result.write("}".getBytes());
+ if(this.document.getDocumentSettings().isOutputDebugLineBreaks()) {
+ result.write('\n');
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the start information of this RtfParagraphStyle.
+ */
+ public byte[] writeBegin() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write("\\s".getBytes());
+ result.write(intToByteArray(this.styleNumber));
+ result.write(writeParagraphSettings());
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Unused
+ * @return An empty byte array.
+ */
+ public byte[] writeEnd() {
+ return new byte[0];
+ }
+
+ /**
+ * Unused
+ * @return An empty byte array.
+ */
+ public byte[] write() {
+ return new byte[0];
+ }
+
+ /**
+ * Tests whether two RtfParagraphStyles are equal. Equality
+ * is determined via the name.
+ */
+ public boolean equals(Object o) {
+ if(o == null || !(o instanceof RtfParagraphStyle)) {
+ return false;
+ }
+ RtfParagraphStyle paragraphStyle = (RtfParagraphStyle) o;
+ boolean result = this.getStyleName().equals(paragraphStyle.getStyleName());
+ return result;
+ }
+
+ /**
+ * Gets the hash code of this RtfParagraphStyle.
+ */
+ public int hashCode() {
+ return this.styleName.hashCode();
+ }
+
+ /**
+ * Gets the number of this RtfParagraphStyle in the stylesheet list.
+ *
+ * @return The number of this RtfParagraphStyle in the stylesheet list.
+ */
+ private int getStyleNumber() {
+ return this.styleNumber;
+ }
+
+ /**
+ * Sets the number of this RtfParagraphStyle in the stylesheet list.
+ *
+ * @param styleNumber The number to use.
+ */
+ protected void setStyleNumber(int styleNumber) {
+ this.styleNumber = styleNumber;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/style/RtfStylesheetList.java b/src/main/java/com/lowagie/text/rtf/style/RtfStylesheetList.java
new file mode 100644
index 0000000..edb04ea
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/style/RtfStylesheetList.java
@@ -0,0 +1,121 @@
+/*
+ * Created on Sep 22, 2005
+ *
+ * To change the template for this generated file go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+package com.lowagie.text.rtf.style;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import com.lowagie.text.rtf.RtfBasicElement;
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.RtfExtendedElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+
+/**
+ * The RtfStylesheetList stores the RtfParagraphStyles that are used in the document.
+ *
+ * @version $Revision: 1.1 $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfStylesheetList extends RtfElement implements RtfExtendedElement {
+
+ /**
+ * The HashMap containing the RtfParagraphStyles.
+ */
+ private HashMap styleMap = null;
+ /**
+ * Whether the default settings have been loaded.
+ */
+ private boolean defaultsLoaded = false;
+
+ /**
+ * Constructs a new RtfStylesheetList for the RtfDocument.
+ *
+ * @param doc The RtfDocument this RtfStylesheetList belongs to.
+ */
+ public RtfStylesheetList(RtfDocument doc) {
+ super(doc);
+ this.styleMap = new HashMap();
+ }
+
+ /**
+ * Register a RtfParagraphStyle with this RtfStylesheetList.
+ *
+ * @param rtfParagraphStyle The RtfParagraphStyle to add.
+ */
+ public void registerParagraphStyle(RtfParagraphStyle rtfParagraphStyle) {
+ RtfParagraphStyle tempStyle = new RtfParagraphStyle(this.document, rtfParagraphStyle);
+ tempStyle.setStyleNumber(this.styleMap.size());
+ tempStyle.handleInheritance();
+ this.styleMap.put(tempStyle.getStyleName(), tempStyle);
+ }
+
+ /**
+ * Registers all default styles. If styles with the given name have already been registered,
+ * then they are NOT overwritten.
+ */
+ private void registerDefaultStyles() {
+ defaultsLoaded = true;
+ if(!this.styleMap.containsKey(RtfParagraphStyle.STYLE_NORMAL.getStyleName())) {
+ registerParagraphStyle(RtfParagraphStyle.STYLE_NORMAL);
+ }
+ if(!this.styleMap.containsKey(RtfParagraphStyle.STYLE_HEADING_1.getStyleName())) {
+ registerParagraphStyle(RtfParagraphStyle.STYLE_HEADING_1);
+ }
+ if(!this.styleMap.containsKey(RtfParagraphStyle.STYLE_HEADING_2.getStyleName())) {
+ registerParagraphStyle(RtfParagraphStyle.STYLE_HEADING_2);
+ }
+ if(!this.styleMap.containsKey(RtfParagraphStyle.STYLE_HEADING_3.getStyleName())) {
+ registerParagraphStyle(RtfParagraphStyle.STYLE_HEADING_3);
+ }
+ }
+
+ /**
+ * Gets the RtfParagraphStyle with the given name. Makes sure that the defaults
+ * have been loaded.
+ *
+ * @param styleName The name of the RtfParagraphStyle to get.
+ * @return The RtfParagraphStyle with the given name or null.
+ */
+ public RtfParagraphStyle getRtfParagraphStyle(String styleName) {
+ if(!defaultsLoaded) {
+ registerDefaultStyles();
+ }
+ if(this.styleMap.containsKey(styleName)) {
+ return (RtfParagraphStyle) this.styleMap.get(styleName);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Writes the definition of the stylesheet list.
+ */
+ public byte[] writeDefinition() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write("{".getBytes());
+ result.write("\\stylesheet".getBytes());
+ result.write(RtfBasicElement.DELIMITER);
+ if(this.document.getDocumentSettings().isOutputDebugLineBreaks()) {
+ result.write("\n".getBytes());
+ }
+ Iterator it = this.styleMap.values().iterator();
+ while(it.hasNext()) {
+ result.write(((RtfParagraphStyle) it.next()).writeDefinition());
+ }
+ result.write("}".getBytes());
+ if(this.document.getDocumentSettings().isOutputDebugLineBreaks()) {
+ result.write('\n');
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/table/RtfBorder.java b/src/main/java/com/lowagie/text/rtf/table/RtfBorder.java
new file mode 100644
index 0000000..8a35867
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/table/RtfBorder.java
@@ -0,0 +1,577 @@
+/*
+ * $Id: RtfBorder.java,v 1.16 2005/05/04 14:33:54 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.table;
+
+import java.awt.Color;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.style.RtfColor;
+
+
+/**
+ * The RtfBorder handle one row or cell border.
+ * INTERNAL USE ONLY
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ * @author Unknown
+ */
+public class RtfBorder extends RtfElement {
+
+ /**
+ * Constant for the left row border
+ */
+ protected static final byte[] ROW_BORDER_LEFT = "\\trbrdrl".getBytes();
+ /**
+ * Constant for the top row border
+ */
+ protected static final byte[] ROW_BORDER_TOP = "\\trbrdrt".getBytes();
+ /**
+ * Constant for the right row border
+ */
+ protected static final byte[] ROW_BORDER_RIGHT = "\\trbrdrr".getBytes();
+ /**
+ * Constant for the bottom row border
+ */
+ protected static final byte[] ROW_BORDER_BOTTOM = "\\trbrdrb".getBytes();
+ /**
+ * Constant for the horizontal line
+ */
+ protected static final byte[] ROW_BORDER_HORIZONTAL = "\\trbrdrh".getBytes();
+ /**
+ * Constant for the vertical line
+ */
+ protected static final byte[] ROW_BORDER_VERTICAL = "\\trbrdrv".getBytes();
+ /**
+ * Constant for the left cell border
+ */
+ protected static final byte[] CELL_BORDER_LEFT = "\\clbrdrl".getBytes();
+ /**
+ * Constant for the top cell border
+ */
+ protected static final byte[] CELL_BORDER_TOP = "\\clbrdrt".getBytes();
+ /**
+ * Constant for the right cell border
+ */
+ protected static final byte[] CELL_BORDER_RIGHT = "\\clbrdrr".getBytes();
+ /**
+ * Constant for the bottom cell border
+ */
+ protected static final byte[] CELL_BORDER_BOTTOM = "\\clbrdrb".getBytes();
+ /**
+ * Constant for the border width
+ */
+ protected static final byte[] BORDER_WIDTH = "\\brdrw".getBytes();
+ /**
+ * Constant for the border colour number
+ */
+ protected static final byte[] BORDER_COLOR_NUMBER = "\\brdrcf".getBytes();
+ /**
+ * Constant for the single border style
+ */
+ protected static final byte[] BORDER_STYLE_SINGLE = "\\brdrs".getBytes();
+ /**
+ * Constant for the double thick border style
+ */
+ protected static final byte[] BORDER_STYLE_DOUBLE_THICK = "\\brdrth".getBytes();
+ /**
+ * Constant for the shadowed border style
+ */
+ protected static final byte[] BORDER_STYLE_SHADOWED = "\\brdrsh".getBytes();
+ /**
+ * Constant for the dotted border style
+ */
+ protected static final byte[] BORDER_STYLE_DOTTED = "\\brdrdot".getBytes();
+ /**
+ * Constant for the dashed border style
+ */
+ protected static final byte[] BORDER_STYLE_DASHED = "\\brdrdash".getBytes();
+ /**
+ * Constant for the hairline border style
+ */
+ protected static final byte[] BORDER_STYLE_HAIRLINE = "\\brdrhair".getBytes();
+ /**
+ * Constant for the double border style
+ */
+ protected static final byte[] BORDER_STYLE_DOUBLE = "\\brdrdb".getBytes();
+ /**
+ * Constant for the dot dash border style
+ */
+ protected static final byte[] BORDER_STYLE_DOT_DASH = "\\brdrdashd".getBytes();
+ /**
+ * Constant for the dot dot dash border style
+ */
+ protected static final byte[] BORDER_STYLE_DOT_DOT_DASH = "\\brdrdashdd".getBytes();
+ /**
+ * Constant for the triple border style
+ */
+ protected static final byte[] BORDER_STYLE_TRIPLE = "\\brdrtriple".getBytes();
+ /**
+ * Constant for the thick thin border style
+ */
+ protected static final byte[] BORDER_STYLE_THICK_THIN = "\\brdrtnthsg".getBytes();
+ /**
+ * Constant for the thin thick border style
+ */
+ protected static final byte[] BORDER_STYLE_THIN_THICK = "\\brdrthtnsg".getBytes();
+ /**
+ * Constant for the thin thick thin border style
+ */
+ protected static final byte[] BORDER_STYLE_THIN_THICK_THIN = "\\brdrtnthtnsg".getBytes();
+ /**
+ * Constant for the thick thin medium border style
+ */
+ protected static final byte[] BORDER_STYLE_THICK_THIN_MED = "\\brdrtnthmg".getBytes();
+ /**
+ * Constant for the thin thick medium border style
+ */
+ protected static final byte[] BORDER_STYLE_THIN_THICK_MED = "\\brdrthtnmg".getBytes();
+ /**
+ * Constant for the thin thick thin medium border style
+ */
+ protected static final byte[] BORDER_STYLE_THIN_THICK_THIN_MED = "\\brdrtnthtnmg".getBytes();
+ /**
+ * Constant for the thick thin large border style
+ */
+ protected static final byte[] BORDER_STYLE_THICK_THIN_LARGE = "\\brdrtnthlg".getBytes();
+ /**
+ * Constant for the thin thick large border style
+ */
+ protected static final byte[] BORDER_STYLE_THIN_THICK_LARGE = "\\brdrthtnlg".getBytes();
+ /**
+ * Constant for the thin thick thin large border style
+ */
+ protected static final byte[] BORDER_STYLE_THIN_THICK_THIN_LARGE = "\\brdrtnthtnlg".getBytes();
+ /**
+ * Constant for the wavy border style
+ */
+ protected static final byte[] BORDER_STYLE_WAVY = "\\brdrwavy".getBytes();
+ /**
+ * Constant for the double wavy border style
+ */
+ protected static final byte[] BORDER_STYLE_DOUBLE_WAVY = "\\brdrwavydb".getBytes();
+ /**
+ * Constant for the striped border style
+ */
+ protected static final byte[] BORDER_STYLE_STRIPED = "\\brdrdashdotstr".getBytes();
+ /**
+ * Constant for the embossed border style
+ */
+ protected static final byte[] BORDER_STYLE_EMBOSS = "\\brdremboss".getBytes();
+ /**
+ * Constant for the engraved border style
+ */
+ protected static final byte[] BORDER_STYLE_ENGRAVE = "\\brdrengrave".getBytes();
+
+ /**
+ * Constant for a row border
+ */
+ protected static final int ROW_BORDER = 1;
+ /**
+ * Constant for a cell border
+ */
+ protected static final int CELL_BORDER = 2;
+
+ /**
+ * This border is no border :-)
+ */
+ protected static final int NO_BORDER = 0;
+ /**
+ * Constant for a left border
+ */
+ protected static final int LEFT_BORDER = 1;
+ /**
+ * Constant for a top border
+ */
+ protected static final int TOP_BORDER = 2;
+ /**
+ * Constant for a right border
+ */
+ protected static final int RIGHT_BORDER = 4;
+ /**
+ * Constant for a bottom border
+ */
+ protected static final int BOTTOM_BORDER = 8;
+ /**
+ * Constant for a box (left, top, right, bottom) border
+ */
+ protected static final int BOX_BORDER = 15;
+ /**
+ * Constant for a vertical line
+ */
+ protected static final int VERTICAL_BORDER = 16;
+ /**
+ * Constant for a horizontal line
+ */
+ protected static final int HORIZONTAL_BORDER = 32;
+
+ /**
+ * Constant for a border with no border
+ */
+ public static final int BORDER_NONE = 0;
+ /**
+ * Constant for a single border
+ */
+ public static final int BORDER_SINGLE = 1;
+ /**
+ * Constant for a double thick border
+ */
+ public static final int BORDER_DOUBLE_THICK = 2;
+ /**
+ * Constant for a shadowed border
+ */
+ public static final int BORDER_SHADOWED = 3;
+ /**
+ * Constant for a dotted border
+ */
+ public static final int BORDER_DOTTED = 4;
+ /**
+ * Constant for a dashed border
+ */
+ public static final int BORDER_DASHED = 5;
+ /**
+ * Constant for a hairline border
+ */
+ public static final int BORDER_HAIRLINE = 6;
+ /**
+ * Constant for a double border
+ */
+ public static final int BORDER_DOUBLE = 7;
+ /**
+ * Constant for a dot dash border
+ */
+ public static final int BORDER_DOT_DASH = 8;
+ /**
+ * Constant for a dot dot dash border
+ */
+ public static final int BORDER_DOT_DOT_DASH = 9;
+ /**
+ * Constant for a triple border
+ */
+ public static final int BORDER_TRIPLE = 10;
+ /**
+ * Constant for a thick thin border
+ */
+ public static final int BORDER_THICK_THIN = 11;
+ /**
+ * Constant for a thin thick border
+ */
+ public static final int BORDER_THIN_THICK = 12;
+ /**
+ * Constant for a thin thick thin border
+ */
+ public static final int BORDER_THIN_THICK_THIN = 13;
+ /**
+ * Constant for a thick thin medium border
+ */
+ public static final int BORDER_THICK_THIN_MED = 14;
+ /**
+ * Constant for a thin thick medium border
+ */
+ public static final int BORDER_THIN_THICK_MED = 15;
+ /**
+ * Constant for a thin thick thin medium border
+ */
+ public static final int BORDER_THIN_THICK_THIN_MED = 16;
+ /**
+ * Constant for a thick thin large border
+ */
+ public static final int BORDER_THICK_THIN_LARGE = 17;
+ /**
+ * Constant for a thin thick large border
+ */
+ public static final int BORDER_THIN_THICK_LARGE = 18;
+ /**
+ * Constant for a thin thick thin large border
+ */
+ public static final int BORDER_THIN_THICK_THIN_LARGE = 19;
+ /**
+ * Constant for a wavy border
+ */
+ public static final int BORDER_WAVY = 20;
+ /**
+ * Constant for a double wavy border
+ */
+ public static final int BORDER_DOUBLE_WAVY = 21;
+ /**
+ * Constant for a striped border
+ */
+ public static final int BORDER_STRIPED = 22;
+ /**
+ * Constant for an embossed border
+ */
+ public static final int BORDER_EMBOSS = 23;
+ /**
+ * Constant for an engraved border
+ */
+ public static final int BORDER_ENGRAVE = 24;
+
+ /**
+ * The type of this RtfBorder
+ */
+ private int borderType = ROW_BORDER;
+ /**
+ * The position of this RtfBorder
+ */
+ private int borderPosition = NO_BORDER;
+ /**
+ * The style of this RtfBorder
+ */
+ private int borderStyle = BORDER_NONE;
+ /**
+ * The width of this RtfBorder
+ */
+ private int borderWidth = 20;
+ /**
+ * The colour of this RtfBorder
+ */
+ private RtfColor borderColor = null;
+
+ /**
+ * Makes a copy of the given RtfBorder
+ *
+ * @param doc The RtfDocument this RtfBorder belongs to
+ * @param borderType The border type of this RtfBorder
+ * @param border The RtfBorder to copy
+ */
+ protected RtfBorder(RtfDocument doc, int borderType, RtfBorder border) {
+ super(doc);
+ this.borderType = borderType;
+ this.borderPosition = border.getBorderPosition();
+ this.borderStyle = border.getBorderStyle();
+ this.borderWidth = border.getBorderWidth();
+ this.borderColor = new RtfColor(this.document, border.getBorderColor());
+ }
+
+ /**
+ * Constructs a RtfBorder
+ *
+ * @param doc The RtfDocument this RtfBorder belongs to
+ * @param borderType The type of border this RtfBorder is
+ * @param borderPosition The position of this RtfBorder
+ * @param borderStyle The style of this RtfBorder
+ * @param borderWidth The width of this RtfBorder
+ * @param borderColor The colour of this RtfBorder
+ */
+ protected RtfBorder(RtfDocument doc, int borderType, int borderPosition, int borderStyle, float borderWidth, Color borderColor) {
+ super(doc);
+ this.borderType = borderType;
+ this.borderPosition = borderPosition;
+ this.borderStyle = borderStyle;
+ if(borderWidth > 2) {
+ borderWidth = 2;
+ }
+ this.borderWidth = (int) (borderWidth * TWIPS_FACTOR);
+ if(this.borderWidth == 0) {
+ this.borderStyle = BORDER_NONE;
+ }
+ if(borderColor == null) {
+ this.borderColor = new RtfColor(this.document, new Color(0, 0, 0));
+ } else {
+ this.borderColor = new RtfColor(this.document, borderColor);
+ }
+ }
+
+ /**
+ * Writes the RtfBorder settings
+ *
+ * @return A byte array with the RtfBorder settings
+ */
+ public byte[] write() {
+ if(this.borderStyle == BORDER_NONE || this.borderPosition == NO_BORDER || this.borderWidth == 0) {
+ return new byte[0];
+ }
+
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ if(this.borderType == ROW_BORDER) {
+ switch(this.borderPosition) {
+ case LEFT_BORDER:
+ result.write(ROW_BORDER_LEFT);
+ break;
+ case TOP_BORDER:
+ result.write(ROW_BORDER_TOP);
+ break;
+ case RIGHT_BORDER:
+ result.write(ROW_BORDER_RIGHT);
+ break;
+ case BOTTOM_BORDER:
+ result.write(ROW_BORDER_BOTTOM);
+ break;
+ case HORIZONTAL_BORDER:
+ result.write(ROW_BORDER_HORIZONTAL);
+ break;
+ case VERTICAL_BORDER:
+ result.write(ROW_BORDER_VERTICAL);
+ break;
+ default:
+ return new byte[0];
+ }
+ result.write(writeBorderStyle());
+ result.write(BORDER_WIDTH);
+ result.write(intToByteArray(this.borderWidth));
+ result.write(BORDER_COLOR_NUMBER);
+ result.write(intToByteArray(this.borderColor.getColorNumber()));
+ result.write('\n');
+ } else if(this.borderType == CELL_BORDER) {
+ switch(this.borderPosition) {
+ case LEFT_BORDER:
+ result.write(CELL_BORDER_LEFT);
+ break;
+ case TOP_BORDER:
+ result.write(CELL_BORDER_TOP);
+ break;
+ case RIGHT_BORDER:
+ result.write(CELL_BORDER_RIGHT);
+ break;
+ case BOTTOM_BORDER:
+ result.write(CELL_BORDER_BOTTOM);
+ break;
+ default:
+ return new byte[0];
+ }
+ result.write(writeBorderStyle());
+ result.write(BORDER_WIDTH);
+ result.write(intToByteArray(this.borderWidth));
+ result.write(BORDER_COLOR_NUMBER);
+ result.write(intToByteArray(this.borderColor.getColorNumber()));
+ result.write('\n');
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+
+ return result.toByteArray();
+ }
+
+ /**
+ * Writes the style of this RtfBorder
+ *
+ * @return A byte array containing the style of this RtfBorder
+ */
+ private byte[] writeBorderStyle() {
+ switch(this.borderStyle) {
+ case BORDER_NONE : return new byte[0];
+ case BORDER_SINGLE : return BORDER_STYLE_SINGLE;
+ case BORDER_DOUBLE_THICK : return BORDER_STYLE_DOUBLE_THICK;
+ case BORDER_SHADOWED : return BORDER_STYLE_SHADOWED;
+ case BORDER_DOTTED : return BORDER_STYLE_DOTTED;
+ case BORDER_DASHED : return BORDER_STYLE_DASHED;
+ case BORDER_HAIRLINE : return BORDER_STYLE_HAIRLINE;
+ case BORDER_DOUBLE : return BORDER_STYLE_DOUBLE;
+ case BORDER_DOT_DASH : return BORDER_STYLE_DOT_DASH;
+ case BORDER_DOT_DOT_DASH : return BORDER_STYLE_DOT_DOT_DASH;
+ case BORDER_TRIPLE : return BORDER_STYLE_TRIPLE;
+ case BORDER_THICK_THIN : return BORDER_STYLE_THICK_THIN;
+ case BORDER_THIN_THICK : return BORDER_STYLE_THIN_THICK;
+ case BORDER_THIN_THICK_THIN : return BORDER_STYLE_THIN_THICK_THIN;
+ case BORDER_THICK_THIN_MED : return BORDER_STYLE_THICK_THIN_MED;
+ case BORDER_THIN_THICK_MED : return BORDER_STYLE_THIN_THICK_MED;
+ case BORDER_THIN_THICK_THIN_MED : return BORDER_STYLE_THIN_THICK_THIN_MED;
+ case BORDER_THICK_THIN_LARGE : return BORDER_STYLE_THICK_THIN_LARGE;
+ case BORDER_THIN_THICK_LARGE : return BORDER_STYLE_THIN_THICK_LARGE;
+ case BORDER_THIN_THICK_THIN_LARGE : return BORDER_STYLE_THIN_THICK_THIN_LARGE;
+ case BORDER_WAVY : return BORDER_STYLE_WAVY;
+ case BORDER_DOUBLE_WAVY : return BORDER_STYLE_DOUBLE_WAVY;
+ case BORDER_STRIPED : return BORDER_STYLE_STRIPED;
+ case BORDER_EMBOSS : return BORDER_STYLE_EMBOSS;
+ case BORDER_ENGRAVE : return BORDER_STYLE_ENGRAVE;
+ default : return BORDER_STYLE_SINGLE;
+ }
+ }
+
+ /**
+ * Gets the colour of this RtfBorder
+ *
+ * @return Returns RtfColor of this RtfBorder
+ */
+ protected RtfColor getBorderColor() {
+ return borderColor;
+ }
+
+ /**
+ * Gets the position of this RtfBorder
+ * @return Returns the position of this RtfBorder
+ */
+ protected int getBorderPosition() {
+ return borderPosition;
+ }
+
+ /**
+ * Gets the style of this RtfBorder
+ *
+ * @return Returns the style of this RtfBorder
+ */
+ protected int getBorderStyle() {
+ return borderStyle;
+ }
+
+ /**
+ * Gets the type of this RtfBorder
+ *
+ * @return Returns the type of this RtfBorder
+ */
+ protected int getBorderType() {
+ return borderType;
+ }
+
+ /**
+ * Gets the width of this RtfBorder
+ *
+ * @return Returns the width of this RtfBorder
+ */
+ protected int getBorderWidth() {
+ return borderWidth;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/table/RtfBorderGroup.java b/src/main/java/com/lowagie/text/rtf/table/RtfBorderGroup.java
new file mode 100644
index 0000000..5b8904f
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/table/RtfBorderGroup.java
@@ -0,0 +1,232 @@
+/*
+ * $Id: RtfBorderGroup.java,v 1.8 2004/12/14 12:51:39 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.table;
+
+import java.awt.Color;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+
+
+/**
+ * The RtfBorderGroup represents a collection of RtfBorders to use in a RtfCell
+ * or RtfTable.
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfBorderGroup extends RtfElement {
+ /**
+ * The type of borders this RtfBorderGroup contains.
+ * RtfBorder.ROW_BORDER or RtfBorder.CELL_BORDER
+ */
+ private int borderType = RtfBorder.ROW_BORDER;
+ /**
+ * The borders in this RtfBorderGroup
+ */
+ private Hashtable borders = null;
+
+ /**
+ * Constructs an empty RtfBorderGroup.
+ */
+ public RtfBorderGroup() {
+ super(null);
+ this.borders = new Hashtable();
+ }
+
+ /**
+ * Constructs a RtfBorderGroup with on border style for multiple borders.
+ *
+ * @param bordersToAdd The borders to add (Rectangle.LEFT, Rectangle.RIGHT, Rectangle.TOP, Rectangle.BOTTOM, Rectangle.BOX)
+ * @param borderStyle The style of border to add (from RtfBorder)
+ * @param borderWidth The border width to use
+ * @param borderColor The border color to use
+ */
+ public RtfBorderGroup(int bordersToAdd, int borderStyle, float borderWidth, Color borderColor) {
+ super(null);
+ this.borders = new Hashtable();
+ addBorder(bordersToAdd, borderStyle, borderWidth, borderColor);
+ }
+
+ /**
+ * Constructs a RtfBorderGroup based on another RtfBorderGroup.
+ *
+ * @param doc The RtfDocument this RtfBorderGroup belongs to
+ * @param borderType The type of borders this RtfBorderGroup contains
+ * @param borderGroup The RtfBorderGroup to use as a base
+ */
+ protected RtfBorderGroup(RtfDocument doc, int borderType, RtfBorderGroup borderGroup) {
+ super(doc);
+ this.borders = new Hashtable();
+ this.borderType = borderType;
+ if(borderGroup != null) {
+ Enumeration borderEnum = borderGroup.getBorders().keys();
+ while(borderEnum.hasMoreElements()) {
+ Integer borderPos = (Integer) borderEnum.nextElement();
+ RtfBorder border = (RtfBorder) borderGroup.getBorders().get(borderPos);
+ this.borders.put(borderPos, new RtfBorder(this.document, this.borderType, border));
+ }
+ }
+ }
+
+ /**
+ * Constructs a RtfBorderGroup with certain borders
+ *
+ * @param doc The RtfDocument this RtfBorderGroup belongs to
+ * @param borderType The type of borders this RtfBorderGroup contains
+ * @param bordersToUse The borders to add (Rectangle.LEFT, Rectangle.RIGHT, Rectangle.TOP, Rectangle.BOTTOM, Rectangle.BOX)
+ * @param borderWidth The border width to use
+ * @param borderColor The border color to use
+ */
+ protected RtfBorderGroup(RtfDocument doc, int borderType, int bordersToUse, float borderWidth, Color borderColor) {
+ super(doc);
+ this.borderType = borderType;
+ this.borders = new Hashtable();
+ addBorder(bordersToUse, RtfBorder.BORDER_SINGLE, borderWidth, borderColor);
+ }
+
+ /**
+ * Sets a border in the Hashtable of borders
+ *
+ * @param borderPosition The position of this RtfBorder
+ * @param borderStyle The type of borders this RtfBorderGroup contains
+ * @param borderWidth The border width to use
+ * @param borderColor The border color to use
+ */
+ private void setBorder(int borderPosition, int borderStyle, float borderWidth, Color borderColor) {
+ RtfBorder border = new RtfBorder(this.document, this.borderType, borderPosition, borderStyle, borderWidth, borderColor);
+ this.borders.put(new Integer(borderPosition), border);
+ }
+
+ /**
+ * Adds borders to the RtfBorderGroup
+ *
+ * @param bordersToAdd The borders to add (Rectangle.LEFT, Rectangle.RIGHT, Rectangle.TOP, Rectangle.BOTTOM, Rectangle.BOX)
+ * @param borderStyle The style of border to add (from RtfBorder)
+ * @param borderWidth The border width to use
+ * @param borderColor The border color to use
+ */
+ public void addBorder(int bordersToAdd, int borderStyle, float borderWidth, Color borderColor) {
+ if((bordersToAdd & Rectangle.LEFT) == Rectangle.LEFT) {
+ setBorder(RtfBorder.LEFT_BORDER, borderStyle, borderWidth, borderColor);
+ }
+ if((bordersToAdd & Rectangle.TOP) == Rectangle.TOP) {
+ setBorder(RtfBorder.TOP_BORDER, borderStyle, borderWidth, borderColor);
+ }
+ if((bordersToAdd & Rectangle.RIGHT) == Rectangle.RIGHT) {
+ setBorder(RtfBorder.RIGHT_BORDER, borderStyle, borderWidth, borderColor);
+ }
+ if((bordersToAdd & Rectangle.BOTTOM) == Rectangle.BOTTOM) {
+ setBorder(RtfBorder.BOTTOM_BORDER, borderStyle, borderWidth, borderColor);
+ }
+ if((bordersToAdd & Rectangle.BOX) == Rectangle.BOX && this.borderType == RtfBorder.ROW_BORDER) {
+ setBorder(RtfBorder.VERTICAL_BORDER, borderStyle, borderWidth, borderColor);
+ setBorder(RtfBorder.HORIZONTAL_BORDER, borderStyle, borderWidth, borderColor);
+ }
+ }
+
+ /**
+ * Removes borders from the list of borders
+ *
+ * @param bordersToRemove The borders to remove (from Rectangle)
+ */
+ public void removeBorder(int bordersToRemove) {
+ if((bordersToRemove & Rectangle.LEFT) == Rectangle.LEFT) {
+ this.borders.remove(new Integer(RtfBorder.LEFT_BORDER));
+ }
+ if((bordersToRemove & Rectangle.TOP) == Rectangle.TOP) {
+ this.borders.remove(new Integer(RtfBorder.TOP_BORDER));
+ }
+ if((bordersToRemove & Rectangle.RIGHT) == Rectangle.RIGHT) {
+ this.borders.remove(new Integer(RtfBorder.RIGHT_BORDER));
+ }
+ if((bordersToRemove & Rectangle.BOTTOM) == Rectangle.BOTTOM) {
+ this.borders.remove(new Integer(RtfBorder.BOTTOM_BORDER));
+ }
+ if((bordersToRemove & Rectangle.BOX) == Rectangle.BOX && this.borderType == RtfBorder.ROW_BORDER) {
+ this.borders.remove(new Integer(RtfBorder.VERTICAL_BORDER));
+ this.borders.remove(new Integer(RtfBorder.HORIZONTAL_BORDER));
+ }
+ }
+
+ /**
+ * Writes the borders of this RtfBorderGroup
+ *
+ * @return A byte array with the borders of this RtfBorderGroup
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ Enumeration borderEnum = this.borders.keys();
+ while(borderEnum.hasMoreElements()) {
+ result.write(((RtfBorder) this.borders.get(borderEnum.nextElement())).write());
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+
+ return result.toByteArray();
+ }
+
+ /**
+ * Gets the RtfBorders of this RtfBorderGroup
+ *
+ * @return The RtfBorders of this RtfBorderGroup
+ */
+ protected Hashtable getBorders() {
+ return this.borders;
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/table/RtfCell.java b/src/main/java/com/lowagie/text/rtf/table/RtfCell.java
new file mode 100644
index 0000000..abfee02
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/table/RtfCell.java
@@ -0,0 +1,534 @@
+/*
+ * $Id: RtfCell.java,v 1.15 2006/02/09 17:13:11 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.table;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.ArrayList;
+
+import com.lowagie.text.BadElementException;
+import com.lowagie.text.Cell;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.List;
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.rtf.RtfBasicElement;
+import com.lowagie.text.rtf.RtfExtendedElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.style.RtfColor;
+import com.lowagie.text.rtf.style.RtfParagraphStyle;
+import com.lowagie.text.rtf.text.RtfParagraph;
+
+
+/**
+ * The RtfCell wraps a Cell, but can also be added directly to a Table.
+ * The RtfCell is an extension of Cell, that supports a multitude of different
+ * borderstyles.
+ *
+ * @version $Id: RtfCell.java,v 1.15 2006/02/09 17:13:11 hallm Exp $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ * @author Steffen Stundzig
+ * @author Benoit WIART True
if this RtfCell is in a header, false
otherwise
+ */
+ public void setInHeader(boolean inHeader) {
+ this.inHeader = inHeader;
+ for(int i = 0; i < this.content.size(); i++) {
+ ((RtfBasicElement) this.content.get(i)).setInHeader(inHeader);
+ }
+ }
+
+ /**
+ * Transforms an integer into its String representation and then returns the bytes
+ * of that string.
+ *
+ * @param i The integer to convert
+ * @return A byte array representing the integer
+ */
+ private byte[] intToByteArray(int i) {
+ return Integer.toString(i).getBytes();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/table/RtfRow.java b/src/main/java/com/lowagie/text/rtf/table/RtfRow.java
new file mode 100644
index 0000000..4075ed2
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/table/RtfRow.java
@@ -0,0 +1,377 @@
+/*
+ * $Id: RtfRow.java,v 1.16 2005/05/24 16:30:53 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004, 2005 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.table;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import com.lowagie.text.Cell;
+import com.lowagie.text.Element;
+import com.lowagie.text.Row;
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+
+
+/**
+ * The RtfRow wraps one Row for a RtfTable.
+ * INTERNAL USE ONLY
+ *
+ * @version $Version:$
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ * @author Steffen Stundzig
+ * @author Lorenz Maierhofer True
if this RtfPhrase is in a table, false
otherwise
+ */
+ public void setInTable(boolean inTable) {
+ super.setInTable(inTable);
+ for(int i = 0; i < this.chunks.size(); i++) {
+ ((RtfBasicElement) this.chunks.get(i)).setInTable(inTable);
+ }
+ }
+
+ /**
+ * Sets whether this RtfPhrase is in a header. Sets the correct inTable setting for all
+ * child elements.
+ *
+ * @param inHeader True
if this RtfPhrase is in a header, false
otherwise
+ */
+ public void setInHeader(boolean inHeader) {
+ super.setInHeader(inHeader);
+ for(int i = 0; i < this.chunks.size(); i++) {
+ ((RtfBasicElement) this.chunks.get(i)).setInHeader(inHeader);
+ }
+ }
+
+ /**
+ * Sets the RtfDocument this RtfPhrase belongs to. Also sets the RtfDocument for all child
+ * elements.
+ *
+ * @param doc The RtfDocument to use
+ */
+ public void setRtfDocument(RtfDocument doc) {
+ super.setRtfDocument(doc);
+ for(int i = 0; i < this.chunks.size(); i++) {
+ ((RtfBasicElement) this.chunks.get(i)).setRtfDocument(this.document);
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/text/RtfSection.java b/src/main/java/com/lowagie/text/rtf/text/RtfSection.java
new file mode 100644
index 0000000..2b5ad86
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/text/RtfSection.java
@@ -0,0 +1,201 @@
+/*
+ * $Id: RtfSection.java,v 1.11 2005/08/19 21:17:04 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.text;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+import com.lowagie.text.Chunk;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.Section;
+import com.lowagie.text.rtf.RtfBasicElement;
+import com.lowagie.text.rtf.RtfElement;
+import com.lowagie.text.rtf.document.RtfDocument;
+import com.lowagie.text.rtf.field.RtfTOCEntry;
+
+
+/**
+ * The RtfSection wraps a Section element.
+ * INTERNAL CLASS
+ *
+ * @version $Revision: 1.11 $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfSection extends RtfElement {
+
+ /**
+ * The title paragraph of this RtfSection
+ */
+ protected RtfParagraph title = null;
+ /**
+ * The sub-items of this RtfSection
+ */
+ protected ArrayList items = null;
+
+ /**
+ * Constructs a RtfSection for a given Section. If the autogenerateTOCEntries
+ * property of the RtfDocument is set and the title is not empty then a TOC entry
+ * is generated for the title.
+ *
+ * @param doc The RtfDocument this RtfSection belongs to
+ * @param section The Section this RtfSection is based on
+ */
+ public RtfSection(RtfDocument doc, Section section) {
+ super(doc);
+ items = new ArrayList();
+ try {
+ if(section.title() != null) {
+ this.title = (RtfParagraph) doc.getMapper().mapElement(section.title());
+ }
+ if(document.getAutogenerateTOCEntries()) {
+ StringBuffer titleText = new StringBuffer();
+ Iterator it = section.title().iterator();
+ while(it.hasNext()) {
+ Element element = (Element) it.next();
+ if(element.type() == Element.CHUNK) {
+ titleText.append(((Chunk) element).content());
+ }
+ }
+ if(titleText.toString().trim().length() > 0) {
+ RtfTOCEntry tocEntry = new RtfTOCEntry(titleText.toString(), section.title().font());
+ tocEntry.setRtfDocument(this.document);
+ this.items.add(tocEntry);
+ }
+ }
+ Iterator iterator = section.iterator();
+ while(iterator.hasNext()) {
+ Element element = (Element) iterator.next();
+ RtfBasicElement rtfElement = doc.getMapper().mapElement(element);
+ if(rtfElement != null) {
+ items.add(rtfElement);
+ }
+ }
+
+ updateIndentation(section.indentationLeft(), section.indentationRight(), section.indentation());
+ } catch(DocumentException de) {
+ de.printStackTrace();
+ }
+ }
+
+ /**
+ * Write this RtfSection and its contents
+ *
+ * @return A byte array with the RtfSection and its contents
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ result.write(RtfParagraph.PARAGRAPH);
+ if(this.title != null) {
+ result.write(this.title.write());
+ }
+ for(int i = 0; i < items.size(); i++) {
+ result.write(((RtfBasicElement) items.get(i)).write());
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+
+ /**
+ * Sets whether this RtfSection is in a table. Sets the correct inTable setting for all
+ * child elements.
+ *
+ * @param inTable True
if this RtfSection is in a table, false
otherwise
+ */
+ public void setInTable(boolean inTable) {
+ super.setInTable(inTable);
+ for(int i = 0; i < this.items.size(); i++) {
+ ((RtfBasicElement) this.items.get(i)).setInTable(inTable);
+ }
+ }
+
+ /**
+ * Sets whether this RtfSection is in a header. Sets the correct inTable setting for all
+ * child elements.
+ *
+ * @param inHeader True
if this RtfSection is in a header, false
otherwise
+ */
+ public void setInHeader(boolean inHeader) {
+ super.setInHeader(inHeader);
+ for(int i = 0; i < this.items.size(); i++) {
+ ((RtfBasicElement) this.items.get(i)).setInHeader(inHeader);
+ }
+ }
+
+ /**
+ * Updates the left, right and content indentation of all RtfParagraph and RtfSection
+ * elements that this RtfSection contains.
+ *
+ * @param indentLeft The left indentation to add.
+ * @param indentRight The right indentation to add.
+ * @param indentContent The content indentation to add.
+ */
+ private void updateIndentation(float indentLeft, float indentRight, float indentContent) {
+ if(this.title != null) {
+ this.title.setIndentLeft((int) (this.title.getIndentLeft() + indentLeft * RtfElement.TWIPS_FACTOR));
+ this.title.setIndentRight((int) (this.title.getIndentRight() + indentRight * RtfElement.TWIPS_FACTOR));
+ }
+ for(int i = 0; i < this.items.size(); i++) {
+ RtfBasicElement rtfElement = (RtfBasicElement) this.items.get(i);
+ if(rtfElement instanceof RtfSection) {
+ ((RtfSection) rtfElement).updateIndentation(indentLeft + indentContent, indentRight, 0);
+ } else if(rtfElement instanceof RtfParagraph) {
+ ((RtfParagraph) rtfElement).setIndentLeft((int) (((RtfParagraph) rtfElement).getIndentLeft() + (indentLeft + indentContent) * RtfElement.TWIPS_FACTOR));
+ ((RtfParagraph) rtfElement).setIndentRight((int) (((RtfParagraph) rtfElement).getIndentRight() + indentRight * RtfElement.TWIPS_FACTOR));
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/text/RtfTab.java b/src/main/java/com/lowagie/text/rtf/text/RtfTab.java
new file mode 100644
index 0000000..1041ae6
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/text/RtfTab.java
@@ -0,0 +1,140 @@
+/*
+ * $Id: RtfTab.java,v 1.1 2006/06/17 09:51:30 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ * Co-Developer of the code is Mark Hall. Portions created by the Co-Developer are
+ * Copyright (C) 2006 by Mark Hall. All Rights Reserved
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.text;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import com.lowagie.text.rtf.RtfAddableElement;
+
+/**
+ * The RtfTab encapsulates a tab position and tab type in a paragraph.
+ * To add tabs to a paragraph construct new RtfTab objects with the desired
+ * tab position and alignment and then add them to the paragraph. In the actual
+ * text the tabs are then defined as standard \t characters.
+ *
+ * RtfTab tab = new RtfTab(300, RtfTab.TAB_LEFT_ALIGN);
+ *
+ * @version $Revision: 1.1 $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfTab extends RtfAddableElement {
+
+ /**
+ * A tab where the text is left aligned.
+ */
+ public static final int TAB_LEFT_ALIGN = 0;
+ /**
+ * A tab where the text is centre aligned.
+ */
+ public static final int TAB_CENTER_ALIGN = 1;
+ /**
+ * A tab where the text is right aligned.
+ */
+ public static final int TAB_RIGHT_ALIGN = 2;
+ /**
+ * A tab where the text is aligned on the decimal character. Which
+ * character that is depends on the language settings of the viewer.
+ */
+ public static final int TAB_DECIMAL_ALIGN = 3;
+
+ /**
+ * The tab position in twips.
+ */
+ private int position = 0;
+ /**
+ * The tab alignment.
+ */
+ private int type = TAB_LEFT_ALIGN;
+
+ /**
+ * Constructs a new RtfTab with the given position and type. The position
+ * is in standard iText points. The type is one of the tab alignment
+ * constants defined in the RtfTab.
+ *
+ * @param position The position of the tab in points.
+ * @param type The tab type constant.
+ */
+ public RtfTab(float position, int type) {
+ this.position = (int) Math.round(position * TWIPS_FACTOR);
+ switch(type) {
+ case TAB_LEFT_ALIGN: this.type = TAB_LEFT_ALIGN; break;
+ case TAB_CENTER_ALIGN: this.type = TAB_CENTER_ALIGN; break;
+ case TAB_RIGHT_ALIGN: this.type = TAB_RIGHT_ALIGN; break;
+ case TAB_DECIMAL_ALIGN: this.type = TAB_DECIMAL_ALIGN; break;
+ default: this.type = TAB_LEFT_ALIGN; break;
+ }
+ }
+
+ /**
+ * Writes the tab settings.
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ switch(this.type) {
+ case TAB_CENTER_ALIGN: result.write("\\tqc".getBytes()); break;
+ case TAB_RIGHT_ALIGN: result.write("\\tqr".getBytes()); break;
+ case TAB_DECIMAL_ALIGN: result.write("\\tqdec".getBytes()); break;
+ }
+ result.write("\\tx".getBytes());
+ result.write(intToByteArray(this.position));
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/rtf/text/RtfTabGroup.java b/src/main/java/com/lowagie/text/rtf/text/RtfTabGroup.java
new file mode 100644
index 0000000..e298221
--- /dev/null
+++ b/src/main/java/com/lowagie/text/rtf/text/RtfTabGroup.java
@@ -0,0 +1,127 @@
+/*
+ * $Id: RtfTabGroup.java,v 1.1 2006/06/17 09:51:30 hallm Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002, 2003, 2004 by Mark Hall
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ * Co-Developer of the code is Mark Hall. Portions created by the Co-Developer are
+ * Copyright (C) 2006 by Mark Hall. All Rights Reserved
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the ?GNU LIBRARY GENERAL PUBLIC LICENSE?), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.rtf.text;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import com.lowagie.text.rtf.RtfAddableElement;
+
+/**
+ * The RtfTabGroup is a convenience class if the same tabs are to be added
+ * to multiple paragraphs.
+ * Paragraph para = new Paragraph();
+ * para.add(tab);
+ * para.add("This paragraph has a\ttab defined.");
+ *
+ * RtfTabGroup tabs = new RtfTabGroup();
+ *
+ * @version $Revision: 1.1 $
+ * @author Mark Hall (mhall@edu.uni-klu.ac.at)
+ */
+public class RtfTabGroup extends RtfAddableElement {
+ /**
+ * The tabs to add.
+ */
+ private ArrayList tabs = null;
+
+ /**
+ * Constructs an empty RtfTabGroup.
+ */
+ public RtfTabGroup() {
+ this.tabs = new ArrayList();
+ }
+
+ /**
+ * Constructs a RtfTabGroup with a set of tabs.
+ *
+ * @param tabs An ArrayList with the RtfTabs to group in this RtfTabGroup.
+ */
+ public RtfTabGroup(ArrayList tabs) {
+ this.tabs = new ArrayList();
+ for(int i = 0; i < tabs.size(); i++) {
+ if(tabs.get(i) instanceof RtfTab) {
+ this.tabs.add(tabs.get(i));
+ }
+ }
+ }
+
+ /**
+ * Adds a RtfTab to the list of grouped tabs.
+ *
+ * @param tab The RtfTab to add.
+ */
+ public void add(RtfTab tab) {
+ this.tabs.add(tab);
+ }
+
+ /**
+ * Combines the tab output form all grouped tabs.
+ */
+ public byte[] write() {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ try {
+ for(int i = 0; i < this.tabs.size(); i++) {
+ result.write(((RtfTab) this.tabs.get(i)).write());
+ }
+ } catch(IOException ioe) {
+ ioe.printStackTrace();
+ }
+ return result.toByteArray();
+ }
+}
diff --git a/src/main/java/com/lowagie/text/xml/SAXiTextHandler.java b/src/main/java/com/lowagie/text/xml/SAXiTextHandler.java
new file mode 100644
index 0000000..00fb5bf
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/SAXiTextHandler.java
@@ -0,0 +1,921 @@
+/*
+ * $Id: SAXiTextHandler.java,v 1.52 2006/04/20 11:49:41 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EmptyStackException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Stack;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.lowagie.text.Anchor;
+import com.lowagie.text.Annotation;
+import com.lowagie.text.BadElementException;
+import com.lowagie.text.Cell;
+import com.lowagie.text.Chapter;
+import com.lowagie.text.Chunk;
+import com.lowagie.text.DocListener;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Element;
+import com.lowagie.text.ElementTags;
+import com.lowagie.text.Entities;
+import com.lowagie.text.ExceptionConverter;
+import com.lowagie.text.Font;
+import com.lowagie.text.Graphic;
+import com.lowagie.text.Image;
+import com.lowagie.text.List;
+import com.lowagie.text.ListItem;
+import com.lowagie.text.Meta;
+import com.lowagie.text.PageSize;
+import com.lowagie.text.Paragraph;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.Row;
+import com.lowagie.text.Section;
+import com.lowagie.text.Table;
+import com.lowagie.text.TextElementArray;
+import com.lowagie.text.html.HtmlTagMap;
+import com.lowagie.text.pdf.BaseFont;
+
+/**
+ * This class is a Handler that controls the iText XML to PDF conversion.
+ * Subclass it, if you want to change the way iText translates XML to PDF.
+ */
+
+public class SAXiTextHandler extends DefaultHandler {
+
+ /** This is the resulting document. */
+ protected DocListener document;
+
+ /**
+ * This is a
+ * tabs.add(new RtfTab(70, RtfTab.TAB_LEFT_ALIGN));
+ * tabs.add(new RtfTab(160, RtfTab.TAB_CENTER_ALIGN));
+ * tabs.add(new RtfTab(250, RtfTab.TAB_DECIMAL_ALIGN));
+ * tabs.add(new RtfTab(500, RtfTab.TAB_RIGHT_ALIGN));
+ * Paragraph para = new Paragraph();
+ * para.add(tabs);
+ * para.add("\tLeft aligned\tCentre aligned\t12,45\tRight aligned");Stack
of objects, waiting to be added to the
+ * document.
+ */
+ protected Stack stack;
+
+ /** Counts the number of chapters in this document. */
+ protected int chapters = 0;
+
+ /** This is the current chunk to which characters can be added. */
+ protected Chunk currentChunk = null;
+
+ /** This is the current chunk to which characters can be added. */
+ protected boolean ignore = false;
+
+ /**
+ * This is a flag that can be set, if you want to open and close the
+ * Document-object yourself.
+ */
+ protected boolean controlOpenClose = true;
+
+ /** current margin of a page. */
+ float topMargin = 36;
+
+ /** current margin of a page. */
+ float rightMargin = 36;
+
+ /** current margin of a page. */
+ float leftMargin = 36;
+
+ /** current margin of a page. */
+ float bottomMargin = 36;
+
+ /**
+ * @param document
+ * @throws IOException
+ */
+ public SAXiTextHandler(DocListener document)
+ throws IOException {
+ super();
+ this.document = document;
+ stack = new Stack();
+ }
+
+ protected HashMap myTags;
+
+ /**
+ * @param document
+ * @param myTags
+ * @throws IOException
+ */
+ public SAXiTextHandler(DocListener document, HtmlTagMap myTags)
+ throws IOException {
+ this(document);
+ this.myTags = myTags;
+ }
+
+ /**
+ * @param document
+ * @param myTags
+ * @param bf
+ * @throws IOException
+ */
+ public SAXiTextHandler(DocListener document, HtmlTagMap myTags,
+ BaseFont bf) throws IOException {
+ this(document, myTags);
+ this.bf = bf;
+ }
+
+ /**
+ * @param document
+ * @param myTags
+ * @throws IOException
+ */
+ public SAXiTextHandler(DocListener document, HashMap myTags)
+ throws IOException {
+ this(document);
+ this.myTags = myTags;
+ }
+
+ /**
+ * Sets the parameter that allows you to enable/disable the control over the
+ * Document.open() and Document.close() method.
+ * true
or false
+ */
+
+ private boolean isNewpage(String tag) {
+ return ElementTags.NEWPAGE.equals(tag);
+ }
+
+ /**
+ * Checks if a certain tag corresponds with the newpage-tag.
+ *
+ * @param tag
+ * a presumed tagname
+ * @return true
or false
+ */
+
+ private boolean isNewline(String tag) {
+ return ElementTags.NEWLINE.equals(tag);
+ }
+
+ /**
+ * Checks if a certain tag corresponds with the roottag.
+ *
+ * @param tag
+ * a presumed tagname
+ * @return true
if tag equals itext
+ *
,false
otherwise.
+ */
+
+ protected boolean isDocumentRoot(String tag) {
+ return ElementTags.ITEXT.equals(tag);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/xml/SAXmyHandler.java b/src/main/java/com/lowagie/text/xml/SAXmyHandler.java
new file mode 100644
index 0000000..acfaa83
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/SAXmyHandler.java
@@ -0,0 +1,128 @@
+/*
+ * $Id: SAXmyHandler.java,v 1.12 2005/11/29 16:00:17 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Properties;
+
+import org.xml.sax.Attributes;
+
+import com.lowagie.text.*;
+
+/**
+ * The Tags
-class maps several XHTML-tags to iText-objects.
+ */
+
+public class SAXmyHandler extends SAXiTextHandler {
+
+/** This hashmap contains all the custom keys and peers. */
+ protected HashMap myTags;
+
+/**
+ * Constructs a new SAXiTextHandler that will translate all the events
+ * triggered by the parser to actions on the Document
-object.
+ *
+ * @param document this is the document on which events must be triggered
+ * @param myTags a userdefined tagmap
+ * @throws IOException
+ * @throws DocumentException
+ */
+
+ public SAXmyHandler(DocListener document, HashMap myTags) throws DocumentException, IOException {
+ super(document);
+ this.myTags = myTags;
+ }
+
+/**
+ * This method gets called when a start tag is encountered.
+ *
+ * @param uri the Uniform Resource Identifier
+ * @param lname the local name (without prefix), or the empty string if Namespace processing is not being performed.
+ * @param name the name of the tag that is encountered
+ * @param attrs the list of attributes
+ */
+
+ public void startElement(String uri, String lname, String name, Attributes attrs) {
+ if (myTags.containsKey(name)) {
+ XmlPeer peer = (XmlPeer) myTags.get(name);
+ handleStartingTags(peer.getTag(), peer.getAttributes(attrs));
+ }
+ else {
+ Properties attributes = new Properties();
+ if (attrs != null) {
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String attribute = attrs.getQName(i);
+ attributes.setProperty(attribute, attrs.getValue(i));
+ }
+ }
+ handleStartingTags(name, attributes);
+ }
+ }
+
+ /**
+ * This method gets called when an end tag is encountered.
+ *
+ * @param uri the Uniform Resource Identifier
+ * @param lname the local name (without prefix), or the empty string if Namespace processing is not being performed.
+ * @param name the name of the tag that ends
+ */
+
+ public void endElement(String uri, String lname, String name) {
+ if (myTags.containsKey(name)) {
+ XmlPeer peer = (XmlPeer) myTags.get(name);
+ handleEndingTags(peer.getTag());
+ }
+ else {
+ handleEndingTags(name);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/xml/TagMap.java b/src/main/java/com/lowagie/text/xml/TagMap.java
new file mode 100644
index 0000000..9eb0cb4
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/TagMap.java
@@ -0,0 +1,213 @@
+/*
+ * $Id: TagMap.java,v 1.10 2004/12/14 11:25:42 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml;
+
+import java.util.HashMap;
+
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.Attributes;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import java.io.InputStream;
+import org.xml.sax.InputSource;
+
+import com.lowagie.text.*;
+
+/**
+ * The Tags
-class maps several XHTML-tags to iText-objects.
+ */
+
+public class TagMap extends HashMap {
+
+ class AttributeHandler extends DefaultHandler {
+
+/** This is a tag */
+ public static final String TAG = "tag";
+
+/** This is a tag */
+ public static final String ATTRIBUTE = "attribute";
+
+/** This is an attribute */
+ public static final String NAME = "name";
+
+/** This is an attribute */
+ public static final String ALIAS = "alias";
+
+/** This is an attribute */
+ public static final String VALUE = "value";
+
+/** This is an attribute */
+ public static final String CONTENT = "content";
+
+/** This is the tagmap using the AttributeHandler */
+ private HashMap tagMap;
+
+/** This is the current peer. */
+ private XmlPeer currentPeer;
+
+/**
+ * Constructs a new SAXiTextHandler that will translate all the events
+ * triggered by the parser to actions on the Document
-object.
+ *
+ * @param tagMap A Hashmap containing XmlPeer-objects
+ */
+
+ public AttributeHandler(HashMap tagMap) {
+ super();
+ this.tagMap = tagMap;
+ }
+
+/**
+ * This method gets called when a start tag is encountered.
+ *
+ * @param uri the Uniform Resource Identifier
+ * @param lname the local name (without prefix), or the empty string if Namespace processing is not being performed.
+ * @param tag the name of the tag that is encountered
+ * @param attrs the list of attributes
+ */
+
+ public void startElement(String uri, String lname, String tag, Attributes attrs) {
+ String name = attrs.getValue(NAME);
+ String alias = attrs.getValue(ALIAS);
+ String value = attrs.getValue(VALUE);
+ if (name != null) {
+ if(TAG.equals(tag)) {
+ currentPeer = new XmlPeer(name, alias);
+ }
+ else if (ATTRIBUTE.equals(tag)) {
+ if (alias != null) {
+ currentPeer.addAlias(name, alias);
+ }
+ if (value != null) {
+ currentPeer.addValue(name, value);
+ }
+ }
+ }
+ value = attrs.getValue(CONTENT);
+ if (value != null) {
+ currentPeer.setContent(value);
+ }
+ }
+
+/**
+ * This method gets called when ignorable white space encountered.
+ *
+ * @param ch an array of characters
+ * @param start the start position in the array
+ * @param length the number of characters to read from the array
+ */
+
+ public void ignorableWhitespace(char[] ch, int start, int length) {
+ // do nothing
+ }
+
+/**
+ * This method gets called when characters are encountered.
+ *
+ * @param ch an array of characters
+ * @param start the start position in the array
+ * @param length the number of characters to read from the array
+ */
+
+ public void characters(char[] ch, int start, int length) {
+ // do nothing
+ }
+
+/**
+ * This method gets called when an end tag is encountered.
+ *
+ * @param uri the Uniform Resource Identifier
+ * @param lname the local name (without prefix), or the empty string if Namespace processing is not being performed.
+ * @param tag the name of the tag that ends
+ */
+
+ public void endElement(String uri, String lname, String tag) {
+ if (TAG.equals(tag))
+ tagMap.put(currentPeer.getAlias(), currentPeer);
+ }
+ }
+
+ /**
+ * Constructs a TagMap
+ * @param tagfile the path to an XML file with the tagmap
+ */
+ public TagMap(String tagfile) {
+ super();
+ try {
+ init(TagMap.class.getClassLoader().getResourceAsStream(tagfile));
+ }catch(Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+ /**
+ * Constructs a TagMap.
+ * @param in An InputStream with the tagmap xml
+ */
+ public TagMap(InputStream in) {
+ super();
+ init(in);
+ }
+
+ protected void init(InputStream in) {
+ try {
+ SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
+ parser.parse(new InputSource(in), new AttributeHandler(this));
+ }
+ catch(Exception e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+
+
+}
diff --git a/src/main/java/com/lowagie/text/xml/XmlParser.java b/src/main/java/com/lowagie/text/xml/XmlParser.java
new file mode 100644
index 0000000..adc2695
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/XmlParser.java
@@ -0,0 +1,379 @@
+/*
+ * $Id: XmlParser.java,v 1.16 2005/12/06 10:23:08 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.HashMap;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import com.lowagie.text.DocListener;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.ExceptionConverter;
+
+/**
+ * This class can be used to parse an XML file.
+ */
+
+public class XmlParser {
+
+/** This is the instance of the parser. */
+ protected SAXParser parser;
+
+/**
+ * Constructs an XmlParser.
+ */
+
+ public XmlParser() {
+ try {
+ parser = SAXParserFactory.newInstance().newSAXParser();
+ }
+ catch(ParserConfigurationException pce) {
+ throw new ExceptionConverter(pce);
+ }
+ catch(SAXException se) {
+ throw new ExceptionConverter(se);
+ }
+ }
+
+/**
+ * Parses a given file.
+ * @param document The document that will listen to the parser
+ * @param is The InputStream with the contents
+ */
+
+ public void go(DocListener document, InputSource is) {
+ try {
+ parser.parse(is, new SAXiTextHandler(document));
+ }
+ catch(SAXException se) {
+ throw new ExceptionConverter(se);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+/**
+ * Parses a given file.
+ * @param document The document that will listen to the parser
+ * @param is The inputsource with the content
+ * @param tagmap A userdefined tagmap
+ */
+
+ public void go(DocListener document, InputSource is, String tagmap) {
+ try {
+ parser.parse(is, new SAXmyHandler(document, new TagMap(tagmap)));
+ }
+ catch(SAXException se) {
+ throw new ExceptionConverter(se);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ catch(DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+ /**
+ * Parses a given file.
+ * @param document The document that will listen to the parser
+ * @param is the inputsource with the content
+ * @param tagmap an inputstream to a userdefined tagmap
+ */
+
+ public void go(DocListener document, InputSource is, InputStream tagmap) {
+ try {
+ parser.parse(is, new SAXmyHandler(document, new TagMap(tagmap)));
+ }
+ catch(SAXException se) {
+ throw new ExceptionConverter(se);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ catch(DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+/**
+ * Parses a given file.
+ * @param document The document that will listen to the parser
+ * @param is the inputsource with the content
+ * @param tagmap a userdefined tagmap
+ */
+
+ public void go(DocListener document, InputSource is, HashMap tagmap) {
+ try {
+ parser.parse(is, new SAXmyHandler(document, tagmap));
+ }
+ catch(SAXException se) {
+ throw new ExceptionConverter(se);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ catch(DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+/**
+ * Parses a given file.
+ * @param document The document that will listen to the parser
+ * @param file The path to a file with the content
+ */
+
+ public void go(DocListener document, String file) {
+ try {
+ parser.parse(file, new SAXiTextHandler(document));
+ }
+ catch(SAXException se) {
+ throw new ExceptionConverter(se);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+/**
+ * Parses a given file.
+ * @param document the document that will listen to the parser
+ * @param file the path to a file with the content
+ * @param tagmap a userdefined tagmap
+ */
+
+ public void go(DocListener document, String file, String tagmap) {
+ try {
+ parser.parse(file, new SAXmyHandler(document, new TagMap(tagmap)));
+ }
+ catch(SAXException se) {
+ throw new ExceptionConverter(se);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ catch(DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+/**
+ * Parses a given file.
+ * @param document The document that will listen to the parser
+ * @param file the path to a file with the content
+ * @param tagmap a userdefined tagmap
+ */
+
+ public void go(DocListener document, String file, HashMap tagmap) {
+ try {
+ parser.parse(file, new SAXmyHandler(document, tagmap));
+ }
+ catch(SAXException se) {
+ throw new ExceptionConverter(se);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ catch(DocumentException de) {
+ throw new ExceptionConverter(de);
+ }
+ }
+
+/**
+ * Parses a given file that validates with the iText DTD and writes the content to a document.
+ * @param document The document that will listen to the parser
+ * @param is the inputsource with the content
+ */
+
+ public static void parse(DocListener document, InputSource is) {
+ XmlParser p = new XmlParser();
+ p.go(document, is);
+ }
+
+/**
+ * Parses a given file that validates with the iText DTD and writes the content to a document.
+ * @param document The document that will listen to the parser
+ * @param is The inputsource with the content
+ * @param tagmap a userdefined tagmap
+ */
+
+ public static void parse(DocListener document, InputSource is, String tagmap) {
+ XmlParser p = new XmlParser();
+ p.go(document, is, tagmap);
+ }
+
+/**
+ * Parses a given file and writes the content to a document, using a certain tagmap.
+ * @param document The document that will listen to the parser
+ * @param is The inputsource with the content
+ * @param tagmap a userdefined tagmap
+ */
+
+ public static void parse(DocListener document, InputSource is, HashMap tagmap) {
+ XmlParser p = new XmlParser();
+ p.go(document, is, tagmap);
+ }
+
+/**
+ * Parses a given file that validates with the iText DTD and writes the content to a document.
+ * @param document The document that will listen to the parser
+ * @param file The path to a file with the content
+ */
+
+ public static void parse(DocListener document, String file) {
+ XmlParser p = new XmlParser();
+ p.go(document, file);
+ }
+
+/**
+ * Parses a given file that validates with the iText DTD and writes the content to a document.
+ * @param document The document that will listen to the parser
+ * @param file The path to a file with the content
+ * @param tagmap A userdefined tagmap
+ */
+
+ public static void parse(DocListener document, String file, String tagmap) {
+ XmlParser p = new XmlParser();
+ p.go(document, file, tagmap);
+ }
+
+/**
+ * Parses a given file and writes the content to a document, using a certain tagmap.
+ * @param document The document that will listen to the parser
+ * @param file The path to a file with the content
+ * @param tagmap A userdefined tagmap
+ */
+
+ public static void parse(DocListener document, String file, HashMap tagmap) {
+ XmlParser p = new XmlParser();
+ p.go(document, file, tagmap);
+ }
+
+/**
+ * Parses a given file that validates with the iText DTD and writes the content to a document.
+ * @param document The document that will listen to the parser
+ * @param is The inputsource with the content
+ */
+
+ public static void parse(DocListener document, InputStream is) {
+ XmlParser p = new XmlParser();
+ p.go(document, new InputSource(is));
+ }
+
+/**
+ * Parses a given file that validates with the iText DTD and writes the content to a document.
+ * @param document The document that will listen to the parser
+ * @param is The inputstream with the content
+ * @param tagmap A userdefined tagmap
+ */
+
+ public static void parse(DocListener document, InputStream is, String tagmap) {
+ XmlParser p = new XmlParser();
+ p.go(document, new InputSource(is), tagmap);
+ }
+
+/**
+ * Parses a given file and writes the content to a document, using a certain tagmap.
+ * @param document The document that will listen to the parser
+ * @param is The InputStream with the content
+ * @param tagmap A userdefined tagmap
+ */
+
+ public static void parse(DocListener document, InputStream is, HashMap tagmap) {
+ XmlParser p = new XmlParser();
+ p.go(document, new InputSource(is), tagmap);
+ }
+
+/**
+ * Parses a given file that validates with the iText DTD and writes the content to a document.
+ * @param document The document that will listen to the parser
+ * @param is The reader that reads the content
+ */
+
+ public static void parse(DocListener document, Reader is) {
+ XmlParser p = new XmlParser();
+ p.go(document, new InputSource(is));
+ }
+
+/**
+ * Parses a given file that validates with the iText DTD and writes the content to a document.
+ * @param document The document that will listen to the parser
+ * @param is The reader that reads the content
+ * @param tagmap A userdefined tagmap
+ */
+
+ public static void parse(DocListener document, Reader is, String tagmap) {
+ XmlParser p = new XmlParser();
+ p.go(document, new InputSource(is), tagmap);
+ }
+
+/**
+ * Parses a given file and writes the content to a document, using a certain tagmap.
+ * @param document The document that will listen to the parser
+ * @param is The reader that reads the content
+ * @param tagmap A userdefined tagmap
+ */
+
+ public static void parse(DocListener document, Reader is, HashMap tagmap) {
+ XmlParser p = new XmlParser();
+ p.go(document, new InputSource(is), tagmap);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/xml/XmlPeer.java b/src/main/java/com/lowagie/text/xml/XmlPeer.java
new file mode 100644
index 0000000..5ca16a5
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/XmlPeer.java
@@ -0,0 +1,182 @@
+/*
+ * $Id: XmlPeer.java,v 1.13 2004/12/14 11:25:42 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml;
+
+import java.util.Properties;
+import org.xml.sax.Attributes;
+
+import com.lowagie.text.ElementTags;
+
+/**
+ * This interface is implemented by the peer of all the iText objects.
+ */
+
+public class XmlPeer {
+
+/** This is the name of the alias. */
+ protected String tagname;
+
+/** This is the name of the alias. */
+ protected String customTagname;
+
+/** This is the Map that contains the aliases of the attributes. */
+ protected Properties attributeAliases = new Properties();
+
+/** This is the Map that contains the default values of the attributes. */
+ protected Properties attributeValues = new Properties();
+
+/** This is String that contains the default content of the attributes. */
+ protected String defaultContent = null;
+
+/**
+ * Creates a XmlPeer.
+ * @param name the iText name of a tag
+ * @param alias the userdefined name of a tag
+ */
+
+ public XmlPeer(String name, String alias) {
+ this.tagname = name;
+ this.customTagname = alias;
+ }
+
+/**
+ * Gets the tagname of the peer.
+ * @return the iText name of a tag
+ */
+
+ public String getTag() {
+ return tagname;
+ }
+
+/**
+ * Gets the tagname of the peer.
+ * @return the userdefined tagname
+ */
+
+ public String getAlias() {
+ return customTagname;
+ }
+
+/** Gets the list of attributes of the peer.
+ * @param attrs the userdefined set of attributes
+ * @return the set of attributes translated to iText attributes
+ */
+ public Properties getAttributes(Attributes attrs) {
+ Properties attributes = new Properties();
+ attributes.putAll(attributeValues);
+ if (defaultContent != null) {
+ attributes.put(ElementTags.ITEXT, defaultContent);
+ }
+ if (attrs != null) {
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String attribute = getName(attrs.getQName(i));
+ attributes.setProperty(attribute, attrs.getValue(i));
+ }
+ }
+ return attributes;
+ }
+
+/**
+ * Sets an alias for an attribute.
+ *
+ * @param name the iText tagname
+ * @param alias the custom tagname
+ */
+
+ public void addAlias(String name, String alias) {
+ attributeAliases.put(alias, name);
+ }
+
+/**
+ * Sets a value for an attribute.
+ *
+ * @param name the iText tagname
+ * @param value the default value for this tag
+ */
+
+ public void addValue(String name, String value) {
+ attributeValues.put(name, value);
+ }
+
+/**
+ * Sets the default content.
+ *
+ * @param content the default content
+ */
+
+ public void setContent(String content) {
+ this.defaultContent = content;
+ }
+
+/**
+ * Returns the iText attribute name.
+ *
+ * @param name the custom attribute name
+ * @return iText translated attribute name
+ */
+
+ public String getName(String name) {
+ String value;
+ if ((value = attributeAliases.getProperty(name)) != null) {
+ return value;
+ }
+ return name;
+ }
+
+/**
+ * Returns the default values.
+ * @return A set of default (userdefined) values
+ */
+
+ public Properties getDefaultValues() {
+ return attributeValues;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/xml/XmlToHtml.java b/src/main/java/com/lowagie/text/xml/XmlToHtml.java
new file mode 100644
index 0000000..e145e57
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/XmlToHtml.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2002 by Matt Benson.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml;
+
+
+import java.io.OutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.html.HtmlWriter;
+
+
+/**
+ * HTML-specific subclass of XmlToXXX
.
+ *
+ * @version 1.0
+ * @author Matt Benson
+ */
+public class XmlToHtml
+ extends XmlToXXX
+{
+
+/**
+ * Construct an XmlToHtml
with the default page size.
+ */
+ public XmlToHtml()
+ {
+ super();
+ }//end default constructor
+
+
+/**
+ * Construct an XmlToHtml
with the specified page size.
+ * @param pageSize String
page size name from
+ * com.lowagie.text.PageSize
.
+ */
+ public XmlToHtml(String pageSize)
+ {
+ super(pageSize);
+ }//end constructor(String)
+
+
+/**
+ * Add a DocWriter
for the specified Document
and
+ * OutputStream
.
+ * @param doc The document to which content will be added
+ * @param out The outputstream where the HTML will be sent to
+ * @throws DocumentException if document errors occur.
+ */
+ protected final void addWriter(Document doc, OutputStream out)
+ throws DocumentException
+ {
+ HtmlWriter.getInstance(doc, out);
+ }//end addWriter
+
+
+/**
+ * Main method of the XmlToHtml
class.
+ * @param args String[]
of command-line arguments.
+ */
+ public static void main(String[] args)
+ {
+ int code = 0;
+
+ if (args.length > 1)
+ {
+ try
+ {
+ XmlToHtml x;
+ if (args.length > 2)
+ {
+ x = new XmlToHtml(args[2]);
+ }//end if at least 3 args
+ else
+ {
+ x = new XmlToHtml();
+ }//end else, only 2 args
+
+ x.parse(new FileInputStream(args[0]), new FileOutputStream(args[1]));
+ }//end try to do everything
+ catch (Exception ex)
+ {
+ code = 2;
+ ex.printStackTrace(System.err);
+ }//end catch Exception
+ }//end if at least 2 args
+ else
+ {
+ code = 1;
+ System.err.println(
+ "Usage: XmlToHtml [XML file in] [PDF file out] [optional page size]");
+ }//end else, not enough arguments
+
+ System.exit(code);
+ }//end main
+
+}//end class XmlToHtml
diff --git a/src/main/java/com/lowagie/text/xml/XmlToPdf.java b/src/main/java/com/lowagie/text/xml/XmlToPdf.java
new file mode 100644
index 0000000..3965831
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/XmlToPdf.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2002 by Matt Benson.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml;
+
+
+import java.io.OutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.pdf.PdfWriter;
+
+
+/**
+ * PDF-specific subclass of XmlToXXX
.
+ *
+ * @version 1.0
+ * @author Matt Benson
+ */
+public class XmlToPdf
+ extends XmlToXXX
+{
+
+/**
+ * Construct an XmlToPdf
with the default page size.
+ */
+ public XmlToPdf()
+ {
+ super();
+ }//end default constructor
+
+
+/**
+ * Construct an XmlToPdf
with the specified page size.
+ * @param pageSize String
page size name from
+ * com.lowagie.text.PageSize
.
+ */
+ public XmlToPdf(String pageSize)
+ {
+ super(pageSize);
+ }//end constructor(String)
+
+
+/**
+ * Add a DocWriter
for the specified Document
and
+ * OutputStream
.
+ * @param doc The document to which content will be added
+ * @param out The outputstream to which the PDF will be sent
+ * @throws DocumentException if document errors occur.
+ */
+ protected final void addWriter(Document doc, OutputStream out)
+ throws DocumentException
+ {
+ PdfWriter.getInstance(doc, out);
+ }//end addWriter
+
+
+/**
+ * Main method of the XmlToPdf
class.
+ * @param args String[]
of command-line arguments.
+ */
+ public static void main(String[] args)
+ {
+ int code = 0;
+
+ if (args.length > 1)
+ {
+ try
+ {
+ XmlToPdf x;
+ if (args.length > 2)
+ {
+ x = new XmlToPdf(args[2]);
+ }//end if at least 3 args
+ else
+ {
+ x = new XmlToPdf();
+ }//end else, only 2 args
+
+ x.parse(new FileInputStream(args[0]), new FileOutputStream(args[1]));
+ }//end try to do everything
+ catch (Exception ex)
+ {
+ code = 2;
+ ex.printStackTrace(System.err);
+ }//end catch Exception
+ }//end if at least 2 args
+ else
+ {
+ code = 1;
+ System.err.println(
+ "Usage: XmlToPdf [XML file in] [PDF file out] [optional page size]");
+ }//end else, not enough arguments
+
+ System.exit(code);
+ }//end main
+
+}//end class XmlToPdf
diff --git a/src/main/java/com/lowagie/text/xml/XmlToRtf.java b/src/main/java/com/lowagie/text/xml/XmlToRtf.java
new file mode 100644
index 0000000..d83b4b6
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/XmlToRtf.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2002 by Matt Benson.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml;
+
+
+import java.io.OutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.rtf.RtfWriter;
+
+
+/**
+ * RTF-specific subclass of XmlToXXX
.
+ *
+ * @version 1.0
+ * @author Matt Benson
+ */
+public class XmlToRtf
+ extends XmlToXXX
+{
+
+/**
+ * Construct an XmlToRtf
with the default page size.
+ */
+ public XmlToRtf()
+ {
+ super();
+ }//end default constructor
+
+
+/**
+ * Construct an XmlToRtf
with the specified page size.
+ * @param pageSize String
page size name from
+ * com.lowagie.text.PageSize
.
+ */
+ public XmlToRtf(String pageSize)
+ {
+ super(pageSize);
+ }//end constructor(String)
+
+
+/**
+ * Add a DocWriter
for the specified Document
and
+ * OutputStream
.
+ * @param doc The document to which content will be added
+ * @param out The outputstream to which RTF will be sent
+ * @throws DocumentException if document errors occur.
+ */
+ protected final void addWriter(Document doc, OutputStream out)
+ throws DocumentException
+ {
+ RtfWriter.getInstance(doc, out);
+ }//end addWriter
+
+
+/**
+ * Main method of the XmlToRtf
class.
+ * @param args String[]
of command-line arguments.
+ */
+ public static void main(String[] args)
+ {
+ int code = 0;
+
+ if (args.length > 1)
+ {
+ try
+ {
+ XmlToRtf x;
+ if (args.length > 2)
+ {
+ x = new XmlToRtf(args[2]);
+ }//end if at least 3 args
+ else
+ {
+ x = new XmlToRtf();
+ }//end else, only 2 args
+
+ x.parse(new FileInputStream(args[0]), new FileOutputStream(args[1]));
+ }//end try to do everything
+ catch (Exception ex)
+ {
+ code = 2;
+ ex.printStackTrace(System.err);
+ }//end catch Exception
+ }//end if at least 2 args
+ else
+ {
+ code = 1;
+ System.err.println(
+ "Usage: XmlToRtf [XML file in] [PDF file out] [optional page size]");
+ }//end else, not enough arguments
+
+ System.exit(code);
+ }//end main
+
+}//end class XmlToRtf
diff --git a/src/main/java/com/lowagie/text/xml/XmlToXXX.java b/src/main/java/com/lowagie/text/xml/XmlToXXX.java
new file mode 100644
index 0000000..016efdd
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/XmlToXXX.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2002 by Matt Benson.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml;
+
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.PageSize;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.DocumentException;
+
+
+/**
+ * Generates an specific file from an iText XML file.
+ *
+ * @version 1.0
+ * @author Matt Benson
+ */
+public abstract class XmlToXXX
+{
+
+ protected Rectangle pageSize;
+
+
+/**
+ * Construct an XmlToXXX
with the default page size.
+ */
+ public XmlToXXX()
+ {
+ this(PageSize.LETTER);
+ }//end default constructor
+
+
+/**
+ * Construct an XmlToXXX
with the specified page size.
+ * @param pageSize String
page size name from
+ * com.lowagie.text.PageSize
.
+ */
+ public XmlToXXX(String pageSize)
+ {
+ this(getPageSize(pageSize));
+ }//end constructor(String)
+
+
+ private XmlToXXX(Rectangle pageSize)
+ {
+ this.pageSize = pageSize;
+ }//end constructor(Rectangle)
+
+
+/**
+ * Parse the XML from the specified InputStream
, writing to the
+ * specified OutputStream
.
+ * @param in the InputStream
from which the XML is read.
+ * @param out the OutputStream
to which the result is written.
+ * @throws DocumentException if document errors occur.
+ */
+ public final void parse(InputStream in, OutputStream out)
+ throws DocumentException
+ {
+ Document doc = new Document(pageSize);
+
+ addWriter(doc, out);
+ XmlParser.parse(doc, in);
+ }//end parse
+
+
+ private static Rectangle getPageSize(String pageSize)
+ {
+ Rectangle result = PageSize.LETTER;
+ Field fld = null;
+ try
+ {
+ fld = PageSize.class.getDeclaredField(pageSize.toUpperCase());
+ result = (fld != null
+ && Modifier.isStatic(fld.getModifiers())
+ && fld.getType().equals(Rectangle.class)) ? (Rectangle)(fld.get(null))
+ : result;
+ }//end try to get field
+ catch (Exception ex)
+ {
+ System.err.println(ex.getMessage());
+ }//end catch Exception
+ return result;
+ }//end getPageSize
+
+
+/**
+ * Add a DocWriter
for the specified Document
and
+ * OutputStream
.
+ * @param doc The document to which content will be added
+ * @param out The outputstream to which the document will be sent
+ * @throws DocumentException if document errors occur.
+ */
+ protected abstract void addWriter(Document doc, OutputStream out)
+ throws DocumentException;
+
+}//end class XmlToXXX
diff --git a/src/main/java/com/lowagie/text/xml/XmlWriter.java b/src/main/java/com/lowagie/text/xml/XmlWriter.java
new file mode 100644
index 0000000..ac3f2d8
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/XmlWriter.java
@@ -0,0 +1,893 @@
+/*
+ * $Id: XmlWriter.java,v 1.35 2005/12/09 12:33:25 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 LIBRARY GENERAL PUBLIC LICENSE for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.TreeMap;
+import java.util.HashMap;
+
+import com.lowagie.text.*;
+import com.lowagie.text.markup.MarkupTags;
+
+/**
+ * A DocWriter
class for XML (Remark: this class is not finished yet!).
+ * XmlWriter
can be added as a DocListener
+ * to a certain Document
by getting an instance.
+ * Every Element
added to the original Document
+ * will be written to the OutputStream
of this XmlWriter
.
+ *
+ * // creation of the document with a certain size and certain margins
+ * Document document = new Document(PageSize.A4, 50, 50, 50, 50);
+ * try {
+ * // this will write XML to the Standard OutputStream
+ * XmlWriter.getInstance(document, System.out);
+ * // this will write XML to a file called text.html
+ * XmlWriter.getInstance(document, new FileOutputStream("text.xml"));
+ * // this will write XML to for instance the OutputStream of a HttpServletResponse-object
+ * XmlWriter.getInstance(document, response.getOutputStream());
+ * }
+ * catch(DocumentException de) {
+ * System.err.println(de.getMessage());
+ * }
+ * // this will close the document and all the OutputStreams listening to it
+ * document.close();
XmlWriter
.
+ *
+ * @param doc The Document
that has to be written as XML
+ * @param os The OutputStream
the writer has to write to.
+ */
+
+ protected XmlWriter(Document doc, OutputStream os) {
+ super(doc, os);
+
+ document.addDocListener(this);
+ try {
+ os.write(PROLOG);
+ os.write(DOCTYPE);
+ os.write(DTD);
+ os.write(QUOTE);
+ os.write(GT);
+ os.write(NEWLINE);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+/**
+ * Constructs an XmlWriter
.
+ *
+ * @param doc The Document
that has to be written as XML
+ * @param os The OutputStream
the writer has to write to.
+ * @param dtd The DTD to use
+ */
+
+ protected XmlWriter(Document doc, OutputStream os, String dtd) {
+ super(doc, os);
+
+ document.addDocListener(this);
+ try {
+ os.write(PROLOG);
+ os.write(DOCTYPE);
+ os.write(getISOBytes(dtd));
+ os.write(QUOTE);
+ os.write(GT);
+ os.write(NEWLINE);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+ // get an instance of the XmlWriter
+
+/**
+ * Gets an instance of the XmlWriter
.
+ *
+ * @param document The Document
that has to be written
+ * @param os The OutputStream
the writer has to write to.
+ * @return a new XmlWriter
+ */
+
+ public static XmlWriter getInstance(Document document, OutputStream os) {
+ return new XmlWriter(document, os);
+ }
+
+/**
+ * Gets an instance of the XmlWriter
.
+ *
+ * @param document The Document
that has to be written
+ * @param os The OutputStream
the writer has to write to.
+ * @param dtd The DTD to use
+ * @return a new XmlWriter
+ */
+
+ public static XmlWriter getInstance(Document document, OutputStream os, String dtd) {
+ return new XmlWriter(document, os, dtd);
+ }
+
+ // implementation of the DocListener methods
+
+/**
+ * Signals that an Element
was added to the Document
.
+ *
+ * @param element A high level object that will be added to the XML
+ * @return true
if the element was added, false
if not.
+ * @throws DocumentException when a document isn't open yet, or has been closed
+ */
+
+ public boolean add(Element element) throws DocumentException {
+ if (pause) {
+ return false;
+ }
+ try {
+ switch(element.type()) {
+ case Element.TITLE:
+ itext.put(ElementTags.TITLE, ((Meta)element).content());
+ return true;
+ case Element.SUBJECT:
+ itext.put(ElementTags.SUBJECT, ((Meta)element).content());
+ return true;
+ case Element.KEYWORDS:
+ itext.put(ElementTags.KEYWORDS, ((Meta)element).content());
+ return true;
+ case Element.AUTHOR:
+ itext.put(ElementTags.AUTHOR, ((Meta)element).content());
+ return true;
+ default:
+ write(element, 1);
+ return true;
+ }
+ }
+ catch(IOException ioe) {
+ return false;
+ }
+ }
+
+/**
+ * Signals that the Document
has been opened and that
+ * Elements
can be added.
+ */
+
+ public void open() {
+ super.open();
+ try {
+ itext.put(ElementTags.PRODUCER, "iTextXML by lowagie.com");
+ itext.put(ElementTags.CREATIONDATE, new Date().toString());
+ writeStart(ElementTags.ITEXT);
+ String key;
+ for (java.util.Iterator i = itext.keySet().iterator(); i.hasNext(); ) {
+ key = (String) i.next();
+ write(key, (String) itext.get(key));
+ }
+ os.write(GT);
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+/**
+ * Signals that an new page has to be LTed.
+ *
+ * @return true
if the page was added, false
if not.
+ * @throws DocumentException when a document isn't open yet, or has been closed
+ */
+
+ public boolean newPage() throws DocumentException {
+ if (pause || !open) {
+ return false;
+ }
+ try {
+ writeStart(ElementTags.NEWPAGE);
+ writeEnd();
+ return true;
+ }
+ catch(IOException ioe) {
+ return false;
+ }
+ }
+
+/**
+ * Signals that the Document
was closed and that no other
+ * Elements
will be added.
+ */
+
+ public void close() {
+ try {
+ os.write(NEWLINE);
+ writeEnd(ElementTags.ITEXT);
+ super.close();
+ }
+ catch(IOException ioe) {
+ throw new ExceptionConverter(ioe);
+ }
+ }
+
+ // methods
+
+/**
+ * Writes the XML representation of an element.
+ *
+ * @param element the element
+ * @param indent the indentation
+ * @throws IOException
+ */
+
+ private void write(Element element, int indent) throws IOException {
+ switch(element.type()) {
+ case Element.CHUNK:
+ {
+ Chunk chunk = (Chunk) element;
+
+ // if the chunk contains an image, return the image representation
+ try {
+ Image image = chunk.getImage();
+ write(image, indent);
+ return;
+ }
+ catch(NullPointerException npe) {
+ // empty on purpose
+ }
+
+ addTabs(indent);
+ HashMap attributes = chunk.getAttributes();
+ if (chunk.font().isStandardFont() && attributes == null && !(hasMarkupAttributes(chunk))) {
+ write(encode(chunk.content(), indent));
+ return;
+ }
+ else {
+ if (attributes != null && attributes.get(Chunk.NEWPAGE) != null) {
+ writeStart(ElementTags.NEWPAGE);
+ writeEnd();
+ return;
+ }
+ writeStart(ElementTags.CHUNK);
+ if (! chunk.font().isStandardFont()) {
+ write(chunk.font());
+ }
+ if (attributes != null) {
+ for (Iterator i = attributes.keySet().iterator(); i.hasNext(); ) {
+ String key = (String) i.next();
+ if (key.equals(Chunk.LOCALGOTO)
+ || key.equals(Chunk.LOCALDESTINATION)
+ || key.equals(Chunk.GENERICTAG)) {
+ String value = (String) attributes.get(key);
+ write(key.toLowerCase(), value);
+ }
+ if (key.equals(Chunk.SUBSUPSCRIPT)) {
+ write(key.toLowerCase(), String.valueOf((Float) attributes.get(key)));
+ }
+ }
+ }
+ if (hasMarkupAttributes(chunk)) {
+ writeMarkupAttributes((MarkupAttributes)chunk);
+ }
+ os.write(GT);
+ write(encode(chunk.content(), indent));
+ writeEnd(ElementTags.CHUNK);
+ }
+ return;
+ }
+ case Element.PHRASE:
+ {
+ Phrase phrase = (Phrase) element;
+
+ addTabs(indent);
+ writeStart(ElementTags.PHRASE);
+
+ write(ElementTags.LEADING, String.valueOf(phrase.leading()));
+ write(phrase.font());
+ if (hasMarkupAttributes(phrase)) {
+ writeMarkupAttributes((MarkupAttributes)phrase);
+ }
+ os.write(GT);
+
+ for (Iterator i = phrase.iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+
+ addTabs(indent);
+ writeEnd(ElementTags.PHRASE);
+ return;
+ }
+ case Element.ANCHOR:
+ {
+ Anchor anchor = (Anchor) element;
+
+ addTabs(indent);
+ writeStart(ElementTags.ANCHOR);
+
+ write(ElementTags.LEADING, String.valueOf(anchor.leading()));
+ write(anchor.font());
+ if (anchor.name() != null) {
+ write(ElementTags.NAME, anchor.name());
+ }
+ if (anchor.reference() != null) {
+ write(ElementTags.REFERENCE, anchor.reference());
+ }
+ if (hasMarkupAttributes(anchor)) {
+ writeMarkupAttributes((MarkupAttributes)anchor);
+ }
+ os.write(GT);
+ for (Iterator i = anchor.iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ addTabs(indent);
+ writeEnd(ElementTags.ANCHOR);
+ return;
+ }
+ case Element.PARAGRAPH:
+ {
+ Paragraph paragraph = (Paragraph) element;
+
+ addTabs(indent);
+ writeStart(ElementTags.PARAGRAPH);
+
+ write(ElementTags.LEADING, String.valueOf(paragraph.leading()));
+ write(paragraph.font());
+ write(ElementTags.ALIGN, ElementTags.getAlignment(paragraph.alignment()));
+ if (paragraph.indentationLeft() != 0) {
+ write(ElementTags.INDENTATIONLEFT, String.valueOf(paragraph.indentationLeft()));
+ }
+ if (paragraph.indentationRight() != 0) {
+ write(ElementTags.INDENTATIONRIGHT, String.valueOf(paragraph.indentationRight()));
+ }
+ if (hasMarkupAttributes(paragraph)) {
+ writeMarkupAttributes((MarkupAttributes)paragraph);
+ }
+ os.write(GT);
+ for (Iterator i = paragraph.iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ addTabs(indent);
+ writeEnd(ElementTags.PARAGRAPH);
+ return;
+ }
+ case Element.SECTION:
+ {
+ Section section = (Section) element;
+
+ addTabs(indent);
+ writeStart(ElementTags.SECTION);
+ writeSection(section, indent);
+ writeEnd(ElementTags.SECTION);
+ return;
+ }
+ case Element.CHAPTER:
+ {
+ Chapter chapter = (Chapter) element;
+
+ addTabs(indent);
+ writeStart(ElementTags.CHAPTER);
+ if (hasMarkupAttributes(chapter)) {
+ writeMarkupAttributes((MarkupAttributes)chapter);
+ }
+ writeSection(chapter, indent);
+ writeEnd(ElementTags.CHAPTER);
+ return;
+
+ }
+ case Element.LIST:
+ {
+ List list = (List) element;
+
+ addTabs(indent);
+ writeStart(ElementTags.LIST);
+ write(ElementTags.NUMBERED, String.valueOf(list.isNumbered()));
+ write(ElementTags.SYMBOLINDENT, String.valueOf(list.symbolIndent()));
+ if (list.first() != 1) {
+ write(ElementTags.FIRST, String.valueOf(list.first()));
+ }
+ if (list.indentationLeft() != 0) {
+ write(ElementTags.INDENTATIONLEFT, String.valueOf(list.indentationLeft()));
+ }
+ if (list.indentationRight() != 0) {
+ write(ElementTags.INDENTATIONRIGHT, String.valueOf(list.indentationRight()));
+ }
+ if (!list.isNumbered()) {
+ write(ElementTags.LISTSYMBOL, list.symbol().content());
+ }
+ write(list.symbol().font());
+ if (hasMarkupAttributes(list)) {
+ writeMarkupAttributes((MarkupAttributes)list);
+ }
+ os.write(GT);
+ for (Iterator i = list.getItems().iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ addTabs(indent);
+ writeEnd(ElementTags.LIST);
+ return;
+ }
+ case Element.LISTITEM:
+ {
+ ListItem listItem = (ListItem) element;
+
+ addTabs(indent);
+ writeStart(ElementTags.LISTITEM);
+ write(ElementTags.LEADING, String.valueOf(listItem.leading()));
+ write(listItem.font());
+ write(ElementTags.ALIGN, ElementTags.getAlignment(listItem.alignment()));
+ if (listItem.indentationLeft() != 0) {
+ write(ElementTags.INDENTATIONLEFT, String.valueOf(listItem.indentationLeft()));
+ }
+ if (listItem.indentationRight() != 0) {
+ write(ElementTags.INDENTATIONRIGHT, String.valueOf(listItem.indentationRight()));
+ }
+ if (hasMarkupAttributes(listItem)) {
+ writeMarkupAttributes((MarkupAttributes)listItem);
+ }
+ os.write(GT);
+ for (Iterator i = listItem.iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ addTabs(indent);
+ writeEnd(ElementTags.LISTITEM);
+ return;
+ }
+ case Element.CELL:
+ {
+ Cell cell = (Cell) element;
+
+ addTabs(indent);
+ writeStart(ElementTags.CELL);
+ write((Rectangle) cell);
+ write(ElementTags.HORIZONTALALIGN, ElementTags.getAlignment(cell.horizontalAlignment()));
+ write(ElementTags.VERTICALALIGN, ElementTags.getAlignment(cell.verticalAlignment()));
+ if (cell.cellWidth() != null) {
+ write(ElementTags.WIDTH, cell.cellWidth());
+ }
+ if (cell.colspan() != 1) {
+ write(ElementTags.COLSPAN, String.valueOf(cell.colspan()));
+ }
+ if (cell.rowspan() != 1) {
+ write(ElementTags.ROWSPAN, String.valueOf(cell.rowspan()));
+ }
+ if (cell.header()) {
+ write(ElementTags.HEADER, String.valueOf(true));
+ }
+ if (cell.noWrap()) {
+ write(ElementTags.NOWRAP, String.valueOf(true));
+ }
+ if (cell.leading() != -1) {
+ write(ElementTags.LEADING, String.valueOf(cell.leading()));
+ }
+ if (hasMarkupAttributes(cell)) {
+ writeMarkupAttributes((MarkupAttributes)cell);
+ }
+ os.write(GT);
+ for (Iterator i = cell.getElements(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ addTabs(indent);
+ writeEnd(ElementTags.CELL);
+ return;
+ }
+ case Element.ROW:
+ {
+ Row row = (Row) element;
+
+ addTabs(indent);
+ writeStart(ElementTags.ROW);
+ if (hasMarkupAttributes(row)){
+ writeMarkupAttributes((MarkupAttributes)row);
+ }
+ os.write(GT);
+ Element cell;
+ for (int i = 0; i < row.columns(); i++) {
+ if ((cell = (Element)row.getCell(i)) != null) {
+ write(cell, indent + 1);
+ }
+ }
+ addTabs(indent);
+ writeEnd(ElementTags.ROW);
+ return;
+ }
+ case Element.TABLE:
+ {
+ Table table;
+ try {
+ table = (Table) element;
+ }
+ catch(ClassCastException cce) {
+ try {
+ table = ((SimpleTable)element).createTable();
+ } catch (BadElementException e) {
+ throw new ExceptionConverter(e);
+ }
+ }
+ table.complete();
+ addTabs(indent);
+ writeStart(ElementTags.TABLE);
+ write(ElementTags.COLUMNS, String.valueOf(table.columns()));
+ os.write(SPACE);
+ write(ElementTags.WIDTH);
+ os.write(EQUALS);
+ os.write(QUOTE);
+ if (! "".equals(table.absWidth())){
+ write(table.absWidth());
+ }
+ else{
+ write(String.valueOf(table.widthPercentage()));
+ write("%");
+ }
+ os.write(QUOTE);
+ write(ElementTags.ALIGN, ElementTags.getAlignment(table.alignment()));
+ write(ElementTags.CELLPADDING, String.valueOf(table.cellpadding()));
+ write(ElementTags.CELLSPACING, String.valueOf(table.cellspacing()));
+ os.write(SPACE);
+ write(ElementTags.WIDTHS);
+ os.write(EQUALS);
+ os.write(QUOTE);
+ float[] widths = table.getProportionalWidths();
+ write(String.valueOf(widths[0]));
+ for (int i = 1; i < widths.length; i++) {
+ write(";");
+ write(String.valueOf(widths[i]));
+ }
+ os.write(QUOTE);
+ write((Rectangle) table);
+ if (hasMarkupAttributes(table)) {
+ writeMarkupAttributes((MarkupAttributes)table);
+ }
+ os.write(GT);
+ Row row;
+ for (Iterator iterator = table.iterator(); iterator.hasNext(); ) {
+ row = (Row) iterator.next();
+ write(row, indent + 1);
+ }
+ addTabs(indent);
+ writeEnd(ElementTags.TABLE);
+ return;
+ }
+ case Element.ANNOTATION:
+ {
+ Annotation annotation = (Annotation) element;
+
+ addTabs(indent);
+ writeStart(ElementTags.ANNOTATION);
+ if (annotation.title() != null) {
+ write(ElementTags.TITLE, annotation.title());
+ }
+ if (annotation.content() != null) {
+ write(ElementTags.CONTENT, annotation.content());
+ }
+ if (hasMarkupAttributes(annotation)) {
+ writeMarkupAttributes((MarkupAttributes)annotation);
+ }
+ writeEnd();
+ return;
+ }
+ case Element.IMGRAW:
+ case Element.JPEG:
+ case Element.IMGTEMPLATE:
+ {
+ Image image = (Image) element;
+ if (image.url() == null) {
+ return;
+ }
+
+ addTabs(indent);
+ writeStart(ElementTags.IMAGE);
+ write(ElementTags.URL, image.url().toString());
+ if ((image.alignment() & Image.LEFT) > 0) {
+ write(ElementTags.ALIGN, ElementTags.ALIGN_LEFT);
+ }
+ else if ((image.alignment() & Image.RIGHT) > 0) {
+ write(ElementTags.ALIGN, ElementTags.ALIGN_RIGHT);
+ }
+ else if ((image.alignment() & Image.MIDDLE) > 0) {
+ write(ElementTags.ALIGN, ElementTags.ALIGN_MIDDLE);
+ }
+ if ((image.alignment() & Image.UNDERLYING) > 0) {
+ write(ElementTags.UNDERLYING, String.valueOf(true));
+ }
+ if ((image.alignment() & Image.TEXTWRAP) > 0) {
+ write(ElementTags.TEXTWRAP, String.valueOf(true));
+ }
+ if (image.alt() != null) {
+ write(ElementTags.ALT, image.alt());
+ }
+ if (image.hasAbsolutePosition()) {
+ write(ElementTags.ABSOLUTEX, String.valueOf(image.absoluteX()));
+ write(ElementTags.ABSOLUTEY, String.valueOf(image.absoluteY()));
+ }
+ write(ElementTags.PLAINWIDTH, String.valueOf(image.plainWidth()));
+ write(ElementTags.PLAINHEIGHT, String.valueOf(image.plainHeight()));
+ if (hasMarkupAttributes(image)) {
+ writeMarkupAttributes((MarkupAttributes)image);
+ }
+ writeEnd();
+ return;
+ }
+ default:
+ return;
+ }
+ }
+
+/**
+ * Writes the XML representation of a section.
+ *
+ * @param section the section to write
+ * @param indent the indentation
+ * @throws IOException
+ */
+
+ private void writeSection(Section section, int indent) throws IOException {
+ write(ElementTags.NUMBERDEPTH, String.valueOf(section.numberDepth()));
+ write(ElementTags.DEPTH, String.valueOf(section.depth()));
+ write(ElementTags.INDENT, String.valueOf(section.indentation()));
+ if (section.indentationLeft() != 0) {
+ write(ElementTags.INDENTATIONLEFT, String.valueOf(section.indentationLeft()));
+ }
+ if (section.indentationRight() != 0) {
+ write(ElementTags.INDENTATIONRIGHT, String.valueOf(section.indentationRight()));
+ }
+ os.write(GT);
+
+ if (section.title() != null) {
+ addTabs(indent + 1);
+ writeStart(ElementTags.TITLE);
+ write(ElementTags.LEADING, String.valueOf(section.title().leading()));
+ write(ElementTags.ALIGN, ElementTags.getAlignment(section.title().alignment()));
+ if (section.title().indentationLeft() != 0) {
+ write(ElementTags.INDENTATIONLEFT, String.valueOf(section.title().indentationLeft()));
+ }
+ if (section.title().indentationRight() != 0) {
+ write(ElementTags.INDENTATIONRIGHT, String.valueOf(section.title().indentationRight()));
+ }
+ write(section.title().font());
+ os.write(GT);
+ Iterator i = section.title().iterator();
+ if (section.depth() > 0) {
+ i.next();
+ }
+ while (i.hasNext()) {
+ write((Element) i.next(), indent + 2);
+ }
+ addTabs(indent + 1);
+ writeEnd(ElementTags.TITLE);
+ }
+ for (Iterator i = section.iterator(); i.hasNext(); ) {
+ write((Element) i.next(), indent + 1);
+ }
+ addTabs(indent);
+ }
+
+/**
+ * Writes the XML representation of this Rectangle
.
+ *
+ * @param rectangle a Rectangle
+ * @throws IOException
+ */
+
+ private void write(Rectangle rectangle) throws IOException {
+ if (rectangle.borderWidth() != Rectangle.UNDEFINED) {
+ write(ElementTags.BORDERWIDTH, String.valueOf(rectangle.borderWidth()));
+ if (rectangle.hasBorder(Rectangle.LEFT)) {
+ write(ElementTags.LEFT, String.valueOf(true));
+ }
+ if (rectangle.hasBorder(Rectangle.RIGHT)) {
+ write(ElementTags.RIGHT, String.valueOf(true));
+ }
+ if (rectangle.hasBorder(Rectangle.TOP)) {
+ write(ElementTags.TOP, String.valueOf(true));
+ }
+ if (rectangle.hasBorder(Rectangle.BOTTOM)) {
+ write(ElementTags.BOTTOM, String.valueOf(true));
+ }
+ }
+ if (rectangle.borderColor() != null) {
+ write(ElementTags.RED, String.valueOf(rectangle.borderColor().getRed()));
+ write(ElementTags.GREEN, String.valueOf(rectangle.borderColor().getGreen()));
+ write(ElementTags.BLUE, String.valueOf(rectangle.borderColor().getBlue()));
+ }
+ if (rectangle.backgroundColor() != null) {
+ write(ElementTags.BGRED, String.valueOf(rectangle.backgroundColor().getRed()));
+ write(ElementTags.BGGREEN, String.valueOf(rectangle.backgroundColor().getGreen()));
+ write(ElementTags.BGBLUE, String.valueOf(rectangle.backgroundColor().getBlue()));
+ }
+ }
+
+/**
+ * Encodes a String
.
+ *
+ * @param string the String
to encode
+ * @param indent counter that keeps the number of tabs that has to be added for indentation
+ * @return the encoded String
+ */
+
+ static final String encode(String string, int indent) {
+ int n = string.length();
+ int pos = 0;
+ char character;
+ StringBuffer buf = new StringBuffer();
+ // loop over all the characters of the String.
+ for (int i = 0; i < n; i++) {
+ character = string.charAt(i);
+ // the Xmlcode of these characters are added to a StringBuffer one by one
+ switch(character) {
+ case ' ':
+ if ((i - pos) > 60) {
+ pos = i;
+ buf.append("\n");
+ addTabs(buf, indent);
+ break;
+ }
+ default:
+ buf.append(xmlCode[(int) character]);
+ }
+ }
+ return buf.toString();
+ }
+
+/**
+ * Adds a number of tabs to a StringBuffer
.
+ *
+ * @param buf the stringbuffer
+ * @param indent the number of tabs to add
+ */
+
+ static final void addTabs(StringBuffer buf, int indent) {
+ for (int i = 0; i < indent; i++) {
+ buf.append("\t");
+ }
+ }
+
+/**
+ * Writes the XML representation of a Font
.
+ *
+ * @param font a Font
+ * @throws IOException
+ */
+
+ private void write(Font font) throws IOException {
+ write(ElementTags.FONT, font.getFamilyname());
+ if (font.size() != Font.UNDEFINED) {
+ write(ElementTags.SIZE, String.valueOf(font.size()));
+ }
+ if (font.style() != Font.UNDEFINED) {
+ os.write(SPACE);
+ write(ElementTags.STYLE);
+ os.write(EQUALS);
+ os.write(QUOTE);
+ switch(font.style() & Font.BOLDITALIC) {
+ case Font.NORMAL:
+ write(MarkupTags.CSS_VALUE_NORMAL);
+ break;
+ case Font.BOLD:
+ write(MarkupTags.CSS_VALUE_BOLD);
+ break;
+ case Font.ITALIC:
+ write(MarkupTags.CSS_VALUE_ITALIC);
+ break;
+ case Font.BOLDITALIC:
+ write(MarkupTags.CSS_VALUE_BOLD);
+ write(", ");
+ write(MarkupTags.CSS_VALUE_ITALIC);
+ break;
+ }
+ if ((font.style() & Font.UNDERLINE) > 0) {
+ write(", ");
+ write(MarkupTags.CSS_VALUE_UNDERLINE);
+ }
+ if ((font.style() & Font.STRIKETHRU) > 0) {
+ write(", ");
+ write(MarkupTags.CSS_VALUE_LINETHROUGH);
+ }
+ os.write(QUOTE);
+ }
+ if (font.color() != null) {
+ write(ElementTags.RED, String.valueOf(font.color().getRed()));
+ write(ElementTags.GREEN, String.valueOf(font.color().getGreen()));
+ write(ElementTags.BLUE, String.valueOf(font.color().getBlue()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/text/xml/xmp/DublinCoreSchema.java b/src/main/java/com/lowagie/text/xml/xmp/DublinCoreSchema.java
new file mode 100644
index 0000000..53b1ade
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/xmp/DublinCoreSchema.java
@@ -0,0 +1,187 @@
+/*
+ * $Id: DublinCoreSchema.java,v 1.5 2005/09/08 07:50:15 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999-2005 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000-2005 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 LIBRARY GENERAL PUBLIC LICENSE for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml.xmp;
+
+import java.io.IOException;
+
+/**
+ * An implementation of an XmpSchema.
+ */
+public class DublinCoreSchema extends XmpSchema {
+
+ /** default namespace identifier*/
+ public static final String DEFAULT_XPATH_ID = "dc";
+ /** default namespace uri*/
+ public static final String DEFAULT_XPATH_URI = "http://purl.org/dc/elements/1.1/";
+
+ /** External Contributors to the resource (other than the authors). */
+ public static final String CONTRIBUTOR = "dc:contributor";
+ /** The extent or scope of the resource. */
+ public static final String COVERAGE = "dc:coverage";
+ /** The authors of the resource (listed in order of precedence, if significant). */
+ public static final String CREATOR = "dc:creator";
+ /** Date(s) that something interesting happened to the resource. */
+ public static final String DATE = "dc:date";
+ /** A textual description of the content of the resource. Multiple values may be present for different languages. */
+ public static final String DESCRIPTION = "dc:description";
+ /** The file format used when saving the resource. Tools and applications should set this property to the save format of the data. It may include appropriate qualifiers. */
+ public static final String FORMAT = "dc:format";
+ /** Unique identifier of the resource. */
+ public static final String IDENTIFIER = "dc:identifier";
+ /** An unordered array specifying the languages used in the resource. */
+ public static final String LANGUAGE = "dc:language";
+ /** Publishers. */
+ public static final String PUBLISHER = "dc:publisher";
+ /** Relationships to other documents. */
+ public static final String RELATION = "dc:relation";
+ /** Informal rights statement, selected by language. */
+ public static final String RIGHTS = "dc:rights";
+ /** Unique identifier of the work from which this resource was derived. */
+ public static final String SOURCE = "dc:source";
+ /** An unordered array of descriptive phrases or keywords that specify the topic of the content of the resource. */
+ public static final String SUBJECT = "dc:subject";
+ /** The title of the document, or the name given to the resource. Typically, it will be a name by which the resource is formally known. */
+ public static final String TITLE = "dc:title";
+ /** A document type; for example, novel, poem, or working paper. */
+ public static final String TYPE = "dc:type";
+
+
+ /**
+ * @throws IOException
+ */
+ public DublinCoreSchema() throws IOException {
+ super("xmlns:" + DEFAULT_XPATH_ID + "=\"" + DEFAULT_XPATH_URI + "\"");
+ setProperty(FORMAT, "application/pdf");
+ }
+
+ /**
+ * Adds a title.
+ * @param title
+ */
+ public void addTitle(String title) {
+ setProperty(TITLE, title);
+ }
+
+ /**
+ * Adds a description.
+ * @param desc
+ */
+ public void addDescription(String desc) {
+ setProperty(DESCRIPTION, desc);
+ }
+
+ /**
+ * Adds a subject.
+ * @param subject
+ */
+ public void addSubject(String subject) {
+ XmpArray array = new XmpArray(XmpArray.UNORDERED);
+ array.add(subject);
+ setProperty(SUBJECT, array);
+ }
+
+
+ /**
+ * Adds a subject.
+ * @param subject array of subjects
+ */
+ public void addSubject(String[] subject) {
+ XmpArray array = new XmpArray(XmpArray.UNORDERED);
+ for (int i = 0; i < subject.length; i++) {
+ array.add(subject[i]);
+ }
+ setProperty(SUBJECT, array);
+ }
+
+ /**
+ * Adds a single author.
+ * @param author
+ */
+ public void addAuthor(String author) {
+ XmpArray array = new XmpArray(XmpArray.ORDERED);
+ array.add(author);
+ setProperty(CREATOR, array);
+ }
+
+ /**
+ * Adds an array of authors.
+ * @param author
+ */
+ public void addAuthor(String[] author) {
+ XmpArray array = new XmpArray(XmpArray.ORDERED);
+ for (int i = 0; i < author.length; i++) {
+ array.add(author[i]);
+ }
+ setProperty(CREATOR, array);
+ }
+
+ /**
+ * Adds a single publisher.
+ * @param publisher
+ */
+ public void addPublisher(String publisher) {
+ XmpArray array = new XmpArray(XmpArray.ORDERED);
+ array.add(publisher);
+ setProperty(PUBLISHER, array);
+ }
+
+ /**
+ * Adds an array of publishers.
+ * @param publisher
+ */
+ public void addPublisher(String[] publisher) {
+ XmpArray array = new XmpArray(XmpArray.ORDERED);
+ for (int i = 0; i < publisher.length; i++) {
+ array.add(publisher[i]);
+ }
+ setProperty(PUBLISHER, array);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/xml/xmp/PdfSchema.java b/src/main/java/com/lowagie/text/xml/xmp/PdfSchema.java
new file mode 100644
index 0000000..2793e57
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/xmp/PdfSchema.java
@@ -0,0 +1,105 @@
+/*
+ * $Id: PdfSchema.java,v 1.4 2005/09/08 07:50:15 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999-2005 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000-2005 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 LIBRARY GENERAL PUBLIC LICENSE for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml.xmp;
+
+import com.lowagie.text.Document;
+import java.io.IOException;
+
+/**
+ * An implementation of an XmpSchema.
+ */
+public class PdfSchema extends XmpSchema {
+
+ /** default namespace identifier*/
+ public static final String DEFAULT_XPATH_ID = "pdf";
+ /** default namespace uri*/
+ public static final String DEFAULT_XPATH_URI = "http://ns.adobe.com/pdf/1.3/";
+
+ /** Keywords. */
+ public static final String KEYWORDS = "pdf:Keywords";
+ /** The PDF file version (for example: 1.0, 1.3, and so on). */
+ public static final String VERSION = "pdf:PDFVersion";
+ /** The Producer. */
+ public static final String PRODUCER = "pdf:Producer";
+
+
+ /**
+ * @throws IOException
+ */
+ public PdfSchema() throws IOException {
+ super("xmlns:" + DEFAULT_XPATH_ID + "=\"" + DEFAULT_XPATH_URI + "\"");
+ addProducer(Document.getVersion());
+ }
+
+ /**
+ * Adds keywords.
+ * @param keywords
+ */
+ public void addKeywords(String keywords) {
+ setProperty(KEYWORDS, keywords);
+ }
+
+ /**
+ * Adds the producer.
+ * @param producer
+ */
+ public void addProducer(String producer) {
+ setProperty(PRODUCER, producer);
+ }
+
+ /**
+ * Adds the version.
+ * @param version
+ */
+ public void addVersion(String version) {
+ setProperty(VERSION, version);
+ }
+}
diff --git a/src/main/java/com/lowagie/text/xml/xmp/XmpArray.java b/src/main/java/com/lowagie/text/xml/xmp/XmpArray.java
new file mode 100644
index 0000000..39016a7
--- /dev/null
+++ b/src/main/java/com/lowagie/text/xml/xmp/XmpArray.java
@@ -0,0 +1,99 @@
+/*
+ * $Id: XmpArray.java,v 1.3 2005/09/08 07:50:15 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999-2005 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000-2005 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 LIBRARY GENERAL PUBLIC LICENSE for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+
+package com.lowagie.text.xml.xmp;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * StringBuffer to construct an XMP array.
+ */
+public class XmpArray extends ArrayList {
+
+ /** An array that is unordered. */
+ public static final String UNORDERED = "rdf:Bag";
+ /** An array that is ordered. */
+ public static final String ORDERED = "rdf:Seq";
+ /** An array with alternatives. */
+ public static final String ALTERNATIVE = "rdf:Alt";
+
+ /** the type of array. */
+ protected String type;
+
+ /**
+ * Creates an XmpArray.
+ * @param type the type of array: UNORDERED, ORDERED or ALTERNATIVE.
+ */
+ public XmpArray(String type) {
+ this.type = type;
+ }
+
+ /**
+ * Returns the String representation of the XmpArray.
+ * @return a String representation
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer("<");
+ buf.append(type);
+ buf.append(">");
+ String s;
+ for (Iterator i = iterator(); i.hasNext(); ) {
+ s = (String) i.next();
+ buf.append("
+ */
+
+ public static void main(String[] args) {
+ if (args.length == 4) {
+ File srcdir = new File(args[0]);
+ File destdir = new File(args[1]);
+ File xsl_examples = new File(srcdir, args[2]);
+ File xsl_site = new File(srcdir, args[3]);
+ try {
+ System.out.print("Building tutorial: ");
+ root = new File(args[1], srcdir.getName()).getCanonicalPath();
+ System.out.println(root);
+ build = new FileWriter(new File(root, "build.xml"));
+ build.write("infile
, using an xslfile
to an
+ * outfile
.
+ *
+ * @param infile
+ * the path to an XML file
+ * @param xslfile
+ * the path to the XSL file
+ * @param outfile
+ * the path for the output file
+ */
+ public static void convert(File infile, File xslfile, File outfile) {
+ try {
+ // Create transformer factory
+ TransformerFactory factory = TransformerFactory.newInstance();
+
+ // Use the factory to create a template containing the xsl file
+ Templates template = factory.newTemplates(new StreamSource(
+ new FileInputStream(xslfile)));
+
+ // Use the template to create a transformer
+ Transformer xformer = template.newTransformer();
+
+ // passing 2 parameters
+ String branch = outfile.getParentFile().getCanonicalPath().substring(root.length());
+ branch = branch.replace(File.separatorChar, '/');
+ StringBuffer path = new StringBuffer();
+ for (int i = 0; i < branch.length(); i++) {
+ if (branch.charAt(i) == '/') path.append("/..");
+ }
+
+ xformer.setParameter("branch", branch);
+ xformer.setParameter("root", path.toString());
+
+ // Prepare the input and output files
+ Source source = new StreamSource(new FileInputStream(infile));
+ Result result = new StreamResult(new FileOutputStream(outfile));
+
+ // Apply the xsl file to the source file and write the result to the
+ // output file
+ xformer.transform(source, result);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
+//The End
diff --git a/src/main/java/com/lowagie/tools/CvsLogParser.java b/src/main/java/com/lowagie/tools/CvsLogParser.java
new file mode 100644
index 0000000..39181f1
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/CvsLogParser.java
@@ -0,0 +1,138 @@
+/*
+ * $Id: CvsLogParser.java,v 1.5 2006/05/22 11:00:30 blowagie Exp $
+ * $Name: $
+ *
+ * This code is free software. It may only be copied or modified
+ * if you include the following copyright notice:
+ *
+ * This code 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.
+ *
+ * itext@lowagie.com
+ */
+package com.lowagie.tools;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StreamTokenizer;
+
+import java.util.Enumeration;
+
+/**
+ * If you get a changelog from CVS, for instance:
+ * cvs -d:ext:blowagie@itext.cvs.sourceforge.net:/cvsroot/itext log -d ">2005-07-29"
+ * you get an overview that contains all the changes.
+ * With this class, you can parse out the important entries.
+ *
+ * @author blowagie
+ */
+public class CvsLogParser implements Enumeration {
+
+ /** the tokenizer object. */
+ protected StreamTokenizer st;
+
+ /** indicates if the current token contains changes. */
+ protected boolean changes = false;
+
+ /** indicates if the tokenizer has more tokens. */
+ protected boolean more = false;
+
+ /**
+ * @param file
+ * @throws FileNotFoundException
+ */
+ public CvsLogParser(String file) throws FileNotFoundException {
+ BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
+ st = new StreamTokenizer(r);
+ st.eolIsSignificant(true);
+ st.ordinaryChar('/');
+ st.ordinaryChar('\'');
+ more = true;
+ }
+
+ /**
+ * @see java.util.Enumeration#hasMoreElements()
+ */
+ public boolean hasMoreElements() {
+ return more;
+ }
+
+ /**
+ * Returns the next token in the log file.
+ * @see java.util.Enumeration#nextElement()
+ */
+ public Object nextElement(){
+ StringBuffer token = new StringBuffer();
+ StringBuffer line = new StringBuffer();
+ boolean moreToken = true;
+ changes = false;
+ try {
+ while (more && moreToken) {
+ st.nextToken();
+ switch(st.ttype) {
+ case StreamTokenizer.TT_EOF:
+ more = false;
+ case StreamTokenizer.TT_EOL:
+ token.append(line.toString());
+ if (line.toString().endsWith("=============================================================================")) {
+ moreToken = false;
+ }
+ else {
+ line = new StringBuffer("\n");
+ }
+ break;
+ case StreamTokenizer.TT_WORD:
+ line.append(st.sval);
+ line.append(" ");
+ break;
+ case StreamTokenizer.TT_NUMBER:
+ if (st.nval > 0 && line.toString().endsWith("selected revisions :")) {
+ changes = true;
+ }
+ line.append(st.nval);
+ break;
+ default:
+ line.append((char) st.ttype);
+ }
+ }
+ return token.toString();
+ }
+ catch(IOException ioe) {
+ more = false;
+ return "";
+ }
+ }
+
+ /**
+ * Indicates if the current token is one that contains changes.
+ * @return true if the token is relevant
+ */
+ private boolean hasChanged() {
+ return changes;
+ }
+
+
+ /**
+ * Parses a log form CVS.
+ * @param args the path to the logfile
+ */
+ public static void main(String[] args) {
+ try {
+ CvsLogParser p = new CvsLogParser(args[0]);
+ String token;
+ while (p.hasMoreElements()) {
+ token = (String) p.nextElement();
+ if (p.hasChanged()) {
+ System.out.println(token);
+ }
+ }
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/Executable.java b/src/main/java/com/lowagie/tools/Executable.java
new file mode 100644
index 0000000..0467b24
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/Executable.java
@@ -0,0 +1,322 @@
+/*
+ * $Id: Executable.java,v 1.6 2005/08/10 14:32:51 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie / Roger Mistelli
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+/**
+ * This class enables you to call an executable that will show a PDF file.
+ */
+public class Executable {
+
+ /**
+ * The path to Acrobat Reader.
+ */
+ public static String acroread = null;
+
+
+ /**
+ * Performs an action on a PDF document.
+ * @param fileName
+ * @param parameters
+ * @param waitForTermination
+ * @return a process
+ * @throws IOException
+ */
+ private static Process action(final String fileName,
+ String parameters, boolean waitForTermination) throws IOException {
+ Process process = null;
+ if (parameters.trim().length() > 0) {
+ parameters = " " + parameters.trim();
+ }
+ else {
+ parameters = "";
+ }
+ if (acroread != null) {
+ process = Runtime.getRuntime().exec(
+ acroread + parameters + " \"" + fileName + "\"");
+ }
+ else if (isWindows()) {
+ if (isWindows9X()) {
+ process = Runtime.getRuntime().exec(
+ "command.com /C start acrord32" + parameters + " \"" + fileName + "\"");
+ }
+ else {
+ process = Runtime.getRuntime().exec(
+ "cmd /c start acrord32" + parameters + " \"" + fileName + "\"");
+ }
+ }
+ else if (isMac()) {
+ if (parameters.trim().length() == 0) {
+ process = Runtime.getRuntime().exec(
+ new String[] { "/usr/bin/open", fileName });
+ }
+ else {
+ process = Runtime.getRuntime().exec(
+ new String[] { "/usr/bin/open", parameters.trim(), fileName });
+ }
+ }
+ try {
+ if (process != null && waitForTermination)
+ process.waitFor();
+ } catch (InterruptedException ie) {
+ }
+ return process;
+ }
+
+ /**
+ * Opens a PDF document.
+ * @param fileName
+ * @param waitForTermination
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process openDocument(String fileName,
+ boolean waitForTermination) throws IOException {
+ return action(fileName, "", waitForTermination);
+ }
+
+ /**
+ * Opens a PDF document.
+ * @param file
+ * @param waitForTermination
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process openDocument(File file,
+ boolean waitForTermination) throws IOException {
+ return openDocument(file.getAbsolutePath(), waitForTermination);
+ }
+
+ /**
+ * Opens a PDF document.
+ * @param fileName
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process openDocument(String fileName) throws IOException {
+ return openDocument(fileName, false);
+ }
+
+ /**
+ * Opens a PDF document.
+ * @param file
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process openDocument(File file) throws IOException {
+ return openDocument(file, false);
+ }
+
+ /**
+ * Prints a PDF document.
+ * @param fileName
+ * @param waitForTermination
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process printDocument(String fileName,
+ boolean waitForTermination) throws IOException {
+ return action(fileName, "/p", waitForTermination);
+ }
+
+ /**
+ * Prints a PDF document.
+ * @param file
+ * @param waitForTermination
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process printDocument(File file,
+ boolean waitForTermination) throws IOException {
+ return printDocument(file.getAbsolutePath(), waitForTermination);
+ }
+
+ /**
+ * Prints a PDF document.
+ * @param fileName
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process printDocument(String fileName) throws IOException {
+ return printDocument(fileName, false);
+ }
+
+ /**
+ * Prints a PDF document.
+ * @param file
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process printDocument(File file) throws IOException {
+ return printDocument(file, false);
+ }
+
+ /**
+ * Prints a PDF document without opening a Dialog box.
+ * @param fileName
+ * @param waitForTermination
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process printDocumentSilent(String fileName,
+ boolean waitForTermination) throws IOException {
+ return action(fileName, "/p /h", waitForTermination);
+ }
+
+ /**
+ * Prints a PDF document without opening a Dialog box.
+ * @param file
+ * @param waitForTermination
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process printDocumentSilent(File file,
+ boolean waitForTermination) throws IOException {
+ return printDocumentSilent(file.getAbsolutePath(), waitForTermination);
+ }
+
+ /**
+ * Prints a PDF document without opening a Dialog box.
+ * @param fileName
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process printDocumentSilent(String fileName) throws IOException {
+ return printDocumentSilent(fileName, false);
+ }
+
+ /**
+ * Prints a PDF document without opening a Dialog box.
+ * @param file
+ * @return a process
+ * @throws IOException
+ */
+ public static final Process printDocumentSilent(File file) throws IOException {
+ return printDocumentSilent(file, false);
+ }
+
+ /**
+ * Launches a browser opening an URL.
+ * This code was based on the Public Domain class BareBonesBrowserLaunch,
+ * found at Centerkey.
+ *
+ * @param url the URL you want to open in the browser
+ * @throws IOException
+ */
+ public static final void launchBrowser(String url) throws IOException {
+ try {
+ if (isMac()) {
+ Class macUtils = Class.forName("com.apple.mrj.MRJFileUtils");
+ Method openURL = macUtils.getDeclaredMethod("openURL", new Class[] {String.class});
+ openURL.invoke(null, new Object[] {url});
+ }
+ else if (isWindows())
+ Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
+ else { //assume Unix or Linux
+ String[] browsers = {
+ "firefox", "opera", "konqueror", "mozilla", "netscape" };
+ String browser = null;
+ for (int count = 0; count < browsers.length && browser == null; count++)
+ if (Runtime.getRuntime().exec(new String[] {"which", browsers[count]}).waitFor() == 0)
+ browser = browsers[count];
+ if (browser == null)
+ throw new Exception("Could not find web browser.");
+ else
+ Runtime.getRuntime().exec(new String[] {browser, url});
+ }
+ }
+ catch (Exception e) {
+ throw new IOException("Error attempting to launch web browser");
+ }
+ }
+
+ /**
+ * Checks the Operating System.
+ *
+ * @return true if the current os is Windows
+ */
+ public static boolean isWindows() {
+ String os = System.getProperty("os.name").toLowerCase();
+ return os.indexOf("windows") != -1 || os.indexOf("nt") != -1;
+ }
+
+ /**
+ * Checks the Operating System.
+ *
+ * @return true if the current os is Windows
+ */
+ public static boolean isWindows9X() {
+ String os = System.getProperty("os.name").toLowerCase();
+ return os.equals("windows 95") || os.equals("windows 98");
+ }
+
+ /**
+ * Checks the Operating System.
+ *
+ * @return true if the current os is Apple
+ */
+ public static boolean isMac() {
+ String os = System.getProperty("os.name").toLowerCase();
+ return os.indexOf("mac") != -1;
+ }
+
+ /**
+ * Checks the Operating System.
+ *
+ * @return true if the current os is Linux
+ */
+ public static boolean isLinux() {
+ String os = System.getProperty("os.name").toLowerCase();
+ return os.indexOf("linux") != -1;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/lowagie/tools/LPR.java b/src/main/java/com/lowagie/tools/LPR.java
new file mode 100644
index 0000000..f969f8a
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/LPR.java
@@ -0,0 +1,716 @@
+/*
+ * Class based on freeware by Mario Muller (http://www.hemasoft.de/dev/lprj/ )
+ * posted by Anonymous. */
+package com.lowagie.tools;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ * Modified!
+ *
+ * This class provides methods for the Line Printer Daemon Protocol
+ * more info about LPD/LPR
+ *
+ * @author Mario Müller
+ * @version 1.0 (12/98)
+ */
+public class LPR {
+ public static final int UNKNOWN = 0;
+
+ private String host;
+
+ private int port = 515;
+
+ private String user = System.getProperty("user.name");
+
+ private Vector jobs = new Vector();
+
+ private String hostname = null;
+
+ private String jobname = "";
+
+ private String cfAlen;
+
+ private String cfA;
+
+ private int copies = 1;
+
+ private int timeout = 60000;
+
+ private boolean cfA_formatted = false; // f
+
+ private boolean cfA_postscript = false; // o
+
+ private boolean cfA_banner = false; // L
+
+ private boolean cfA_pr = false;
+
+ /**
+ * default constructor without parameters, standard port is 515
+ */
+ public LPR() {
+ try {
+ hostname = InetAddress.getLocalHost().getHostName();
+ } catch (Exception e2) {
+ System.out.println("can't resolve hostname");
+ hostname = null;
+ }
+ }
+
+ /**
+ * constuctor with host and username standard port is 515
+ */
+ public LPR(String host, String user) {
+ this();
+ setHost(host);
+ setUser(user);
+ }
+
+ /**
+ * constuctor with host, port and username
+ */
+ public LPR(String host, int port, String user) {
+ this();
+ setHost(host);
+ setUser(user);
+ setPort(port);
+ }
+
+ /**
+ * set LPD host
+ */
+ public void setHost(String value) {
+ host = value;
+ }
+
+ /**
+ * get LPD host
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * set LPD port
+ */
+ public void setPort(int value) {
+ port = value;
+ }
+
+ /**
+ * get LPD port
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * set username
+ */
+ public void setUser(String value) {
+ user = value;
+ }
+
+ /**
+ * get username
+ */
+ public String getUser() {
+ return user;
+ }
+
+ /**
+ * set the timeout for any commands
+ */
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+
+ /**
+ * get the timeout for any commands
+ */
+ public int getTimeout() {
+ return timeout;
+ }
+
+ /**
+ * get if file printed as postscript file
+ */
+ public boolean getCfA_postscript() {
+ return cfA_postscript;
+ }
+
+ /**
+ * set if file printed as postscript file
+ * get
method.
+ */
+ public abstract Object construct();
+
+ /**
+ * Called on the event dispatching thread (not on the worker thread)
+ * after the construct
method has returned.
+ */
+ public void finished() {
+ }
+
+ /**
+ * A new method that interrupts the worker thread. Call this method
+ * to force the worker to stop what it's doing.
+ */
+ public void interrupt() {
+ Thread t = threadVar.get();
+ if (t != null) {
+ t.interrupt();
+ }
+ threadVar.clear();
+ }
+
+ /**
+ * Return the value created by the construct
method.
+ * Returns null if either the constructing thread or the current
+ * thread was interrupted before a value was produced.
+ *
+ * @return the value created by the construct
method
+ */
+ public Object get() {
+ while (true) {
+ Thread t = threadVar.get();
+ if (t == null) {
+ return getValueX();
+ }
+ try {
+ t.join();
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt(); // propagate
+ return null;
+ }
+ }
+ }
+
+
+ /**
+ * Start a thread that will call the construct
method
+ * and then exit.
+ */
+ public SwingWorker() {
+ final Runnable doFinished = new Runnable() {
+ public void run() { finished(); }
+ };
+
+ Runnable doConstruct = new Runnable() {
+ public void run() {
+ try {
+ setValueX(construct());
+ }
+ finally {
+ threadVar.clear();
+ }
+
+ SwingUtilities.invokeLater(doFinished);
+ }
+ };
+
+ Thread t = new Thread(doConstruct);
+ threadVar = new ThreadVar(t);
+ }
+
+ /**
+ * Start the worker thread.
+ */
+ public void start() {
+ Thread t = threadVar.get();
+ if (t != null) {
+ t.start();
+ }
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/ToolMenuItems.java b/src/main/java/com/lowagie/tools/ToolMenuItems.java
new file mode 100644
index 0000000..fdcf916
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/ToolMenuItems.java
@@ -0,0 +1,84 @@
+/*
+ * $Id: ToolMenuItems.java,v 1.4 2005/08/29 08:44:16 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools;
+
+/**
+ * Keeps all the possible menuitems.
+ */
+public interface ToolMenuItems {
+
+ /** An item in the menubar. */
+ public static final String FILE = "File";
+ /** An item in the menubar. */
+ public static final String CLOSE = "Close";
+ /** An item in the menubar. */
+ public static final String TOOLS = "Tools";
+ /** An item in the menubar. */
+ public static final String HELP = "Help";
+ /** An item in the menubar. */
+ public static final String VERSION = "Version";
+ /** An item in the menubar. */
+ public static final String ABOUT = "About";
+ /** An item in the menubar. */
+ public static final String TOOL = "Tool";
+ /** An item in the menubar. */
+ public static final String USAGE = "Usage";
+ /** An item in the menubar. */
+ public static final String ARGUMENTS = "Arguments";
+ /** An item in the menubar. */
+ public static final String EXECUTE = "Execute";
+ /** An item in the menubar. */
+ public static final String EXECUTESHOW = "Execute+Open";
+ /** An item in the menubar. */
+ public static final String EXECUTEPRINT = "Execute+Printdialog";
+ /** An item in the menubar. */
+ public static final String EXECUTEPRINTSILENT = "Execute+Print";
+
+}
diff --git a/src/main/java/com/lowagie/tools/Toolbox.java b/src/main/java/com/lowagie/tools/Toolbox.java
new file mode 100644
index 0000000..89a0bc3
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/Toolbox.java
@@ -0,0 +1,354 @@
+/*
+ * $Id: Toolbox.java,v 1.16 2006/05/29 10:30:39 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Bruno Lowagie.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyVetoException;
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.TreeMap;
+
+import javax.swing.Box;
+import javax.swing.JDesktopPane;
+import javax.swing.JFrame;
+import javax.swing.JInternalFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTextArea;
+
+import com.lowagie.tools.plugins.AbstractTool;
+
+import java.awt.Toolkit;
+import java.awt.Dimension;
+
+/**
+ * This is a utility that allows you to use a number of iText tools.
+ */
+public class Toolbox extends JFrame implements ToolMenuItems, ActionListener {
+
+ /** The DesktopPane of the toolbox. */
+ private JDesktopPane desktop;
+ /** The ConsolePane of the toolbox. */
+ private JScrollPane console;
+
+ /** The list of tools in the toolbox. */
+ private Properties toolmap = new Properties();
+
+ /** x-coordinate of the location of a new internal frame. */
+ private int locationX = 0;
+
+ /** y-coordinate of the location of a new internal frame. */
+ private int locationY = 0;
+
+ /**
+ * Constructs the Toolbox object.
+ */
+ public Toolbox() {
+ super();
+ setSize(600, 500);
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setResizable(true);
+ setTitle("iText Toolbox");
+ setJMenuBar(getMenubar());
+ desktop = new JDesktopPane();
+ Console c;
+ try {
+ c = new Console();
+ console = new JScrollPane(c.textArea);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, desktop, console);
+ splitPane.setContinuousLayout(true);
+ splitPane.setOneTouchExpandable(true);
+ splitPane.setDividerLocation(300);
+ setContentPane(splitPane);
+ centerFrame(this);
+ setVisible(true);
+ }
+
+ /**
+ * Starts the Toolbox utility.
+ *
+ * @param args
+ * no arguments needed
+ */
+ public static void main(String[] args) {
+ Toolbox toolbox = new Toolbox();
+ if(args.length==0){
+
+ }else{
+ try {
+ AbstractTool tool = toolbox.createFrame(args[0]);
+ String[] nargs=new String[args.length-1];
+ System.arraycopy(args,1,nargs,0,args.length-1);
+ tool.setArguments(nargs);
+ tool.execute();
+ }
+ catch (PropertyVetoException ex) {
+ }
+ catch (ClassNotFoundException ex) {
+ }
+ catch (IllegalAccessException ex) {
+ }
+ catch (InstantiationException ex) {
+ }
+ }
+ }
+
+ /**
+ * Gets the menubar.
+ *
+ * @return a menubar
+ */
+ private JMenuBar getMenubar() {
+ Properties p = new Properties();
+ try {
+ p.load(Toolbox.class.getClassLoader().getResourceAsStream(
+ "com/lowagie/tools/plugins/tools.txt"));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ toolmap = new Properties();
+ TreeMap tmp = new TreeMap();
+ tmp.putAll(p);
+ JMenuBar menubar = new JMenuBar();
+ JMenu file = new JMenu(FILE);
+ JMenuItem close = new JMenuItem(CLOSE);
+ close.setMnemonic(KeyEvent.VK_C);
+ close.addActionListener(this);
+ file.add(close);
+ JMenu tools = new JMenu(TOOLS);
+ file.setMnemonic(KeyEvent.VK_T);
+ String name, tool;
+ JMenu current = null;
+ JMenuItem item;
+ for (Iterator i = tmp.keySet().iterator(); i.hasNext();) {
+ name = (String) i.next();
+ if (current == null || !name.startsWith(current.getText())) {
+ current = new JMenu(name.substring(0, name.indexOf(".")));
+ tools.add(current);
+ }
+ item = new JMenuItem(name.substring(current.getText().length() + 1));
+ item.addActionListener(this);
+ tool = (String) tmp.get(name);
+ try {
+ Class.forName(tool);
+ toolmap.put(item.getText(), tool);
+ current.add(item);
+ } catch (ClassNotFoundException e) {
+ System.err.println("Plugin " + name
+ + " was not found in your CLASSPATH.");
+ }
+ }
+ JMenu help = new JMenu(HELP);
+ JMenuItem about = new JMenuItem(ABOUT);
+ about.setMnemonic(KeyEvent.VK_A);
+ about.addActionListener(this);
+ help.add(about);
+ JMenuItem versions = new JMenuItem(VERSION);
+ versions.addActionListener(this);
+ help.add(versions);
+ menubar.add(file);
+ menubar.add(tools);
+ menubar.add(Box.createGlue());
+ menubar.add(help);
+ return menubar;
+ }
+
+ /**
+ * Creates an Internal Frame.
+ *
+ * @param name
+ * the name of the app
+ * @throws ClassNotFoundException
+ * @throws IllegalAccessException
+ * @throws InstantiationException
+ * @throws PropertyVetoException
+ */
+ public AbstractTool createFrame(String name) throws InstantiationException,
+ IllegalAccessException, ClassNotFoundException,
+ PropertyVetoException {
+ AbstractTool ti = (AbstractTool) Class.forName(
+ (String) toolmap.get(name)).newInstance();
+ JInternalFrame f = ti.getInternalFrame();
+ f.setLocation(locationX, locationY);
+ locationX += 25;
+ if (locationX > this.getWidth() + 50)
+ locationX = 0;
+ locationY += 25;
+ if (locationY > this.getHeight() + 50)
+ locationY = 0;
+ f.setVisible(true);
+ desktop.add(f);
+ f.setSelected(true);
+ return ti;
+ }
+
+ public static void centerFrame(JFrame f) {
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ Dimension frameSize = f.getSize();
+ if (frameSize.height > screenSize.height) {
+ frameSize.height = screenSize.height;
+ }
+ if (frameSize.width > screenSize.width) {
+ frameSize.width = screenSize.width;
+ }
+ f.setLocation((screenSize.width - frameSize.width) / 2,
+ (screenSize.height - frameSize.height) / 2);
+ }
+
+ /**
+ * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+ */
+ public void actionPerformed(ActionEvent evt) {
+ if (CLOSE.equals(evt.getActionCommand())) {
+ System.out.println("The Toolbox is closed.");
+ System.exit(0);
+ } else if (ABOUT.equals(evt.getActionCommand())) {
+ System.out
+ .println("The iText Toolbox is part of iText, a Free Java-PDF Library.\nVisit http://www.lowagie.com/iText/toolbox.html for more info.");
+ try {
+ Executable
+ .launchBrowser("http://www.lowagie.com/iText/toolbox.html");
+ } catch (IOException ioe) {
+ JOptionPane
+ .showMessageDialog(
+ this,
+ "The iText Toolbox is part of iText, a Free Java-PDF Library.\nVisit http://www.lowagie.com/iText/toolbox.html for more info.");
+ }
+ } else if (VERSION.equals(evt.getActionCommand())) {
+ JFrame f = new Versions();
+ centerFrame(f);
+ f.setVisible(true);
+ } else {
+ try {
+ createFrame(evt.getActionCommand());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ /**
+ * A Class that redirects output to System.out and System.err.
+ */
+ public class Console {
+ PipedInputStream piOut;
+ PipedInputStream piErr;
+ PipedOutputStream poOut;
+ PipedOutputStream poErr;
+ JTextArea textArea = new JTextArea();
+
+ /**
+ * Creates a new Console object.
+ * @param columns
+ * @param rows
+ * @throws IOException
+ */
+ public Console() throws IOException {
+ // Set up System.out
+ piOut = new PipedInputStream();
+ poOut = new PipedOutputStream(piOut);
+ System.setOut(new PrintStream(poOut, true));
+
+ // Set up System.err
+ piErr = new PipedInputStream();
+ poErr = new PipedOutputStream(piErr);
+ System.setErr(new PrintStream(poErr, true));
+
+ // Add a scrolling text area
+ textArea.setEditable(false);
+
+ // Create reader threads
+ new ReaderThread(piOut).start();
+ new ReaderThread(piErr).start();
+ }
+
+ class ReaderThread extends Thread {
+ PipedInputStream pi;
+
+ ReaderThread(PipedInputStream pi) {
+ this.pi = pi;
+ }
+
+ /**
+ * @see java.lang.Thread#run()
+ */
+ public void run() {
+ final byte[] buf = new byte[1024];
+
+ while (true) {
+ try {
+ final int len = pi.read(buf);
+ if (len == -1) {
+ break;
+ }
+ textArea.append(new String(buf, 0, len));
+ textArea.setCaretPosition(textArea.getDocument().getLength());
+
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/lowagie/tools/Versions.java b/src/main/java/com/lowagie/tools/Versions.java
new file mode 100644
index 0000000..dd0796f
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/Versions.java
@@ -0,0 +1,143 @@
+/*
+ * $Id: Versions.java,v 1.7 2006/02/17 11:54:42 psoares33 Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Carsten Hammer.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools;
+
+import java.awt.*;
+import javax.swing.*;
+import java.awt.BorderLayout;
+import com.lowagie.text.Document;
+import com.lowagie.tools.plugins.*;
+import java.util.Iterator;
+import java.util.TreeSet;
+import java.awt.Dimension;
+import java.util.Properties;
+
+/**
+ * JFrame that shows the versions of all the plugins.
+ */
+public class Versions extends JFrame {
+ /**
+ * Constructs a JFrame.
+ */
+ public Versions() {
+ super("Plugins and their version");
+ try {
+ jbInit();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Main method (test purposes only)
+ *
+ * @param args
+ */
+ public static void main(String[] args) {
+ Versions untitled1 = new Versions();
+ }
+
+ private void jbInit() throws Exception {
+ this.getContentPane().setLayout(borderLayout1);
+ StringBuffer sb = new StringBuffer();
+ sb.append("");
+
+ Iterator it = new TreeSet(AbstractTool.versionsarray).iterator();
+
+ while (it.hasNext()) {
+ sb.append("rowIndex
.
+ *
+ * @param rowIndex the row whose value is to be queried
+ * @param columnIndex the column whose value is to be queried
+ * @return the value Object at the specified cell
+ * @todo Implement this javax.swing.table.TableModel method
+ */
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ Rectangle rec = reader.getPageSizeWithRotation(rowIndex + 1);
+ switch (columnIndex) {
+ case 0:
+ return myFormatter.format(rowIndex + 1);
+ case 1:
+ return new Float(rec.width());
+ case 2:
+ return new Float(rec.height());
+ case 3:
+ return new Float(rec.getRotation());
+ }
+ return null;
+ }
+
+ public String getColumnName(int column) {
+ String name = new Integer(column + 1).toString();
+ switch (column) {
+ case 0:
+ name = "PagenrcolumnIndex
.
+ *
+ * @param columnIndex the index of the column
+ * @return the name of the column
+ * @todo Implement this javax.swing.table.TableModel method
+ */
+ public String getColumnName(int columnIndex) {
+ switch (columnIndex) {
+ case 0:
+ return "XRefNr";
+ case 1:
+ return "Object";
+ default:
+ return null;
+ }
+
+ }
+ };
+ return tm;
+ }
+
+ /**
+ * Walk down the Pagetree
+ * @param page PdfDictionary
+ * @param pdfreader PdfReader
+ * @param count_in_leaf int
+ * @param node DefaultMutableTreeNode
+ */
+ protected void iteratePages(PdfDictionary page, PdfReader pdfreader,
+ DefaultMutableTreeNode node) {
+ DefaultMutableTreeNode leaf;
+
+ PdfArray kidsPR = (PdfArray) PdfReader.getPdfObject(page.get(PdfName.KIDS));
+ if (kidsPR == null) {
+ node.add(new Pagetreenode(page, pagecount, this, pdfreader));
+ System.out.println("Page= " + (pagecount + 1));
+ pageInh.add(pagecount, page);
+ pagecount++;
+ }
+ else {
+ leaf = new PagelistTreeNode(kidsPR);
+ node.add(leaf);
+ page.put(PdfName.TYPE, PdfName.PAGES);
+ ArrayList kids = kidsPR.getArrayList();
+ for (int k = 0; k < kids.size(); ++k) {
+ PdfDictionary kid = (PdfDictionary) PdfReader.getPdfObject( (
+ PRIndirectReference) kids.get(k));
+ iteratePages(kid, pdfreader, leaf);
+ }
+ }
+ }
+
+ protected void iterateOutlines(PdfDictionary outlines, PdfReader pdfreader,
+ DefaultMutableTreeNode node) {
+ DefaultMutableTreeNode leaf;
+ PdfDictionary kid = outlines;
+ do {
+ PdfString title = (PdfString) pdfreader.getPdfObject(
+ kid.get(PdfName.TITLE));
+ leaf = new OutlinelistTreeNode(title, kid);
+ node.add(leaf);
+ PdfDictionary first = (PdfDictionary) PdfReader.getPdfObject( (
+ PRIndirectReference) kid.get(PdfName.FIRST));
+ if (first != null) {
+ iterateOutlines(first, pdfreader, leaf);
+ }
+ else {
+ PdfDictionary se = (PdfDictionary) PdfReader.getPdfObject( (
+ PRIndirectReference) kid.get(new PdfName("SE")));
+ if (se != null) {
+ iterateObjects(se, pdfreader, leaf);
+ }
+ PdfObject dest = (PdfObject) pdfreader.getPdfObject(kid.get(PdfName.
+ DEST));
+ if (dest != null) {
+ iterateObjects(dest, pdfreader, leaf);
+ }
+ PdfObject a = (PdfObject) pdfreader.getPdfObject(kid.get(PdfName.A));
+ if (a != null) {
+ iterateObjects(a, pdfreader, leaf);
+ }
+ }
+ }
+ while ( (kid = (PdfDictionary) pdfreader.getPdfObject(kid.get(PdfName.NEXT))) != null);
+ }
+
+ /**
+ * Recursive investigate PDF Objecttree (other than pagetree objects!)
+ * @param pdfobj PdfObject
+ * @param pdfreader PdfReader
+ * @param node DefaultMutableTreeNode
+ */
+ public void iterateObjects(PdfObject pdfobj, PdfReader pdfreader,
+ DefaultMutableTreeNode node) {
+ DefaultMutableTreeNode leaf;
+ if (pdfobj.isDictionary()) {
+ leaf = new DictionaryTreeNode("PdfDictionary " + pdfobj,
+ (PdfDictionary) pdfobj);
+ node.add(leaf);
+ Set s = ( (PdfDictionary) pdfobj).getKeys();
+ Iterator it = s.iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ i++;
+ Object obj = it.next();
+// System.out.println("Feld:" + obj);
+
+ PdfObject value = PdfReader.getPdfObject( ( (PdfDictionary) pdfobj).get( (
+ PdfName) obj));
+// System.out.println("Value:" + value);
+ SimpletextTreeNode sttn = new SimpletextTreeNode(obj + " " + value);
+ leaf.add(sttn);
+ if (obj.equals(PdfName.PARENT)) {
+ continue;
+ }
+ if (value != null) {
+ iterateObjects(value, pdfreader, sttn);
+ }
+ }
+ }
+ else if (pdfobj.isArray()) {
+ leaf = new ArrayTreeNode("PdfArray " + pdfobj, (PdfArray) pdfobj);
+ node.add(leaf);
+ ArrayList kids = ( (PdfArray) pdfobj).getArrayList();
+ for (int k = 0; k < kids.size(); ++k) {
+ PdfObject curkid = (PdfObject) kids.get(k);
+ if (curkid.isIndirect()) {
+ PdfObject kid = PdfReader.getPdfObject( (
+ PRIndirectReference) kids.get(k));
+ if (kid != null) {
+ iterateObjects(kid, pdfreader, leaf);
+ }
+ }
+ else if (curkid.isNumber()) {
+
+ }
+ else {
+ PdfObject kid = (PdfObject) kids.get(k);
+ iterateObjects(kid, pdfreader, leaf);
+ }
+ }
+ }
+ else if (pdfobj.isIndirect()) {
+ leaf = new SimpletextTreeNode("PRIndirectReference " + pdfobj);
+ node.add(leaf);
+ PdfObject target = PdfReader.getPdfObject( (
+ PRIndirectReference) pdfobj);
+ if (target != null) {
+ iterateObjects(target, pdfreader, leaf);
+ }
+ }
+ else if (pdfobj.isBoolean()) {
+// leaf = new SimpletextTreeNode("Boolean " + pdfobj);
+// node.add(leaf);
+ }
+ else if (pdfobj.isName()) {
+// leaf = new SimpletextTreeNode("Name " + pdfobj);
+// node.add(leaf);
+ }
+ else if (pdfobj.isNull()) {
+// leaf = new SimpletextTreeNode("Null " + pdfobj);
+// node.add(leaf);
+ }
+ else if (pdfobj.isNumber()) {
+// leaf = new SimpletextTreeNode("Number " + pdfobj);
+// node.add(leaf);
+ }
+ else if (pdfobj.isString()) {
+// leaf = new SimpletextTreeNode("String " + pdfobj);
+// node.add(leaf);
+ }
+ else if (pdfobj.isStream()) {
+ leaf = new TextpaneTreeNode(pdfobj,"Stream");
+ node.add(leaf);
+ leaf = new DictionaryTreeNode("PdfDictionary " + pdfobj,
+ (PdfDictionary) pdfobj);
+ node.add(leaf);
+ Set s = ( (PdfDictionary) pdfobj).getKeys();
+ Iterator it = s.iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ i++;
+ Object obj = it.next();
+// System.out.println("Feld:" + obj);
+
+ PdfObject value = PdfReader.getPdfObject( ( (PdfDictionary) pdfobj).get( (
+ PdfName) obj));
+// System.out.println("Value:" + value);
+ SimpletextTreeNode sttn = new SimpletextTreeNode(obj + " " + value);
+ leaf.add(sttn);
+ if (obj.equals(PdfName.PARENT)) {
+ continue;
+ }
+ if (value != null) {
+ iterateObjects(value, pdfreader, sttn);
+ }
+ }
+
+ }
+
+ else {
+ leaf = new SimpletextTreeNode("Unknown " + pdfobj);
+ node.add(leaf);
+ }
+
+ }
+
+ /**
+ * Returns the root of the tree.
+ *
+ * @return the root of the tree
+ * @todo Diese javax.swing.tree.TreeModel-Methode implementieren
+ */
+ public Object getRoot() {
+ return root;
+ }
+
+ /**
+ * Returns the child of parent
at index index
in the
+ * parent's child array.
+ *
+ * @param parent a node in the tree, obtained from this data source
+ * @param index int
+ * @return the child of parent
at index index
+ * @todo Diese javax.swing.tree.TreeModel-Methode implementieren
+ */
+ public Object getChild(Object parent, int index) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) parent;
+ return node.getChildAt(index);
+ }
+
+ /**
+ * Returns the number of children of parent
.
+ *
+ * @param parent a node in the tree, obtained from this data source
+ * @return the number of children of the node parent
+ * @todo Diese javax.swing.tree.TreeModel-Methode implementieren
+ */
+ public int getChildCount(Object parent) {
+ DefaultMutableTreeNode node = (DefaultMutableTreeNode) parent;
+ return node.getChildCount();
+ }
+
+ /**
+ * Returns true
if node
is a leaf.
+ *
+ * @param node a node in the tree, obtained from this data source
+ * @return true if node
is a leaf
+ * @todo Diese javax.swing.tree.TreeModel-Methode implementieren
+ */
+ public boolean isLeaf(Object node) {
+ DefaultMutableTreeNode leaf = (DefaultMutableTreeNode) node;
+ return leaf.isLeaf();
+ }
+
+ /**
+ * Messaged when the user has altered the value for the item identified by
+ * path
to newValue
.
+ *
+ * @param path path to the node that the user has altered
+ * @param newValue the new value from the TreeCellEditor
+ * @todo Diese javax.swing.tree.TreeModel-Methode implementieren
+ */
+ public void valueForPathChanged(TreePath path, Object newValue) {
+ throw new RuntimeException("Manipulation of objecttree not yet supported!");
+ }
+
+ /**
+ * Returns the index of child in parent.
+ *
+ * @param parent a note in the tree, obtained from this data source
+ * @param child the node we are interested in
+ * @return the index of the child in the parent, or -1 if either
+ * child
or parent
are null
+ * @todo Diese javax.swing.tree.TreeModel-Methode implementieren
+ */
+ public int getIndexOfChild(Object parent, Object child) {
+ DefaultMutableTreeNode parentobj = (DefaultMutableTreeNode) parent;
+ DefaultMutableTreeNode childobj = (DefaultMutableTreeNode) child;
+ return parentobj.getIndex(childobj);
+ }
+
+ public synchronized void removeTreeModelListener(TreeModelListener l) {
+ if (treeModelListeners != null && treeModelListeners.contains(l)) {
+ Vector v = (Vector) treeModelListeners.clone();
+ v.removeElement(l);
+ treeModelListeners = v;
+ }
+ }
+
+ public synchronized void addTreeModelListener(TreeModelListener l) {
+ Vector v = treeModelListeners == null ? new Vector(2) :
+ (Vector) treeModelListeners.clone();
+ if (!v.contains(l)) {
+ v.addElement(l);
+ treeModelListeners = v;
+ }
+ }
+
+ protected void fireTreeNodesChanged(TreeModelEvent e) {
+ if (treeModelListeners != null) {
+ Vector listeners = treeModelListeners;
+ int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ ( (TreeModelListener) listeners.elementAt(i)).treeNodesChanged(e);
+ }
+ }
+ }
+
+ protected void fireTreeNodesInserted(TreeModelEvent e) {
+ if (treeModelListeners != null) {
+ Vector listeners = treeModelListeners;
+ int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ ( (TreeModelListener) listeners.elementAt(i)).treeNodesInserted(e);
+ }
+ }
+ }
+
+ protected void fireTreeNodesRemoved(TreeModelEvent e) {
+ if (treeModelListeners != null) {
+ Vector listeners = treeModelListeners;
+ int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ ( (TreeModelListener) listeners.elementAt(i)).treeNodesRemoved(e);
+ }
+ }
+ }
+
+ protected void fireTreeStructureChanged(TreeModelEvent e) {
+ if (treeModelListeners != null) {
+ Vector listeners = treeModelListeners;
+ int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ ( (TreeModelListener) listeners.elementAt(i)).treeStructureChanged(e);
+ }
+ }
+ }
+
+ /**
+ * When an object implementing interface Runnable
is used to
+ * create a thread, starting the thread causes the object's run
+ * method to be called in that separately executing thread.
+ *
+ * @todo Diese java.lang.Runnable-Methode implementieren
+ */
+ public void run() {
+ try {
+ PdfDictionary catalog = reader.getCatalog();
+ PdfDictionary rootPages = (PdfDictionary) PdfReader.getPdfObject(
+ catalog.get(PdfName.PAGES));
+ DefaultMutableTreeNode rootPagesGUI = new SimpletextTreeNode("Pagetree " +
+ rootPages);
+ filenode.add(rootPagesGUI);
+ iteratePages(rootPages, reader, rootPagesGUI);
+
+ PdfDictionary rootOutlines = (PdfDictionary) PdfReader.getPdfObject(
+ catalog.get(PdfName.OUTLINES));
+ if (rootOutlines != null) {
+ DefaultMutableTreeNode outlinetree = new SimpletextTreeNode(
+ "Outlinetree " + rootOutlines);
+ filenode.add(outlinetree);
+ PdfObject firstindref = rootOutlines.get(PdfName.FIRST);
+ if (firstindref != null) {
+ PdfDictionary first = (PdfDictionary) PdfReader.getPdfObject( (
+ PRIndirectReference) firstindref);
+ if (first != null) {
+ iterateOutlines(first, reader, outlinetree);
+ }
+ }
+ }
+ System.out.println(" Pagecount= " + pagecount);
+ progressdialog.setVisible(false);
+ }
+ catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ public int getPagecount() {
+ return pagecount;
+ }
+
+ public void updatecount() {
+ progressdialog.setAktuelleseite(getPagecount());
+ }
+}
diff --git a/src/main/java/com/lowagie/tools/plugins/treeview/ArrayTreeNode.java b/src/main/java/com/lowagie/tools/plugins/treeview/ArrayTreeNode.java
new file mode 100644
index 0000000..07ec49c
--- /dev/null
+++ b/src/main/java/com/lowagie/tools/plugins/treeview/ArrayTreeNode.java
@@ -0,0 +1,107 @@
+/*
+ * $Id: ArrayTreeNode.java,v 1.2 2006/05/30 09:13:36 blowagie Exp $
+ * $Name: $
+ *
+ * Copyright 2005 by Anonymous.
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * (the "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the License.
+ *
+ * The Original Code is 'iText, a free JAVA-PDF library'.
+ *
+ * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
+ * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
+ * All Rights Reserved.
+ * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
+ * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
+ *
+ * Contributor(s): all the names of the contributors are added in the source code
+ * where applicable.
+ *
+ * Alternatively, the contents of this file may be used under the terms of the
+ * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
+ * provisions of LGPL are applicable instead of those above. If you wish to
+ * allow use of your version of this file only under the terms of the LGPL
+ * License and not to allow others to use your version of this file under
+ * the MPL, indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by the LGPL.
+ * If you do not delete the provisions above, a recipient may use your version
+ * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the MPL as stated above or under the terms of the GNU
+ * Library General Public License as published by the Free Software Foundation;
+ * either version 2 of the License, or 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 Library general Public License for more
+ * details.
+ *
+ * If you didn't download this code from the following link, you should check if
+ * you aren't using an obsolete version:
+ * http://www.lowagie.com/iText/
+ */
+package com.lowagie.tools.plugins.treeview;
+
+import com.lowagie.text.pdf.PdfArray;
+import java.util.ArrayList;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+
+/**
+ *