aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/knowcenter/wag/egov/egiz/pdf
diff options
context:
space:
mode:
authortknall <tknall@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2006-12-01 12:20:24 +0000
committertknall <tknall@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2006-12-01 12:20:24 +0000
commit6025b6016517c6d898d8957d1d7e03ba71431912 (patch)
treeb15bd6fa5ffe9588a9bca3f2b8a7e358f83b6eba /src/main/java/at/knowcenter/wag/egov/egiz/pdf
parentd2c77e820ab4aba8235d71275755021347b3ad10 (diff)
downloadpdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.tar.gz
pdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.tar.bz2
pdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.zip
Initial import of release 2.2.REL-2.2@923
git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@4 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c
Diffstat (limited to 'src/main/java/at/knowcenter/wag/egov/egiz/pdf')
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/AbsoluteTextSignature.java656
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinaryBlockInfo.java53
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java1731
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignatureHolder.java146
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/EGIZDate.java189
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/IncrementalUpdateInformation.java152
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFPage.java539
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureCreation.java170
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObject.java50
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObjectIText.java490
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFUtilities.java89
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/Placeholder.java552
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/Pos.java62
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/ReplaceInfo.java64
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/SignatureHolder.java62
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/SplitStrings.java162
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/StringInfo.java93
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/TablePos.java56
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/TextualSignature.java177
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/TextualSignatureHolder.java73
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/Utils.java96
21 files changed, 5662 insertions, 0 deletions
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/AbsoluteTextSignature.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/AbsoluteTextSignature.java
new file mode 100644
index 0000000..5523041
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/AbsoluteTextSignature.java
@@ -0,0 +1,656 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: AbsoluteTextSignature.java,v 1.1 2006/10/31 08:08:33 wprinz Exp $
+ */package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Vector;
+
+import org.apache.log4j.Logger;
+
+import at.knowcenter.wag.egov.egiz.PdfAS;
+import at.knowcenter.wag.egov.egiz.cfg.ConfigLogger;
+import at.knowcenter.wag.egov.egiz.exceptions.SignatureException;
+import at.knowcenter.wag.egov.egiz.exceptions.SignatureTypesException;
+import at.knowcenter.wag.egov.egiz.framework.FoundBlock;
+import at.knowcenter.wag.egov.egiz.framework.FoundKey;
+import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
+import at.knowcenter.wag.egov.egiz.sig.SignatureTypeDefinition;
+import at.knowcenter.wag.egov.egiz.sig.SignatureTypes;
+
+/**
+ * Contains methods and helpers that implement the absolute text signature.
+ * @author wprinz
+ */
+public class AbsoluteTextSignature
+{
+
+ /**
+ * The logger definition.
+ */
+ private static final Logger logger = ConfigLogger.getLogger(AbsoluteTextSignature.class);
+
+
+ /**
+ * Extracts all signature holders from a given text.
+ *
+ * <p>
+ * First the latest signature holder is extracted. Then the latest signature
+ * holder in the rest text, which is the second latest one, is extracted. Then
+ * the third latest signature holder is extracted and so forth until no more
+ * signature holders are found.
+ * </p>
+ *
+ * @param text
+ * The text.
+ * @return Returns the List of extracted signature holders ordered by their
+ * date ascendingly (the lowest, earliest date first, the latest,
+ * newest date last). An empty list is returned if no signature
+ * holders were found.
+ * @throws SignatureException
+ * F.e.
+ * @throws SignatureTypesException
+ * F.e.
+ */
+ public static List extractSignatureHoldersFromText(String text) throws SignatureException, SignatureTypesException
+ {
+ List holders = new ArrayList();
+
+ String current_text = text;
+ for (;;)
+ {
+ SignatureHolder signature_holder = extractLatestBlock(current_text);
+ if (signature_holder == null)
+ {
+ break;
+ }
+
+ holders.add(0, signature_holder);
+
+ current_text = signature_holder.getSignedText();
+ }
+
+ return holders;
+ }
+
+ /**
+ * Extracts the latest signature block from the given text and creates a
+ * SignatureHolder object that can be verified.
+ *
+ * @param text
+ * The text.
+ * @return Returns the SignatureObject extracted from the text, or null, if no
+ * latest block was found.
+ * @throws SignatureException
+ * F.e.
+ * @throws SignatureTypesException
+ * F.e.
+ */
+ public static SignatureHolder extractLatestBlock(String text) throws SignatureException, SignatureTypesException
+ {
+ FoundBlock latest_block = findLatestBlock(text);
+ if (latest_block == null)
+ {
+ return null;
+ }
+
+ String reconstructed_text = cutOutBlock(text, latest_block);
+
+ SignatureObject so = createSignatureObjectFromFoundBlock(text, latest_block);
+ TextualSignatureHolder tsh = new TextualSignatureHolder(reconstructed_text, so);
+
+ return tsh;
+ }
+
+ /**
+ * Finds the latest signature block for a given text.
+ *
+ * <p>
+ * The latest block is the one with the highest, most recent date. Usually
+ * this block will be extracted (cut out) of the text which will result in the
+ * originally signed text of this signature to be verified using the cut out
+ * data.
+ * </p>
+ *
+ * @param text
+ * The text to be analyzed.
+ * @return Returns the latest found block or null, if there was none.
+ * @throws SignatureException
+ * F.e.
+ * @throws SignatureTypesException
+ * F.e.
+ */
+ public static FoundBlock findLatestBlock(String text) throws SignatureException, SignatureTypesException
+ {
+// try
+// {
+// writeTextToFile(text, new File("C:\\wprinz\\text.utf8.txt"));
+// }
+// catch (IOException e)
+// {
+// e.printStackTrace();
+// }
+
+ SignatureTypes sig_types = SignatureTypes.getInstance();
+ List signatureTypes_ = sig_types.getSignatureTypeDefinitions();
+
+ List found_candidates = new ArrayList();
+
+ for (int i = 0; i < signatureTypes_.size(); i++)
+ {
+ SignatureTypeDefinition block_type = (SignatureTypeDefinition) signatureTypes_.get(i);
+ List found_candidates_for_type = findPotentialSignaturesForProfile(text, block_type);
+
+ found_candidates.addAll(found_candidates_for_type);
+ }
+
+ if (found_candidates.isEmpty())
+ {
+ logger.debug("no candidates found at all");
+ return null;
+ }
+
+ logger.debug("checking block integrity");
+ for (int i = 0; i < found_candidates.size(); i++)
+ {
+ FoundBlock found_block = (FoundBlock) found_candidates.get(i);
+
+ String date_value = getDateValue(text, found_block);
+ logger.debug("date_value = " + date_value);
+ EGIZDate date = EGIZDate.parseFromString(date_value);
+
+ logger.debug("found_block = " + date + " - " + found_block);
+
+ checkBlockIntegrity(text, found_block);
+ }
+
+ sortFoundBlocksByDate(text, found_candidates);
+
+ logger.debug("sorted blocks:");
+ for (int i = 0; i < found_candidates.size(); i++)
+ {
+ FoundBlock found_block = (FoundBlock) found_candidates.get(i);
+
+ String date_value = getDateValue(text, found_block);
+ EGIZDate date = EGIZDate.parseFromString(date_value);
+
+ logger.debug(" #" + i + ": " + date + " - " + found_block);
+ }
+
+ List latest_blocks = filterLastDateEqualBlocks(text, found_candidates);
+ logger.debug("latest blocks:");
+ for (int i = 0; i < latest_blocks.size(); i++)
+ {
+ FoundBlock found_block = (FoundBlock) latest_blocks.get(i);
+
+ String date_value = getDateValue(text, found_block);
+ EGIZDate date = EGIZDate.parseFromString(date_value);
+
+ logger.debug(" #" + i + ": " + date + " - " + found_block);
+ }
+
+ boolean semantic_equality = PdfAS.checkForSemanticEquality(latest_blocks);
+ logger.debug("semantic_equality = " + semantic_equality);
+ if (!semantic_equality)
+ {
+ throw new SignatureException(314, "The latest blocks weren't semantically equal.");
+ }
+
+ FoundBlock latest_block = (FoundBlock) latest_blocks.get(0);
+ logger.debug("latest block = " + latest_block);
+ return latest_block;
+ }
+
+ /**
+ * Finds the List of potential blocks within the given text for the given
+ * profile.
+ *
+ * @param text
+ * The text, in which potential block are to be sought.
+ * @param block_type
+ * The profile for which the text is to be sought.
+ * @return Returns the List of potential FoundBlocks or an empty List if none
+ * could be found.
+ */
+ public static List findPotentialSignaturesForProfile(String text,
+ SignatureTypeDefinition block_type)
+ {
+ logger.debug("find potential signatures for " + block_type.getType());
+
+ List found_blocks = new ArrayList();
+
+ final boolean old_style = false;
+
+ Vector keys = block_type.getRevertSortedKeys();
+ Vector captions = block_type.getRevertSortedCaptions();
+
+ String last_key = (String) keys.get(0);
+ logger.debug("last_key = " + last_key);
+ String last_caption = (String) captions.get(0);
+ logger.debug("last_caption = " + last_caption);
+
+ List found_last_captions = findIndices(text, last_caption);
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("found " + found_last_captions.size() + " last captions.");
+ for (int i = 0; i < found_last_captions.size(); i++)
+ {
+ logger.debug(" found last caption at index " + found_last_captions.get(i));
+ }
+ }
+
+ for (int lci = 0; lci < found_last_captions.size(); lci++)
+ {
+ int last_caption_index = ((Integer) found_last_captions.get(lci)).intValue();
+ logger.debug("resolving signature block from last caption index " + last_caption_index);
+
+ int potential_block_end = findEndOfValue(text, last_caption_index);
+ logger.debug("potential_block_end = " + potential_block_end);
+
+ List found_keys = PdfAS.findBlockInText(text.substring(0, potential_block_end), block_type, old_style); // findRestKeys(text,
+ // keys,
+ // captions,
+ // last_caption_index);
+
+ if (found_keys == null)
+ {
+ logger.debug("Not all other captions could be found for the last_caption_index " + last_caption_index + " ==> discarding this index.");
+
+ continue;
+ }
+
+ // sort found keys ascendingly
+ PdfAS.sortFoundKeysAscendingly(found_keys);
+
+ boolean reverse_check_ok = reverseCheckFoundKeys(text, found_keys);
+ if (!reverse_check_ok)
+ {
+ logger.debug("The reverse check ruled this list of found keys out ==> they are discarded.");
+
+ continue;
+ }
+
+ logger.debug("The reverse check proved this list of found keys out ==> adding them as potential candidates.");
+
+ FoundBlock found_block = new FoundBlock();
+ found_block.std = block_type;
+ found_block.found_keys = found_keys;
+ found_block.end_index = findEndOfValue(text, last_caption_index);
+ found_blocks.add(found_block);
+ }
+
+ logger.debug("found " + found_blocks.size() + " potential signatures for " + block_type.getType());
+ return found_blocks;
+ }
+
+ /**
+ * Finds all indices of the given subtext within a given text.
+ *
+ * <p>
+ * This is usually used to find the indices of the last captions.
+ * </p>
+ *
+ * @param text
+ * The text to be searched.
+ * @param subtext
+ * The subtext to be sought.
+ * @return Returns the List of found indices.
+ */
+ public static List findIndices(String text, String subtext)
+ {
+ List found_indices = new ArrayList();
+ int search_from_index = 0;
+ for (;;)
+ {
+ int found_index = text.indexOf(subtext, search_from_index);
+ if (found_index < 0)
+ {
+ break;
+ }
+ found_indices.add(new Integer(found_index));
+ search_from_index = found_index + subtext.length();
+ }
+ return found_indices;
+ }
+
+ /**
+ * Finds the other keys/captions according to their order starting from the
+ * last_caption index upwards.
+ *
+ * @param text
+ * The text.
+ * @param keys
+ * The list of keys.
+ * @param captions
+ * The list of captions.
+ * @param last_caption_index
+ * The index of the last caption.
+ * @return Returns the List of found keys, if all keys could be found, or null
+ * if not all keys could be found.
+ */
+ public static List findRestKeys(String text, List keys, List captions,
+ int last_caption_index)
+ {
+ List found_keys = new ArrayList();
+
+ FoundKey last_caption_found_key = new FoundKey((String) keys.get(0), (String) captions.get(0), last_caption_index);
+ found_keys.add(last_caption_found_key);
+
+ String rest_text = text.substring(0, last_caption_index);
+
+ for (int i = 1; i < captions.size(); i++)
+ {
+ String sought_caption = (String) captions.get(i);
+ int index = rest_text.lastIndexOf(sought_caption);
+
+ if (index < 0)
+ {
+ return null;
+ }
+
+ FoundKey found_key = new FoundKey((String) keys.get(i), (String) captions.get(i), index);
+ found_keys.add(0, found_key);
+
+ rest_text = rest_text.substring(0, index);
+ }
+
+ return found_keys;
+ }
+
+ /**
+ * Performs a reverse (top to bottom) search for the found keys and checks
+ * that these indices are the same as those that were found during the regular
+ * (bottom up) search.
+ * <p>
+ * If a reverse check proves that the found keys are not at the same positions
+ * as during regular search, this list of found keys should be discarded.
+ * </p>
+ *
+ * @param text
+ * The text.
+ * @param found_keys
+ * The found keys to be reversely checked.
+ * @return Returns true, if all (also the non required) captions could be
+ * found at the same indices as during regular search, false
+ * otherwise.
+ */
+ public static boolean reverseCheckFoundKeys(String text, List found_keys)
+ {
+ int search_from_index = ((FoundKey) found_keys.get(0)).start_index;
+ for (int i = 0; i < found_keys.size(); i++)
+ {
+ FoundKey found_key = (FoundKey) found_keys.get(i);
+
+ int reverse_found_index = text.indexOf(found_key.caption, search_from_index);
+ if (reverse_found_index < 0)
+ {
+ throw new RuntimeException("The caption " + found_key.caption + " wasn't found in the text during reverse checking - there is something wrong.");
+ }
+
+ if (reverse_found_index != found_key.start_index)
+ {
+ logger.debug("The index for caption " + found_key.caption + " wasn't proved during reverse checking.");
+ return false;
+ }
+
+ search_from_index = found_key.start_index + found_key.caption.length();
+ }
+
+ return true;
+ }
+
+ /**
+ * Finds the end of the value in the text.
+ *
+ * <p>
+ * This simply scans for a '\n' from a given start index. The line up to and
+ * inclusive the '\n' is considered to be the value.
+ * </p>
+ * <p>
+ * Note that this method does NOT find the accurate value, if the value goes
+ * over multiple lines! This may bear a serious problem. Usually this method
+ * is only used to finding the end of the last value in a found block, because
+ * mid- values are exactly determined by their start index and the start of
+ * the next caption. Nevertheless, if the last value spans over multiple
+ * lines, this method will not retrieve it completely.
+ * </p>
+ *
+ * @param text
+ * The text.
+ * @param start_index
+ * The start index from where the end of the value is sought.
+ * @return Returns the end index of the value, which is the index of the first
+ * character not belonging to the value anymore (the character after
+ * the '\n').
+ */
+ public static int findEndOfValue(String text, int start_index)
+ {
+ int newline_index = text.indexOf('\n', start_index);
+ if (newline_index < 0)
+ {
+ return text.length();
+ }
+ return newline_index + 1;
+ }
+
+ /**
+ * Checks the integrity of a found block.
+ *
+ * <p>
+ * This is an assertive function.
+ * </p>
+ *
+ * @param text
+ * The text.
+ * @param found_block
+ * The found block.
+ */
+ public static void checkBlockIntegrity(String text, FoundBlock found_block)
+ {
+ for (int i = 0; i < found_block.found_keys.size() - 1; i++)
+ {
+ FoundKey this_key = (FoundKey) found_block.found_keys.get(i);
+ FoundKey next_key = (FoundKey) found_block.found_keys.get(i + 1);
+
+ int this_end_index = findEndOfValue(text, this_key.start_index);
+ if (this_end_index != next_key.start_index)
+ {
+ logger.warn("multi line value: " + this_key);
+ // throw new RuntimeException("The end index of found key " + this_key +
+ // " doesn't match the start index of found key " + next_key);
+ }
+ }
+
+ FoundKey last_key = (FoundKey) found_block.found_keys.get(found_block.found_keys.size() - 1);
+ if (findEndOfValue(text, last_key.start_index) != found_block.end_index)
+ {
+ throw new RuntimeException("The end index of last key " + last_key + " doesn't match the end index of the block " + found_block);
+ }
+
+ }
+
+ /**
+ * Cuts out the given found block from the text.
+ *
+ * @param text
+ * The text.
+ * @param block
+ * The found block.
+ * @return Returns the rest text without the block.
+ */
+ public static String cutOutBlock(String text, FoundBlock block)
+ {
+ int block_start_index = ((FoundKey) block.found_keys.get(0)).getStartIndex();
+ int block_end_index = block.end_index;
+
+ if (block_end_index == text.length())
+ {
+ // if the block is at the end of the text, remove the "\n" before the
+ // block as well.
+ String pre = text.substring(0, block_start_index - 1);
+ return pre;
+ }
+
+ String pre = text.substring(0, block_start_index);
+ String post = text.substring(block_end_index);
+
+ String rest_text = pre + post;
+ return rest_text;
+ }
+
+ /**
+ * Returns the value of the date field as String.
+ *
+ * @param text
+ * The text.
+ * @param block
+ * The found block.
+ * @return Returns the date value.
+ */
+ public static String getDateValue(String text, FoundBlock block)
+ {
+ FoundKey date_key = block.getDateFoundKey();
+ int date_value_start_index = date_key.start_index + date_key.caption.length();
+ int date_value_end_index = findEndOfValue(text, date_value_start_index);
+ String date_value = text.substring(date_value_start_index, date_value_end_index).trim();
+
+ return date_value;
+ }
+
+ /**
+ * Creates a SignatureObject from a found block by extracting the
+ * corresponding values.
+ *
+ * @param text
+ * The text.
+ * @param found_block
+ * The found block.
+ * @return Returns the created SignatureObject.
+ * @throws SignatureTypesException
+ * F.e.
+ * @throws SignatureException
+ * F.e.
+ */
+ public static SignatureObject createSignatureObjectFromFoundBlock(
+ String text, FoundBlock found_block) throws SignatureTypesException, SignatureException
+ {
+ SignatureObject signatureObject = new SignatureObject();
+
+ signatureObject.setSigType(found_block.std.getType());
+ signatureObject.initByType();
+
+ int end_index = found_block.end_index;
+ for (int i = found_block.found_keys.size() - 1; i >= 0; i--)
+ {
+ FoundKey cur_key = (FoundKey) found_block.found_keys.get(i);
+ int start_index = cur_key.getStartIndex() + cur_key.caption.length();
+
+ String value = text.substring(start_index, end_index);
+
+ signatureObject.setSigValueCaption(cur_key.getKey(), value, cur_key.caption);
+
+ end_index = cur_key.getStartIndex();
+ }
+
+ return signatureObject;
+
+ }
+
+ /**
+ * Parses the EGIZDate from a found block and the given text.
+ *
+ * @param text
+ * The text.
+ * @param found_block
+ * The found block.
+ * @return Returns the parsed EGIZDate.
+ */
+ public static EGIZDate getDateFromFoundBlock(String text,
+ FoundBlock found_block)
+ {
+ String date_value = getDateValue(text, found_block);
+ EGIZDate date = EGIZDate.parseFromString(date_value);
+ return date;
+ }
+
+ /**
+ * Sorts the List of found blocks by date.
+ *
+ * @param text
+ * The text.
+ * @param found_blocks
+ * The List of found blocks.
+ */
+ public static void sortFoundBlocksByDate(final String text, List found_blocks)
+ {
+ Collections.sort(found_blocks, new Comparator()
+ {
+ public int compare(Object arg0, Object arg1)
+ {
+ FoundBlock fb0 = (FoundBlock) arg0;
+ FoundBlock fb1 = (FoundBlock) arg1;
+
+ EGIZDate date0 = getDateFromFoundBlock(text, fb0);
+ EGIZDate date1 = getDateFromFoundBlock(text, fb1);
+ return date0.compareTo(date1);
+ }
+ });
+ }
+
+ /**
+ * Given a List of FoundBlock objects, this method returns the last blocks of
+ * this list that have the same date.
+ *
+ * <p>
+ * Usually a date sorted list (earliest first, latest last) will be provided
+ * to this method. Then the last date equal blocks are returned, which are the
+ * last blocks.
+ * </p>
+ *
+ * @param text
+ * The text to retrieve the values of the fields from.
+ * @param found_blocks
+ * The List of FoundBlock objects.
+ * @return Returns the List of the last date equal blocks.
+ */
+ public static List filterLastDateEqualBlocks(String text, List found_blocks)
+ {
+ List latest_blocks = new ArrayList();
+
+ latest_blocks.add(found_blocks.get(found_blocks.size() - 1));
+
+ for (int i = found_blocks.size() - 2; i >= 0; i--)
+ {
+ FoundBlock this_block = (FoundBlock) found_blocks.get(i);
+ FoundBlock succ_block = (FoundBlock) found_blocks.get(i + 1);
+
+ EGIZDate this_date = getDateFromFoundBlock(text, this_block);
+ EGIZDate succ_date = getDateFromFoundBlock(text, succ_block);
+
+ if (!this_date.equals(succ_date))
+ {
+ break;
+ }
+ latest_blocks.add(0, this_block);
+ }
+
+ return latest_blocks;
+ }
+
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinaryBlockInfo.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinaryBlockInfo.java
new file mode 100644
index 0000000..cdc092f
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinaryBlockInfo.java
@@ -0,0 +1,53 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: BinaryBlockInfo.java,v 1.1 2006/08/25 17:10:08 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.util.List;
+
+/**
+ * Helper class that holds information about a binary signature block.
+ *
+ * @author wprinz
+ */
+public class BinaryBlockInfo
+{
+ /**
+ * The signed size, in bytes.
+ *
+ * <p>
+ * This includes the block itself.
+ * </p>
+ */
+ public int signed_size = -1;
+
+ /**
+ * The List of ReplaceInfo objects that specify the replaced strings.
+ */
+ public List replaces = null;
+
+// /**
+// * The start of the /ODS number in the PDF.
+// */
+// public int ods_start = -1;
+//
+// /**
+// * The start of the \replaces array in the PDF.
+// */
+// public int array_start = -1;
+
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java
new file mode 100644
index 0000000..c5acbc4
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java
@@ -0,0 +1,1731 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: BinarySignature.java,v 1.4 2006/10/11 07:57:58 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
+import at.knowcenter.wag.egov.egiz.exceptions.PlaceholderException;
+import at.knowcenter.wag.egov.egiz.exceptions.PresentableException;
+import at.knowcenter.wag.egov.egiz.exceptions.SettingNotFoundException;
+import at.knowcenter.wag.egov.egiz.exceptions.SignatureException;
+import at.knowcenter.wag.egov.egiz.exceptions.SignatureTypesException;
+import at.knowcenter.wag.egov.egiz.sig.SignatureBlock;
+import at.knowcenter.wag.egov.egiz.sig.SignatureFieldDefinition;
+import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
+import at.knowcenter.wag.egov.egiz.sig.SignatureTypes;
+import at.knowcenter.wag.egov.egiz.sig.X509Cert;
+import at.knowcenter.wag.egov.egiz.tools.CodingHelper;
+import at.knowcenter.wag.exactparser.ByteArrayUtils;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.PRStream;
+import com.lowagie.text.pdf.PdfArray;
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfDictionary;
+import com.lowagie.text.pdf.PdfIndirectObject;
+import com.lowagie.text.pdf.PdfIndirectReference;
+import com.lowagie.text.pdf.PdfName;
+import com.lowagie.text.pdf.PdfNumber;
+import com.lowagie.text.pdf.PdfObject;
+import com.lowagie.text.pdf.PdfPTable;
+import com.lowagie.text.pdf.PdfReader;
+import com.lowagie.text.pdf.PdfStamper;
+import com.lowagie.text.pdf.PdfStamperImp;
+import com.lowagie.text.pdf.PdfString;
+import com.lowagie.text.pdf.PdfTemplate;
+
+/**
+ * Contains various extension functions to digitally sign documents.
+ *
+ * <p>
+ * These functions are used to replace parts of the original Egiz plain text
+ * signature mechanism.
+ * </p>
+ *
+ * @author wprinz
+ */
+public abstract class BinarySignature
+{
+ /**
+ * The tolerance area of the line break algorithm.
+ *
+ * @see Placeholder#replacePlaceholderWithTolerance(byte[], List, byte[], int)
+ */
+ public static final int LINE_BREAK_TOLERANCE = 10;
+
+ /**
+ * The number of bytes left out for the certificate placeholder.
+ */
+ public static final int CERTIFICATE_PLACEHOLDER_LENGTH = 10000;
+
+ /**
+ * The placeholder character used to fill out Strings in the layout process.
+ */
+ public static final byte LAYOUT_PLACEHOLDER = 'w';
+
+ /**
+ * This placeholder is used to fill out holes between the byte ranges before
+ * the document is signed.
+ */
+ public static final byte SIGN_PLACEHOLDER = 0;
+
+ /**
+ * The nil brev used to define an unrecognized value.
+ */
+ public static final byte[] BREV_NIL = { 'n', 'i', 'l' };
+
+ /**
+ * The date brev.
+ */
+ public static final byte[] BREV_DAT = { 'd', 'a', 't' };
+
+ /**
+ * The issure brev.
+ */
+ public static final byte[] BREV_ISS = { 'i', 's', 's' };
+
+ /**
+ * The serial number brev.
+ */
+ public static final byte[] BREV_SNR = { 's', 'n', 'r' };
+
+ /**
+ * The value brev.
+ */
+ public static final byte[] BREV_VAL = { 'v', 'a', 'l' };
+
+ /**
+ * The SIG_ID brev.
+ */
+ public static final byte[] BREV_SID = { 's', 'i', 'd' };
+
+ /**
+ * No explicit encoding.
+ */
+ public static final byte[] ENCODING_NIL = { 'n', 'i', 'l' };
+
+ /**
+ * PDF WinAnsiEncoding.
+ */
+ public static final byte[] ENCODING_WIN = { 'w', 'i', 'n' };
+
+ /**
+ * URL encoding.
+ */
+ public static final byte[] ENCODING_URL = { 'u', 'r', 'l' };
+
+ /**
+ * The PDFName of the Egiz Dictionary.
+ *
+ * <p>
+ * Used to locate and identify the Egiz Dictionary in the document.
+ * </p>
+ */
+ public static final PdfName EGIZ_DICT_NAME = new PdfName("EGIZSigDict");
+
+ /**
+ * The PDFName of the Original Document Size (ODS) field in an Egiz
+ * Dictionary.
+ *
+ * <p>
+ * The ODS must be a positive integral number.
+ * </p>
+ */
+ public static final PdfName EGIZ_ODS_NAME = new PdfName("ODS");
+
+ /**
+ * The PDFName of the Kennzeichnung attribute.
+ */
+ public static final PdfName EGIZ_KZ_NAME = new PdfName("ID");
+
+ /**
+ * The PDFName of the /replaces field in an Egiz Dictionary.
+ */
+ public static final PdfName EGIZ_REPLACES_NAME = new PdfName("replaces");
+
+ /**
+ * The PDFName of the /encodings field in an Egiz Dictionary.
+ */
+ public static final PdfName EGIZ_ENCODINGS_NAME = new PdfName("encodings");
+
+ /**
+ * The PDFName of the byte ranges array.
+ */
+ public static final PdfName EGIZ_BYTERANGES_NAME = new PdfName("ByteRange");
+
+ /**
+ * The PdfName of the certificate array.
+ */
+ public static final PdfName EGIZ_CERTIFICATE_NAME = new PdfName("Cert");
+
+ /**
+ * The PDFName of the Signature XObject field in an Egiz Dictionary.
+ *
+ * <p>
+ * This must be an indirect reference to the XObject containing the Signature
+ * table.
+ * </p>
+ */
+ public static final PdfName EGIZ_XOBJ_NAME = new PdfName("SigXObject");
+
+ /**
+ * The number placeholder that is used to give numbers a fixed length.
+ */
+ protected static final PdfNumber NUMBER_PLACEHOLDER = new PdfNumber(99999999);
+
+ /**
+ * Extracts the signature text only.
+ *
+ * <p>
+ * The signature text is the text of the Signature XObject.
+ * </p>
+ *
+ * @param egiz_dict
+ * The Egiz Dictionary.
+ *
+ * @return Returns the signature text.
+ */
+ public static String extractSignatureTextOnly(PdfDictionary egiz_dict) throws IOException
+ {
+ PdfIndirectReference xobj_ir = (PdfIndirectReference) egiz_dict.get(EGIZ_XOBJ_NAME);
+ PRStream temp_stream = (PRStream) PdfReader.getPdfObject(xobj_ir);
+
+ byte[] stream_bytes = PdfReader.getStreamBytes(temp_stream);
+
+ return Utils.extractPureTextFromContentStream(stream_bytes);
+ }
+
+ /**
+ * Retrieves the size of the original document from the Egiz Dictionary.
+ *
+ * @param egiz_dict
+ * The Egiz Dictionary.
+ * @return Returns the size (in bytes) of the original document.
+ */
+ public static int getOriginalDocumentSizeFromEgizDict(PdfDictionary egiz_dict)
+ {
+ PdfObject ods_obj = egiz_dict.get(EGIZ_ODS_NAME);
+ PdfNumber ods_number = (PdfNumber) PdfReader.getPdfObject(ods_obj);
+
+ return ods_number.intValue();
+ }
+
+ /**
+ * Retrieves the previous Egiz dictionary from the given one, if a previous
+ * dictionary exists.
+ *
+ * @param egiz_dict
+ * The Egiz Dictionary.
+ * @return Returns the previous Egiz Dictionary, or null if there is none.
+ */
+ public static PdfDictionary getPreviousFromEgizDict(PdfDictionary egiz_dict)
+ {
+ PdfObject prev_obj = egiz_dict.get(PdfName.PREV);
+ PdfDictionary previous_dict = (PdfDictionary) PdfReader.getPdfObject(prev_obj);
+ return previous_dict;
+ }
+
+ /**
+ * Retrieves the Egiz Dictionary from the document if present.
+ *
+ * @param reader
+ * The reader to retrieve the dictionary from.
+ * @return Returns the Egiz Dictionary, if present, or returns null, if no
+ * egiz dictionary was found.
+ */
+ public static PdfDictionary getEgizDictFromReader(PdfReader reader)
+ {
+ PdfIndirectReference dict_ir = getEgizDictIndRefFromReader(reader);
+ if (dict_ir == null)
+ {
+ return null;
+ }
+
+ PdfDictionary egiz_dict = (PdfDictionary) PdfReader.getPdfObject(dict_ir);
+
+ return egiz_dict;
+ }
+
+ /**
+ * Retrieves the Egiz Dictionary's indirect reference from the reader.
+ *
+ * @param reader
+ * The reader.
+ * @return Returns the indirect reference of the Egiz Dictionary, or null, if
+ * none exists.
+ */
+ public static PdfIndirectReference getEgizDictIndRefFromReader(
+ PdfReader reader)
+ {
+ PdfDictionary catalog = reader.getCatalog();
+ PdfIndirectReference dict_ir = (PdfIndirectReference) catalog.get(EGIZ_DICT_NAME);
+ return dict_ir;
+ }
+
+ /**
+ * Retrieves the chain of Egiz Dictionaries from the reader.
+ *
+ * <p>
+ * The first element in the List will be the top most (oldest) Egiz
+ * Dictionary. The last element in the List will be the bottom most (latest)
+ * Egiz Dictionary. If the list is empty, no dictionary could be found at all,
+ * which means that the document is not digitally signed.
+ * </p>
+ *
+ * @param reader
+ * The reader.
+ * @return Returns the List of PdfDictionaries from the document.
+ */
+ public static List getEgizDictChainFromReader(PdfReader reader)
+ {
+ List dicts = new ArrayList();
+
+ PdfDictionary current_dict = getEgizDictFromReader(reader);
+ if (current_dict != null)
+ {
+ dicts.add(0, current_dict);
+
+ while ((current_dict = getPreviousFromEgizDict(current_dict)) != null)
+ {
+ dicts.add(0, current_dict);
+ }
+ }
+
+ return dicts;
+ }
+
+ /**
+ * Builds a digest of the given data.
+ *
+ * @param data
+ * The data to be digested.
+ * @param length
+ * The length of the data portion that should be used for digesting.
+ * This allows to build the digest only over parts of the data.
+ * @return Returns the created digest.
+ * @throws PDFDocumentException
+ * Forwarded exception.
+ */
+ public static byte[] buildDigest(final byte[] data, final int length) throws PDFDocumentException
+ {
+ MessageDigest sha_512 = null;
+ try
+ {
+ sha_512 = MessageDigest.getInstance("SHA-512");
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ e.printStackTrace();
+ throw new PDFDocumentException(202, "Digest algorithm not supported - NoSuchAlgorithmException", e);
+ }
+
+ sha_512.reset();
+ sha_512.update(data, 0, length);
+ byte[] digest = sha_512.digest();
+
+ return digest;
+ }
+
+ /**
+ * Retrieves the signable text from the given document.
+ *
+ * @param data
+ * The data.
+ * @param ods
+ * The original document size.
+ * @return Returns the signable text.
+ */
+ public static String retrieveSignableTextFromData(final byte[] data,
+ final int ods)
+ {
+ // byte[] digest = buildDigest(data, ods);
+ String raw_text = CodingHelper.encodeBase64(data);// digest); // data);
+ return raw_text;
+ }
+
+ /**
+ * Fills the holes in the byte ranges with the SIGN_PLACEHOLDER.
+ *
+ * @param data
+ * The given byte ranged data.
+ * @param byte_ranges
+ * The byte ranges.
+ * @return Returns the filled text.
+ */
+ public static byte[] prepareDataToSign(final byte[] data,
+ final List byte_ranges)
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ Iterator it = byte_ranges.iterator();
+ int last_end = 0;
+ while (it.hasNext())
+ {
+ StringInfo si = (StringInfo) it.next();
+
+ for (int i = last_end; i < si.string_start; i++)
+ {
+ baos.write(SIGN_PLACEHOLDER);
+ }
+
+ baos.write(data, si.string_start, si.string_length);
+
+ last_end = si.string_start + si.string_length;
+ }
+ byte[] data_to_sign = baos.toByteArray();
+
+ return data_to_sign;
+ }
+
+ /**
+ * Extracts the binary 'text' of a document.
+ *
+ * <p>
+ * If the document contains an Egiz Dictionary, which means that it is already
+ * signed, the binary text is the Base64 coded string of the original document
+ * followed by the Ascii representation of the signature block.
+ * </p>
+ * <p>
+ * If the document does not contain an Egiz Dictionary, which means that it is
+ * unsigned, only the binary Base64 coded original document is returned as
+ * binary text.
+ * </p>
+ * <p>
+ * This function is intented for being used instead of the "text extraction"
+ * mechanism used in the plain text Egiz project.
+ * </p>
+ *
+ * @param doc
+ * The file.
+ * @return Returns the binary text of the document.
+ * @throws PDFDocumentException
+ * Forwarded exception.
+ */
+ public static String extractTextBinary(File doc) throws PDFDocumentException
+ {
+ try
+ {
+ FileInputStream fis = new FileInputStream(doc);
+ return extractTextBinary(fis);
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new PDFDocumentException(202, e);
+ }
+ }
+
+ /**
+ * Extracts the text binary.
+ *
+ * @param is
+ * @return Returns the binary text.
+ * @throws PDFDocumentException
+ */
+ public static String extractTextBinary(InputStream is) throws PDFDocumentException
+ {
+ try
+ {
+ // for some stupid reason this produces a read error if the is comes from
+ // a
+ // multipart servlet form..???
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int i = -1;
+ int acc = 0;
+ byte[] b = new byte[1000];
+ while ((i = is.read(b)) > 0)
+ {
+ acc += i;
+ System.out.print(" " + i);
+ baos.write(b, 0, i);
+ }
+ System.out.println("acc = " + acc);
+ byte[] pdf = baos.toByteArray();
+
+ return extractTextBinary(pdf);
+ }
+ catch (IOException e)
+ {
+ throw new PDFDocumentException(202, e);
+ }
+ }
+
+ /**
+ * Extracts the signable text from a binary pdf document.
+ *
+ * <p>
+ * The signable text is the text that will be signed or verified afterwards.
+ * </p>
+ *
+ * @param pdf
+ * The pdf document.
+ * @return Returns the extracted text String.
+ * @throws PDFDocumentException
+ * Forwarded exception.
+ */
+ public static String extractTextBinary(final byte[] pdf) throws PDFDocumentException
+ {
+ try
+ {
+ PdfReader reader = new PdfReader(new ByteArrayInputStream(pdf));
+ PdfDictionary egiz_dict = getEgizDictFromReader(reader);
+ if (egiz_dict == null)
+ {
+ System.out.println("NO Egiz Dict found - whole doc = original doc");
+
+ int ods = pdf.length;
+ return retrieveSignableTextFromData(pdf, ods);
+ }
+
+ String sig_text = extractSignatureTextOnly(egiz_dict);
+
+ int ods = getOriginalDocumentSizeFromEgizDict(egiz_dict);
+
+ String raw_text = retrieveSignableTextFromData(pdf, ods);
+ raw_text += "\n";
+ raw_text += sig_text;
+
+ return raw_text;
+ }
+ catch (IOException e)
+ {
+ throw new PDFDocumentException(202, e);
+ }
+ }
+
+ /**
+ * Retrieves the List of SignatureHolders containing the information of all
+ * digital signatures of the given document.
+ *
+ * <p>
+ * If the List of SignatureHolders is empty, the document is not signed
+ * anyways.
+ * </p>
+ *
+ * @param pdf
+ * The complete pdf document.
+ * @return Returns the List of SignatureHolders.
+ * @throws PDFDocumentException
+ * @throws SignatureTypesException
+ * @throws SignatureException
+ */
+ public static List extractSignatureHoldersBinary(final byte[] pdf) throws PDFDocumentException, SignatureTypesException, SignatureException
+ {
+ try
+ {
+ PdfReader reader = new PdfReader(new ByteArrayInputStream(pdf));
+ List chain = getEgizDictChainFromReader(reader);
+
+ List signatures = new ArrayList();
+ Iterator it = chain.iterator();
+ while (it.hasNext())
+ {
+ PdfDictionary dict = (PdfDictionary) it.next();
+
+ int ods = getOriginalDocumentSizeFromEgizDict(dict);
+ String signature_text = extractSignatureTextOnly(dict);
+
+ SignatureTypes sig_types = SignatureTypes.getInstance();
+ List types = sig_types.getSignatureTypeDefinitions();
+ SignatureBlock sig_block = new SignatureBlock(types);
+ boolean could_separate = sig_block.separateBlockFromRawText(signature_text, false);
+
+ if (could_separate)
+ {
+ SignatureObject sig_object = sig_block.getSignatureObject();
+
+ SignatureHolder holder = new BinarySignatureHolder(pdf, ods, sig_object);
+ signatures.add(holder);
+ }
+ }
+
+ return signatures;
+ }
+ catch (IOException e)
+ {
+ throw new PDFDocumentException(201, e);
+ }
+ }
+
+ // /**
+ // * Signs a document with the given signature table using the Incremental
+ // * Update method.
+ // *
+ // * <p>
+ // * The table containing the signature text will be appended. As specified by
+ // * the parameters, the signature will be appended to the last page, or a
+ // plain
+ // * new page will be created for the signature to hold.
+ // * </p>
+ // * <p>
+ // * The table will be completely wrapped by an XObject, which will also be
+ // * indirectly referenced by the Egiz Dictionary. This will ease the
+ // * verification process.
+ // * </p>
+ // * <p>
+ // * An Egiz Dictionary will be added to the new document that contains
+ // * information about the signature. Basically the size of the original
+ // * document and the reference of the signature table.
+ // * </p>
+ // *
+ // * @param original_document
+ // * The file name of the original document.
+ // * @param new_document
+ // * The file name of the new document to be created.
+ // * @param pdf_table
+ // * The PdfPTable that contains the signature block.
+ // * @param pos_x
+ // * The x position where the table should be inserted.
+ // * @param pos_y
+ // * The y position where the table should be inserted (on the last
+ // * page). If this is negative, a new page will be appended to the
+ // * document. Then the table will be inserted on that new page using
+ // * the absolute value of pos_y. Note that pos_y specifies the top
+ // * line of the table.
+ // * @throws PresentableException
+ // * Forwarded exception.
+ // *
+ // * @see #writeIncrementalUpdate(byte[], PdfPTable, float, float, boolean)
+ // */
+ // public static void writeIncrementalUpdate(String original_document,
+ // String new_document, PdfPTable pdf_table, float pos_x, float pos_y,
+ // int egiz_dict_num_replaces) throws PresentableException
+ // {
+ // try
+ // {
+ // File original_document_file = new File(original_document);
+ // FileInputStream fis = new FileInputStream(original_document_file);
+ // byte[] pdf = new byte[(int) original_document_file.length()];
+ // fis.read(pdf);
+ // fis.close();
+ //
+ // byte[] signed_pdf = writeIncrementalUpdate(pdf, pdf_table, pos_x, pos_y,
+ // egiz_dict_num_replaces);
+ //
+ // File new_document_file = new File(new_document);
+ // FileOutputStream fos = new FileOutputStream(new_document_file);
+ // fos.write(signed_pdf);
+ // fos.close();
+ // }
+ // catch (IOException e)
+ // {
+ // throw new PresentableException(e);
+ // }
+ // }
+
+ /**
+ * Signs a document with the given signature table using the Incremental
+ * Update method.
+ *
+ * <p>
+ * The table containing the signature text will be appended. As specified by
+ * the parameters, the signature will be appended to the last page, or a plain
+ * new page will be created for the signature to hold.
+ * </p>
+ * <p>
+ * The table will be completely wrapped by an XObject, which will also be
+ * indirectly referenced by the Egiz Dictionary. This will ease the
+ * verification process.
+ * </p>
+ * <p>
+ * An Egiz Dictionary will be added to the new document that contains
+ * information about the signature. Basically the size of the original
+ * document and the reference of the signature table.
+ * </p>
+ *
+ * @param original_document
+ * The original document.
+ * @param pdf_table
+ * The PdfPTable that contains the signature block.
+ * @param pos
+ * The Position object giving the exact location where to place the
+ * table. if page is -1, a new page will be appended to the document.
+ * Then the table will be inserted on that new page using the
+ * coordinates.
+ * @return Returns the new document.
+ * @throws PresentableException
+ * Forwarded exception.
+ */
+ public static IncrementalUpdateInformation writeIncrementalUpdate(
+ byte[] original_document, PdfPTable pdf_table, TablePos pos,
+ List variable_field_definitions, List all_field_definitions) throws PresentableException
+ {
+ try
+ {
+ IncrementalUpdateInformation iui = new IncrementalUpdateInformation();
+ iui.original_document = original_document;
+ iui.start_index = original_document.length;
+
+ Document.compress = false;
+
+ // System.out.println("wprinz: STAMPING PDF");
+
+ PdfReader reader = new PdfReader(original_document);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ // IMPORTANT: append the new content to the original document using
+ // incremental updated
+ // The stamper allows this by setting append = true
+ PdfStamper stamper = new PdfStamper(reader, baos, '\0', true);
+
+ int pdf_page_num = reader.getNumberOfPages();
+
+ // int signature_page = -1;
+ //
+ // if (pos_y >= 0)
+ // {
+ // signature_page = pdf_page_num;
+ // }
+ if (pos.page == -1)
+ {
+ pos.page = pdf_page_num + 1;
+
+ Rectangle psize = reader.getPageSizeWithRotation(pdf_page_num);
+ Rectangle rect = new Rectangle(psize);
+ stamper.insertPage(pos.page, rect);
+
+ // pos_y *= -1;
+ }
+
+ if (pos.page < 1 || pos.page > stamper.getReader().getNumberOfPages())
+ {
+ throw new PDFDocumentException(224, "The provided pos.page (=" + pos.page + ") is out of range.");
+ }
+ PdfContentByte content = stamper.getOverContent(pos.page);
+ // content = StampContent einer PageStamp.
+
+ // System.out.println("table_width = " + pdf_table.getTotalWidth() + ",
+ // table_height = " + pdf_table.getTotalHeight());
+
+ PdfTemplate table_template = content.createTemplate(pdf_table.getTotalWidth(), pdf_table.getTotalHeight());
+
+ pdf_table.writeSelectedRows(0, -1, 0, pdf_table.getTotalHeight(), table_template);
+
+ // table_template.moveTo(0, 0);
+ // table_template.lineTo(pdf_table.getTotalWidth(),
+ // pdf_table.getTotalHeight());
+ // table_template.stroke();
+ // table_template.moveTo(0, 0);
+ // table_template.lineTo(100, 100);
+ // table_template.stroke();
+
+ // pdf_table.writeSelectedRows(0, -1, SIGNATURE_BORDER / 2,
+ // table_position, content);
+
+ content.addTemplate(table_template, pos.pos_x, pos.pos_y - pdf_table.getTotalHeight());
+
+ // For debugging print a 100x100 grid
+// {
+// Rectangle psize = reader.getPageSizeWithRotation(pos.page);
+// float page_width = psize.width();
+// float page_height = psize.height();
+// for (float x = 0; x < page_width; x += 100)
+// {
+// content.moveTo(x, 0);
+// content.lineTo(x, page_height);
+// content.stroke();
+// }
+// for (float y = 0; y < page_height; y += 100)
+// {
+// content.moveTo(0, y);
+// content.lineTo(page_width, y);
+// content.stroke();
+// }
+// }
+
+ // content.setLineWidth(10.0f);
+ // content.moveTo(0, 0);
+ // content.lineTo(100, 100);
+ // content.stroke();
+
+ // PdfIndirectReference page_ref =
+ // stamper.getWriter().getPageReference(signature_page);
+ // System.out.println("page_ref = " + page_ref.toString());
+
+ // PdfObject page_obj = PdfReader.getPdfObject(page_ref);
+ // System.out.println("page_obj = " + page_obj);
+
+ // PdfDictionary page_dict = (PdfDictionary) page_obj;
+ // PdfObject resources_obj = page_dict.get(PdfName.RESOURCES);
+ // System.out.println("resources_obj = " + resources_obj);
+ // PdfDictionary resources = (PdfDictionary)
+ // PdfReader.getPdfObject(resources_obj);
+ // for (Iterator it = resources.getKeys().iterator(); it.hasNext();)
+ // {
+ // PdfName key = (PdfName) it.next();
+ // PdfObject value = resources.get(key);
+ // System.out.println(" " + key + " = " + value);
+ // }
+
+ // add the EGIZ dict:
+ if (variable_field_definitions != null)
+ {
+ createEgizDict(stamper, table_template, iui, variable_field_definitions, all_field_definitions);
+ }
+
+ stamper.close();
+ // System.out.println("wprinz: STAMPING FINISHED");
+
+ iui.signed_pdf = baos.toByteArray();
+
+ return iui;
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ throw new PresentableException(e);
+ }
+ catch (DocumentException e)
+ {
+ e.printStackTrace();
+ throw new PresentableException(e);
+ }
+ }
+
+ /**
+ * Creates the EGIZ Dictionary and adds it to the document.
+ *
+ * @param stamper
+ * The PdfStamper.
+ * @param table_template
+ * The Template of the Signature block.
+ * @param iui
+ * The IncrementalUpdateInformation.
+ * @param variable_field_definitions
+ * The field definitions.
+ * @throws IOException
+ * @throws SettingNotFoundException
+ */
+ protected static void createEgizDict(PdfStamper stamper,
+ PdfTemplate table_template, IncrementalUpdateInformation iui,
+ List variable_field_definitions, List all_field_definitions) throws IOException, SettingNotFoundException
+ {
+ // iui.temp_ir = table_template.getIndirectReference();
+ iui.temp_ir_number = table_template.getIndirectReference().getNumber();
+ iui.temp_ir_generation = table_template.getIndirectReference().getGeneration();
+
+ byte[] content_stream = table_template.toPdf(null);
+ iui.content_stream_length = content_stream.length;
+
+ iui.replaces = determineReplacesInContentStream(content_stream, 0, content_stream.length, variable_field_definitions);
+ iui.kz_list = determineKZ(content_stream, 0, content_stream.length, all_field_definitions);
+
+ // PdfIndirectReference previous_egiz_dict_ind_ref =
+ // getEgizDictIndRefFromReader(reader);
+
+ PdfDictionary egiz_dict = new PdfDictionary(EGIZ_DICT_NAME);
+ egiz_dict.put(EGIZ_XOBJ_NAME, table_template.getIndirectReference());
+ egiz_dict.put(EGIZ_ODS_NAME, NUMBER_PLACEHOLDER);
+
+ PdfArray kz_array = new PdfArray();
+ for (int i = 0; i < iui.kz_list.size(); i++)
+ {
+ kz_array.add(NUMBER_PLACEHOLDER); // start
+ kz_array.add(NUMBER_PLACEHOLDER); // length
+ }
+ egiz_dict.put(EGIZ_KZ_NAME, kz_array);
+
+ int num_replaces = calcNumReps(iui.replaces);
+ int num_holes = num_replaces + 1 + 1;
+ // +1 = the /encodings hole
+ // +1 = the /Cert
+ int num_byte_ranges = num_holes + 1;
+
+ PdfArray byte_ranges_array = new PdfArray();
+ for (int i = 0; i < num_byte_ranges; i++)
+ {
+ byte_ranges_array.add(NUMBER_PLACEHOLDER); // start
+ byte_ranges_array.add(NUMBER_PLACEHOLDER); // length
+ }
+ egiz_dict.put(EGIZ_BYTERANGES_NAME, byte_ranges_array);
+
+ PdfArray encodings_array = new PdfArray();
+ encodings_array.add(new PdfName(new String(ENCODING_NIL))); // the
+ // /encodings
+ encodings_array.add(new PdfName(new String(ENCODING_NIL))); // the /Cert
+
+ // array itself
+ PdfArray replaces_array = new PdfArray();
+ replaces_array.add(new PdfName(new String(BREV_NIL, "US-ASCII"))); // the
+ // /encodings
+ replaces_array.add(new PdfName(new String(BREV_NIL, "US-ASCII"))); // the
+ // /Cert
+
+ Iterator it = iui.replaces.iterator();
+ while (it.hasNext())
+ {
+ ReplaceInfo ri = (ReplaceInfo) it.next();
+ for (int i = 0; i < ri.replaces.size(); i++)
+ {
+ byte[] brev = typeToBrev(ri.sfd.field_name);
+ String brev_string = new String(brev, "US-ASCII");
+ PdfName brev_name = new PdfName(brev_string);
+ replaces_array.add(brev_name);
+
+ encodings_array.add(new PdfName(new String(ENCODING_WIN, "US-ASCII")));
+ }
+ }
+ egiz_dict.put(EGIZ_REPLACES_NAME, replaces_array);
+
+ egiz_dict.put(EGIZ_ENCODINGS_NAME, encodings_array);
+
+ PdfArray cert_array = new PdfArray();
+ byte[] cert_bytes = new byte[CERTIFICATE_PLACEHOLDER_LENGTH];
+ for (int i = 0; i < cert_bytes.length; i++)
+ {
+ cert_bytes[i] = 0;
+ }
+ PdfString cert_placeholder = new PdfString(cert_bytes);
+ cert_array.add(cert_placeholder);
+ egiz_dict.put(EGIZ_CERTIFICATE_NAME, cert_array);
+
+ // if (previous_egiz_dict_ind_ref != null)
+ // {
+ // egiz_dict.put(PdfName.PREV, previous_egiz_dict_ind_ref);
+ // }
+ // no EGIZ chain
+
+ PdfIndirectObject dict_ref = stamper.getWriter().addToBody(egiz_dict);
+ // iui.egiz_dict_ir = dict_ref.getIndirectReference();
+ iui.egiz_dict_ir_number = dict_ref.getIndirectReference().getNumber();
+ iui.egiz_dict_ir_generation = dict_ref.getIndirectReference().getGeneration();
+
+ PdfIndirectReference root_ref = (PdfIndirectReference) stamper.getReader().getTrailer().get(PdfName.ROOT);
+ PdfDictionary root = (PdfDictionary) PdfReader.getPdfObject(root_ref);
+ // root.put(EGIZ_DICT_NAME, dict_ref.getIndirectReference());
+ ((PdfStamperImp) stamper.getWriter()).markUsed(root);
+
+ // PdfDictionary extra_cata = stamper.getWriter().getExtraCatalog();
+ // extra_cata.put(dict_type, dict_ref.getIndirectReference());
+
+ ((PdfStamperImp) stamper.getWriter()).setEgizDictTrailerInfo(EGIZ_DICT_NAME, dict_ref.getIndirectReference());
+ }
+
+ /**
+ * Converts a field name (type) to the corresponding BREV.
+ *
+ * @param type
+ * The field name (type).
+ * @return Returns the corresponding BREV, or BREV_NIL if the type is not
+ * recognized.
+ */
+ protected static byte[] typeToBrev(String type)
+ {
+ if (type.equals(SignatureTypes.SIG_DATE))
+ {
+ return BREV_DAT;
+ }
+ if (type.equals(SignatureTypes.SIG_ISSUER))
+ {
+ return BREV_ISS;
+ }
+ if (type.equals(SignatureTypes.SIG_VALUE))
+ {
+ return BREV_VAL;
+ }
+ if (type.equals(SignatureTypes.SIG_NUMBER))
+ {
+ return BREV_SNR;
+ }
+ if (type.equals(SignatureTypes.SIG_ID))
+ {
+ return BREV_SID;
+ }
+
+ return BREV_NIL;
+ }
+
+ /**
+ * Updates the information in the egiz dictionary to reflect the real offsets
+ * of the byte ranges.
+ *
+ * <p>
+ * This replaces the "dummy numbers" in the egiz dictionary with the correct
+ * values.
+ * </p>
+ *
+ * @param iui
+ * The IncrementalUpdateInformation.
+ * @throws PDFDocumentException
+ */
+ public static void markByteRanges(IncrementalUpdateInformation iui) throws PDFDocumentException
+ {
+ try
+ {
+ iui.byte_ranges = new ArrayList();
+
+ int num_digits = Integer.toString(NUMBER_PLACEHOLDER.intValue()).length();
+ byte[] signed_pdf = iui.signed_pdf;
+
+ String str = iui.egiz_dict_ir_number + " " + iui.egiz_dict_ir_generation + " obj";
+ byte[] obj_bytes = str.getBytes("US-ASCII");
+ int obj_index = ByteArrayUtils.lastIndexOf(signed_pdf, obj_bytes);
+ int obj_start = obj_index + obj_bytes.length;
+
+ String ods_str = "/ODS ";
+ byte[] ods_bytes = ods_str.getBytes("US-ASCII");
+ int ods_index = ByteArrayUtils.indexOf(signed_pdf, obj_start, ods_bytes);
+ int ods_start = ods_index + ods_bytes.length;
+
+ String kz_str = "/ID[";
+ byte[] kz_bytes = kz_str.getBytes("US-ASCII");
+ int kz_index = ByteArrayUtils.indexOf(signed_pdf, obj_start, kz_bytes);
+ int kz_start = kz_index + kz_bytes.length;
+
+ String br_str = "/ByteRange[";
+ byte[] br_bytes = br_str.getBytes("US-ASCII");
+ int br_index = ByteArrayUtils.indexOf(signed_pdf, obj_start, br_bytes);
+ int array_start = br_index + br_bytes.length;
+
+ String enc_str = "/encodings[";
+ byte[] enc_bytes = enc_str.getBytes("US-ASCII");
+ int enc_index = ByteArrayUtils.indexOf(signed_pdf, obj_start, enc_bytes);
+ int enc_start = enc_index + enc_bytes.length;
+
+ String cert_str = "/Cert[(";
+ byte[] cert_bytes = cert_str.getBytes("US-ASCII");
+ int cert_index = ByteArrayUtils.indexOf(signed_pdf, obj_start, cert_bytes);
+ int cert_start = cert_index + cert_bytes.length;
+
+ replaceNumber(signed_pdf, ods_start, signed_pdf.length, num_digits);
+
+ // update the Kennzeichnung byte ranges
+ int cur_pos = kz_start;
+ for (int i = 0; i < iui.kz_list.size(); i++)
+ {
+ StringInfo si = (StringInfo) iui.kz_list.get(i);
+
+ replaceNumber(signed_pdf, cur_pos, si.string_start, num_digits);
+ cur_pos += num_digits;
+ cur_pos += 1;
+ replaceNumber(signed_pdf, cur_pos, si.string_length, num_digits);
+ cur_pos += num_digits;
+ cur_pos += 1;
+ }
+
+ int num_replaces = calcNumReps(iui.replaces);
+ int num_holes = num_replaces + 1 + 1; // +1 = the /encodings hole
+ // int num_byte_ranges = num_holes + 1;
+
+ cur_pos = array_start;
+ int cur_br_start = 0;
+
+ // write the /encodings byte range
+ {
+ int enc_length = (1 + 3) * num_holes;
+
+ StringInfo byte_range = new StringInfo();
+ byte_range.string_start = cur_br_start;
+ byte_range.string_length = enc_start;
+ iui.byte_ranges.add(byte_range);
+
+ replaceNumber(signed_pdf, cur_pos, byte_range.string_start, num_digits);
+ cur_pos += num_digits;
+ cur_pos += 1;
+ replaceNumber(signed_pdf, cur_pos, byte_range.string_length, num_digits);
+ cur_pos += num_digits;
+ cur_pos += 1;
+
+ cur_br_start = enc_start + enc_length;
+
+ iui.enc_start = enc_start;
+ iui.enc_length = enc_length;
+ }
+
+ // write the /Cert byte range
+ {
+ int cert_length = CERTIFICATE_PLACEHOLDER_LENGTH;
+
+ StringInfo byte_range = new StringInfo();
+ byte_range.string_start = cur_br_start;
+ byte_range.string_length = cert_start - cur_br_start;
+ iui.byte_ranges.add(byte_range);
+
+ replaceNumber(signed_pdf, cur_pos, byte_range.string_start, num_digits);
+ cur_pos += num_digits;
+ cur_pos += 1;
+ replaceNumber(signed_pdf, cur_pos, byte_range.string_length, num_digits);
+ cur_pos += num_digits;
+ cur_pos += 1;
+
+ cur_br_start = cert_start + cert_length;
+
+ iui.cert_start = cert_start;
+ iui.cert_length = cert_length;
+ }
+
+ Iterator rit = iui.replaces.iterator();
+ while (rit.hasNext())
+ {
+ ReplaceInfo ri = (ReplaceInfo) rit.next();
+
+ // byte [] value_bytes = ri.value.getBytes("ISO-8859-1");
+ // int write_pos = 0;
+
+ Iterator sit = ri.replaces.iterator();
+ while (sit.hasNext())
+ {
+ StringInfo si = (StringInfo) sit.next();
+
+ StringInfo byte_range = new StringInfo();
+ byte_range.string_start = cur_br_start;
+ byte_range.string_length = si.string_start - cur_br_start;
+ iui.byte_ranges.add(byte_range);
+
+ replaceNumber(signed_pdf, cur_pos, byte_range.string_start, num_digits);
+ cur_pos += num_digits;
+ cur_pos += 1;
+ replaceNumber(signed_pdf, cur_pos, byte_range.string_length, num_digits);
+ cur_pos += num_digits;
+ cur_pos += 1;
+
+ cur_br_start = si.string_start + si.string_length;
+ }
+
+ }
+
+ StringInfo byte_range = new StringInfo();
+ byte_range.string_start = cur_br_start;
+ byte_range.string_length = signed_pdf.length - cur_br_start;
+ iui.byte_ranges.add(byte_range);
+
+ replaceNumber(signed_pdf, cur_pos, byte_range.string_start, num_digits);
+ cur_pos += num_digits;
+ cur_pos += 1;
+ replaceNumber(signed_pdf, cur_pos, byte_range.string_length, num_digits);
+ cur_pos += num_digits;
+ cur_pos += 1;
+
+ }
+ catch (IOException e)
+ {
+ throw new PDFDocumentException(201, e);
+ }
+
+ }
+
+ /**
+ * Replaces the certificate placeholder with the certificate from the signed
+ * Signature Object.
+ *
+ * @param iui
+ * The IncrementalUpdateInformation.
+ * @throws PDFDocumentException
+ */
+ public static void replaceCertificate(IncrementalUpdateInformation iui) throws PDFDocumentException
+ {
+ X509Cert cert = iui.signed_signature_object.getX509Cert();
+ // X509Certificate certificate = cert.getX509Certificate();
+ try
+ {
+ byte[] encoded = cert.getCertString().getBytes("US-ASCII"); // certificate.getEncoded();
+ byte[] escaped = Placeholder.escapePDFString(encoded);
+ if (escaped.length > iui.cert_length)
+ {
+ throw new PlaceholderException("certificate", escaped.length - iui.cert_length);
+ }
+ System.arraycopy(escaped, 0, iui.signed_pdf, iui.cert_start, escaped.length);
+ }
+ // catch (CertificateEncodingException e)
+ // {
+ // throw new PDFDocumentException(300, e);
+ // }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new PDFDocumentException(300, e);
+ }
+ }
+
+ /**
+ * Replaces the placeholders with values from the signed SignatureObject.
+ *
+ * @param iui
+ * The IncrementalUpdateInformation.
+ * @throws PDFDocumentException
+ */
+ public static void replacePlaceholders(IncrementalUpdateInformation iui) throws PDFDocumentException
+ {
+ final byte[] signed_pdf = iui.signed_pdf;
+
+ // int num_replaces = calcNumReps(iui.replaces);
+ // int num_holes = num_replaces + 1 + 1; // +1 = the /encodings hole; +1 =
+ // the
+ // /Cert
+
+ int encoding_entry_index = 0;
+ {// /encodings itself
+ int enc_offset = iui.enc_start + encoding_entry_index * 4;
+ signed_pdf[enc_offset] = '/';
+ System.arraycopy(ENCODING_NIL, 0, signed_pdf, enc_offset + 1, ENCODING_NIL.length);
+ encoding_entry_index++;
+ }
+ {// /Cert itself
+ int enc_offset = iui.enc_start + encoding_entry_index * 4;
+ signed_pdf[enc_offset] = '/';
+ System.arraycopy(ENCODING_NIL, 0, signed_pdf, enc_offset + 1, ENCODING_NIL.length);
+ encoding_entry_index++;
+ }
+
+ for (int i = 0; i < iui.replaces.size(); i++)
+ {
+ ReplaceInfo ri = (ReplaceInfo) iui.replaces.get(i);
+
+ try
+ {
+ String value = ri.value;
+
+ if (value == null)
+ {
+ value = "";
+ }
+
+ byte[] encoding = ENCODING_WIN;
+ byte[] replace_bytes = Placeholder.applyWinAnsiEncoding(value);
+
+ String restored_value = Placeholder.unapplyWinAnsiEncoding(replace_bytes);
+ if (!value.equals(restored_value))
+ {
+ // debug:
+ System.out.println("WinAnsiEncoding doesn't fit - using URL instead!");
+ // /debug!
+
+ replace_bytes = Placeholder.applyURLEncoding(value);
+
+ encoding = ENCODING_URL;
+ }
+
+ for (int string_index = 0; string_index < ri.replaces.size(); string_index++)
+ {
+ int enc_offset = iui.enc_start + encoding_entry_index * 4;
+ signed_pdf[enc_offset] = '/';
+ System.arraycopy(encoding, 0, signed_pdf, enc_offset + 1, encoding.length);
+ encoding_entry_index++;
+ }
+
+ Placeholder.replacePlaceholderWithTolerance(signed_pdf, ri.replaces, replace_bytes, LINE_BREAK_TOLERANCE);
+ }
+ catch (PlaceholderException e)
+ {
+ throw new PlaceholderException(ri.sfd.field_name, e.getMissing());
+ }
+
+ }
+ }
+
+ /**
+ * Calculates the number of actual String replaces from a given ReplaceInfo
+ * list.
+ * <p>
+ * This is used to determine the number of actual replaces that has to be
+ * carried out. Accordingly to this number, entries in the dictionary are
+ * created.
+ * </p>
+ *
+ * @param replaces
+ * The ReplaceInfo list.
+ * @return Returns the number of string replaces.
+ */
+ protected static int calcNumReps(List replaces)
+ {
+ int number = 0;
+ Iterator it = replaces.iterator();
+ while (it.hasNext())
+ {
+ ReplaceInfo ri = (ReplaceInfo) it.next();
+ number += ri.replaces.size();
+ }
+ return number;
+ }
+
+ /**
+ * Determines the List of ReplaceInfo objects of replaces in the content
+ * stream regarding the given field definitions.
+ *
+ * <p>
+ * This method collects all variable String fields in a content stream and
+ * orders them according to their start offset.
+ * </p>
+ *
+ * @param pdf
+ * The PDF.
+ * @param begin
+ * The start of the content stream.
+ * @param end
+ * The end of the content stream.
+ * @param field_definitions
+ * The field definitions that are counceled to find out which and
+ * where varaible strings are.
+ * @return Returns the list of ReplaceInfo objects specifying the variable
+ * areas.
+ */
+ protected static List determineReplacesInContentStream(final byte[] pdf,
+ int begin, int end, List field_definitions)
+ {
+ List replaces = new ArrayList();
+ try
+ {
+
+ List strings = Placeholder.parseStrings(pdf, begin, end);
+
+ for (int index = 0; index < field_definitions.size(); index++)
+ {
+ SignatureFieldDefinition sfd = (SignatureFieldDefinition) field_definitions.get(index);
+
+ if (sfd.placeholder_length > 0)
+ {
+ ReplaceInfo ri = new ReplaceInfo();
+ ri.sfd = sfd;
+ ri.replaces = new ArrayList();
+
+ byte[] caption = sfd.caption.getBytes("ISO-8859-1");
+
+ int caption_index = findIndex(strings, caption);
+ int start_index = skipStrings(strings, caption_index, caption);
+ int next_index = findFirstNotPlaceholder(strings, start_index);
+
+ for (int i = start_index; i < next_index; i++)
+ {
+ StringInfo si = (StringInfo) strings.get(i);
+ ri.replaces.add(si);
+ }
+
+ replaces.add(ri);
+ }
+ }
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ }
+
+ // sort replaces
+ Collections.sort(replaces, new Comparator()
+ {
+ public int compare(Object arg0, Object arg1)
+ {
+ ReplaceInfo ri0 = (ReplaceInfo) arg0;
+ ReplaceInfo ri1 = (ReplaceInfo) arg1;
+ int start0 = ((StringInfo) ri0.replaces.get(0)).string_start;
+ int start1 = ((StringInfo) ri1.replaces.get(0)).string_start;
+ return start0 - start1;
+ }
+ });
+
+ return replaces;
+ }
+
+ /**
+ * Determines the Kennzeichnug in the content stream.
+ *
+ * @param pdf
+ * The PDF.
+ * @param begin
+ * The start of the content stream.
+ * @param end
+ * The end of the content stream.
+ * @param field_definitions
+ * The field definitions.
+ * @return Returns the List of StringInfo objects representing the KZ field.
+ * @throws SettingNotFoundException
+ * F.e.
+ */
+ protected static List determineKZ(final byte[] pdf, int begin, int end,
+ List field_definitions) throws SettingNotFoundException
+ {
+ try
+ {
+ List strings = Placeholder.parseStrings(pdf, begin, end);
+
+ for (int index = 0; index < field_definitions.size(); index++)
+ {
+ SignatureFieldDefinition sfd = (SignatureFieldDefinition) field_definitions.get(index);
+
+ if (sfd.field_name.equals(SignatureTypes.SIG_KZ))
+ {
+ List kz_list = new ArrayList();
+
+ byte[] caption = sfd.caption.getBytes("ISO-8859-1");
+
+ int caption_index = findIndex(strings, caption);
+ int start_index = skipStrings(strings, caption_index, caption);
+
+ int end_index = -1;
+ for (end_index = start_index; end_index < strings.size(); end_index++)
+ {
+ StringInfo si = (StringInfo) strings.get(end_index);
+
+ if (startsWithCaption(si, field_definitions))
+ {
+ break;
+ }
+
+ kz_list.add(si);
+ }
+
+ return kz_list;
+ }
+ }
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ }
+ throw new SettingNotFoundException(102, "Field " + SignatureTypes.SIG_KZ + " not found.");
+ }
+
+ /**
+ * Finds the index of the StringInfo within the StringInfo list that has the
+ * given content (caption).
+ *
+ * @param strings
+ * The list of StringInfos.
+ * @param caption
+ * The text to be matched to the strings.
+ * @return Returns the index of the found string, or -1 if no string matched.
+ */
+ protected static int findIndex(List strings, byte[] caption)
+ {
+ for (int i = 0; i < strings.size(); i++)
+ {
+ if (isCaption(strings, i, caption))
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ protected static boolean isCaption(List strings, int index, byte[] caption)
+ {
+ try
+ {
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (int i = index; i < strings.size(); i++)
+ {
+ StringInfo si = (StringInfo) strings.get(i);
+ baos.write(si.copyStringBytes());
+ }
+ byte[] str_data = baos.toByteArray();
+ byte[] unescaped = Placeholder.unescapePDFString(str_data);
+ if (ByteArrayUtils.compareByteArrays(unescaped, 0, caption))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ return false;
+ }
+
+ }
+
+ protected static int skipStrings(List strings, int index, byte[] caption)
+ {
+ int length = 0;
+ for (int i = index; i < strings.size(); i++)
+ {
+ StringInfo si = (StringInfo) strings.get(i);
+ length += si.string_length;
+
+ if (length >= caption.length)
+ {
+ return i + 1;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Tells, if the given StringInfo contains only placeholder characters.
+ *
+ * @param si
+ * The StringInfo.
+ * @param placeholder
+ * The placeholder character.
+ * @return Returns true, if the string contains only the given placeholder
+ * characters, false otherwise.
+ */
+ protected static boolean isPlaceholder(StringInfo si, byte placeholder)
+ {
+ byte[] string_bytes = si.copyStringBytes();
+ for (int i = 0; i < string_bytes.length; i++)
+ {
+ if (string_bytes[i] != placeholder)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected static boolean startsWithCaption(StringInfo si,
+ List field_definitions)
+ {
+ try
+ {
+ for (int i = 0; i < field_definitions.size(); i++)
+ {
+ SignatureFieldDefinition sfd = (SignatureFieldDefinition) field_definitions.get(i);
+
+ String caption = sfd.caption;
+ String str = si.getString("ISO-8859-1");
+
+ if (caption.startsWith(str))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * Finds the first string after and at the given index not being a placeholder
+ * string.
+ *
+ * @param strings
+ * The list of StringInfos.
+ * @param start
+ * The index where to start the search.
+ * @return Returns the index of the first not placeholder string, or
+ * strings.size() if no more non placeholder strings could be found.
+ */
+ protected static int findFirstNotPlaceholder(List strings, int start)
+ {
+ for (int i = start; i < strings.size(); i++)
+ {
+ StringInfo si = (StringInfo) strings.get(i);
+ if (!isPlaceholder(si, LAYOUT_PLACEHOLDER))
+ {
+ return i;
+ }
+ }
+ return strings.size();
+ }
+
+ /**
+ * Restores the given String to its placeholder.
+ *
+ * @param pdf
+ * The PDF.
+ * @param si
+ * The string.
+ * @param placeholder
+ * The placeholder the string should be filled with.
+ */
+ public static void restorePlaceholder(final byte[] pdf, StringInfo si,
+ final byte placeholder)
+ {
+ byte[] ph = new byte[si.string_length];
+ for (int i = 0; i < ph.length; i++)
+ {
+ ph[i] = placeholder;
+ }
+ System.arraycopy(ph, 0, pdf, si.string_start, ph.length);
+ }
+
+ /**
+ * Reconstructs the replaces from the PDF and forms suitable value strings.
+ *
+ * @param pdf
+ * The PDF.
+ * @param brevs
+ * The brevs.
+ * @param sis
+ * The StringInfo objects of the strings.
+ * @return Returns the List of ReplaceInfo objects containing the restored
+ * values.
+ * @throws PDFDocumentException
+ */
+ public static List reconstructReplaces(final byte[] pdf, byte[][] brevs,
+ StringInfo[] sis, byte[][] encodings) throws PDFDocumentException
+ {
+ try
+ {
+ List replaces = new ArrayList();
+
+ ReplaceInfo cur_ri = null;
+
+ for (int cur = 0; cur < brevs.length; cur++)
+ {
+ if (ByteArrayUtils.compareByteArrays(brevs[cur], 0, BREV_NIL))
+ {
+ continue;
+ }
+
+ if (cur_ri == null || !ByteArrayUtils.compareByteArrays(cur_ri.brev, 0, brevs[cur]))
+ {
+ cur_ri = new ReplaceInfo();
+
+ cur_ri.replaces = new ArrayList();
+
+ cur_ri.brev = brevs[cur];
+ cur_ri.enc = encodings[cur];
+
+ replaces.add(cur_ri);
+ }
+
+ cur_ri.replaces.add(sis[cur]);
+ }
+
+ // restore value Strings
+ Iterator rit = replaces.iterator();
+ while (rit.hasNext())
+ {
+ ReplaceInfo ri = (ReplaceInfo) rit.next();
+ ri.value = Placeholder.reconstructStringFromPartition(pdf, ri.replaces, ri.enc);
+
+ // System.out.println(new String(ri.brev, "US-ASCII") + ": " +
+ // ri.value);
+ }
+
+ return replaces;
+ }
+ catch (IOException e)
+ {
+ throw new PDFDocumentException(310, e);
+ }
+
+ }
+
+ /**
+ * Reads an unsigned integer number.
+ *
+ * @param pdf
+ * The PDF.
+ * @param start_index
+ * The start index of the number.
+ * @param num_digits
+ * The number of digits.
+ * @return Returns the read number.
+ */
+ public static int readNumber(final byte[] pdf, final int start_index,
+ final int num_digits)
+ {
+ try
+ {
+ byte[] n_bytes = new byte[num_digits];
+ System.arraycopy(pdf, start_index, n_bytes, 0, num_digits);
+ String n_string = new String(n_bytes, "US-ASCII");
+
+ int n = Integer.parseInt(n_string);
+ return n;
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ return -1;
+ }
+ }
+
+ /**
+ * Replaces a number by the new value.
+ *
+ * @param pdf
+ * The PDF.
+ * @param start_index
+ * The start index of the number.
+ * @param number
+ * The new number.
+ * @param num_digits
+ * The number of digits.
+ */
+ public static void replaceNumber(final byte[] pdf, final int start_index,
+ final int number, final int num_digits)
+ {
+ try
+ {
+ if (number < 0)
+ {
+ throw new IllegalArgumentException("The given number " + number + " must not be negative.");
+ }
+ String number_string = Integer.toString(number);
+ if (number_string.length() > num_digits)
+ {
+ throw new IllegalArgumentException("The given number " + number + " has more than " + num_digits + " digits.");
+ }
+
+ int leading_zeros = num_digits - number_string.length();
+ String zeros_string = "";
+ for (int i = 0; i < leading_zeros; i++)
+ {
+ zeros_string += "0";
+ }
+
+ String total_string = zeros_string + number_string;
+ byte[] total_bytes = total_string.getBytes("US-ASCII");
+ System.arraycopy(total_bytes, 0, pdf, start_index, num_digits);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * For debugging purposes.
+ *
+ * @param args
+ * @throws IOException
+ */
+ public static void main(String[] args) throws IOException
+ {
+ File signed_doc = new File("C:/wprinz/temp.pdf");
+
+ PdfReader reader = new PdfReader(new FileInputStream(signed_doc));
+ PdfDictionary egiz_dict = getEgizDictFromReader(reader);
+ if (egiz_dict == null)
+ {
+ System.out.println("NO Egiz Dict");
+ return;
+ }
+
+ String sig_text = extractSignatureTextOnly(egiz_dict);
+ System.out.println("Sig Text:");
+ System.out.println(sig_text);
+
+ int ods = getOriginalDocumentSizeFromEgizDict(egiz_dict);
+ System.out.println("Original Document Size = " + ods);
+ }
+
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignatureHolder.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignatureHolder.java
new file mode 100644
index 0000000..1f522ff
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignatureHolder.java
@@ -0,0 +1,146 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: BinarySignatureHolder.java,v 1.1 2006/10/11 07:58:28 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.Serializable;
+
+import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
+
+/**
+ * Data structure that holds the information of one binary signature block,
+ * which is the signed/signable pdf and the corresponding SignatureObject.
+ *
+ * <p>
+ * The actual signed text is computed by Base64 encoding the binary data when
+ * first requested.
+ * </p>
+ * <p>
+ * The corresponding getters can be used to retrieve the signed document (e.g.
+ * for displaying a preview).
+ * </p>
+ *
+ * @author wprinz
+ */
+public class BinarySignatureHolder implements Serializable, SignatureHolder
+{
+
+ /**
+ * SVUID.
+ */
+ private static final long serialVersionUID = -7208103904479272760L;
+
+ /**
+ * The whole pdf this holder was extracted from.
+ */
+ private byte[] signed_pdf = null;
+
+ /**
+ * The number of bytes that give the signed document.
+ */
+ private int signed_pdf_length = -1;
+
+ /**
+ * The signed text of this object.
+ *
+ * <p>
+ * This is the value that will be signed by the Connector.
+ * </p>
+ */
+ private String signed_text = null;
+
+ /**
+ * The signature object.
+ */
+ private SignatureObject signature_object = null;
+
+ /**
+ * Constructor that takes the pdf and the SignatureObject as parameters.
+ *
+ * @param pdf
+ * The pdf data.
+ * @param length
+ * The length (number of bytes) of the pdf data to be used for being
+ * converted into "signed text".
+ * @param so
+ * The signed signature object.
+ */
+ public BinarySignatureHolder(final byte[] pdf, final int length, SignatureObject so)
+ {
+ this.signed_pdf = pdf;
+ this.signed_pdf_length = length;
+ this.signature_object = so;
+
+ this.signed_text = null;
+ }
+
+ /**
+ * @see at.knowcenter.wag.egov.egiz.pdf.SignatureHolder#getSignedText()
+ */
+ public String getSignedText()
+ {
+ if (this.signed_text == null)
+ {
+ computeSignedText();
+ }
+ return this.signed_text;
+ }
+
+ /**
+ * @see at.knowcenter.wag.egov.egiz.pdf.SignatureHolder#getSignatureObject()
+ */
+ public SignatureObject getSignatureObject()
+ {
+ return this.signature_object;
+ }
+
+ /**
+ * Computes or recomputes the signed text from the underlying binary data.
+ *
+ * <p>
+ * This usually encodes the binary data of given length in Base64.
+ * </p>
+ *
+ * <p>
+ * This is usually called automatically when the signed text is first
+ * requested.
+ * </p>
+ */
+ protected void computeSignedText()
+ {
+ this.signed_text = BinarySignature.retrieveSignableTextFromData(this.signed_pdf, this.signed_pdf_length);
+ }
+
+ /**
+ * Returns the signed_pdf.
+ * @return Returns the signed_pdf.
+ */
+ public byte[] getSignedPdf()
+ {
+ return this.signed_pdf;
+ }
+
+ /**
+ * Returns the signed_pdf_length.
+ * @return Returns the signed_pdf_length.
+ */
+ public int getSignedPdfLength()
+ {
+ return this.signed_pdf_length;
+ }
+
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/EGIZDate.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/EGIZDate.java
new file mode 100644
index 0000000..7b71d27
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/EGIZDate.java
@@ -0,0 +1,189 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: EGIZDate.java,v 1.1 2006/10/31 08:08:33 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Represents a signature date and the signing time as can be found in the
+ * SIG_DATE field.
+ *
+ * <p>
+ * This is used to compare date values of signatures.
+ * </p>
+ *
+ * @author wprinz
+ */
+public class EGIZDate
+{
+
+ /**
+ * The year.
+ */
+ protected int year;
+
+ /**
+ * The month.
+ */
+ protected int month;
+
+ /**
+ * The day.
+ */
+ protected int day;
+
+ /**
+ * The hour.
+ */
+ protected int hour;
+
+ /**
+ * The minute.
+ */
+ protected int minute;
+
+ /**
+ * The second.
+ */
+ protected int second;
+
+ /**
+ * Constructor that fills the date with values.
+ *
+ * @param year
+ * The year.
+ * @param month
+ * The month.
+ * @param day
+ * The day.
+ * @param hour
+ * The hour.
+ * @param minute
+ * The minute.
+ * @param second
+ * The second.
+ */
+ public EGIZDate(int year, int month, int day, int hour, int minute, int second)
+ {
+ this.year = year;
+ this.month = month;
+ this.day = day;
+ this.hour = hour;
+ this.minute = minute;
+ this.second = second;
+ }
+
+ /**
+ * Parses the date information from a given date value.
+ *
+ * <p>
+ * Usually the date value is one extracted from the value of the SIG_DATE
+ * field.
+ * </p>
+ *
+ * @param date_value
+ * The date value String.
+ * @return Returns the parsed EGIZDate. An IllegalArgumentException is thrown
+ * if the date String has an illegal format.
+ */
+ public static EGIZDate parseFromString(String date_value)
+ {
+ // find the according RFC standard and cite it
+
+ // BKU writes a Z after the date for some reason.
+ Pattern date_pattern = Pattern.compile("^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d[Z]?$");
+ Matcher date_matcher = date_pattern.matcher(date_value);
+ if (!date_matcher.matches())
+ {
+ throw new IllegalArgumentException("The date_value (" + date_value + " has an illegal format.");
+ }
+ // for some strange reasons capture groups don't work
+
+ int year = Integer.parseInt(date_value.substring(0, 4));
+ int month = Integer.parseInt(date_value.substring(5, 7));
+ int day = Integer.parseInt(date_value.substring(8, 10));
+ int hour = Integer.parseInt(date_value.substring(11, 13));
+ int minute = Integer.parseInt(date_value.substring(14, 16));
+ int second = Integer.parseInt(date_value.substring(17, 19));
+
+ return new EGIZDate(year, month, day, hour, minute, second);
+ }
+
+ /**
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj)
+ {
+ if (!(obj instanceof EGIZDate))
+ {
+ return false;
+ }
+
+ return toString().equals(obj.toString());
+ }
+
+ /**
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return toString().hashCode();
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return "parsed date = " + year + "-" + month + "-" + day + "T" + hour + ":" + minute + ":" + second;
+ }
+
+ /**
+ * Converts the date to a long integer.
+ *
+ * <p>
+ * An earlier date is lower than a later date.
+ * </p>
+ * <p>
+ * E.g. a date in 1999 will get a smaller number than a date in 2006.
+ * </p>
+ *
+ * @return Returns the compareable long.
+ */
+ protected long toCompareableLong()
+ {
+ return +this.year * 12 * 31 * 24 * 60 * 60 + this.month * 31 * 24 * 60 * 60 + this.day * 24 * 60 * 60 + this.hour * 60 * 60 + this.minute * 60 + this.second;
+ }
+
+ /**
+ * Compares this EGIZDate to another EXIZDate.
+ *
+ * @param other
+ * The other EGIZDate.
+ * @return Returns negative if this date is earlier (lower) than the other
+ * date. Returns 0 if both dates are equal. Returns positive if this
+ * date is later (higher) than the other date.
+ */
+ public int compareTo(EGIZDate other)
+ {
+ long diff = toCompareableLong() - other.toCompareableLong();
+ return (int) diff;
+ }
+
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/IncrementalUpdateInformation.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/IncrementalUpdateInformation.java
new file mode 100644
index 0000000..cb983cb
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/IncrementalUpdateInformation.java
@@ -0,0 +1,152 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: IncrementalUpdateInformation.java,v 1.2 2006/10/31 08:09:33 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.Serializable;
+import java.util.List;
+
+import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
+
+/**
+ * This parameter object contains all useful inforamtion the binary incremental
+ * update methods need to create and replace a binary singature block.
+ *
+ * <p>
+ * This class is basically used to transport information about the document from
+ * the prepareSign to the finishSign of the Signator. In future, this could be
+ * extended and encapsulated to task proprietary IUI instances. E.g. a
+ * BinarySignatorIUI, a TextualSignatorIUI, both implementing the core IUI
+ * interface, but encapsulating Binary or Textual specialities.
+ * </p>
+ *
+ * @author wprinz
+ */
+public class IncrementalUpdateInformation implements Serializable
+{
+ /**
+ * SVUID.
+ */
+ private static final long serialVersionUID = -5904526956127108035L;
+
+ /**
+ * The original PDF document.
+ */
+ public byte[] original_document = null;
+
+ /**
+ * The Singature type to be created.
+ */
+ public String signature_type = null;
+
+ /**
+ * The signed pdf document.
+ *
+ * <p>
+ * This is the original document plus the incremental update block.
+ * </p>
+ */
+ public byte[] signed_pdf = null;
+
+ /**
+ * The start index of this incremental update block.
+ */
+ int start_index = -1;
+
+ /**
+ * The indirect reference of the egiz dict.
+ */
+ // PdfIndirectReference egiz_dict_ir = null;
+ public int egiz_dict_ir_number;
+
+ public int egiz_dict_ir_generation;
+
+ /**
+ * The List of ReplaceInfo objects specifying the byte ranges where the
+ * variable data has to be fille in.
+ */
+ public List replaces = null;
+
+ /**
+ * The List of StringInfo objects specifying the byte ranges that should
+ * be/were signed.
+ */
+ public List byte_ranges = null;
+
+ /**
+ * The indirect reference of the signature x-object.
+ */
+ // public PdfIndirectReference temp_ir;
+ public int temp_ir_number;
+
+ public int temp_ir_generation;
+
+ /**
+ * The start index of the content stream of the signature x-object.
+ */
+ public int content_stream_start = -1;
+
+ /**
+ * The length of the content stream of the signature x-object.
+ */
+ public int content_stream_length = -1;
+
+ /**
+ * The document text for signing.
+ */
+ public String document_text;
+
+ /**
+ * The SignatureObject containing the variable values after the document text
+ * has been signed.
+ * <p>
+ * These values have to be filled in.
+ * </p>
+ */
+ public SignatureObject signed_signature_object;
+
+ /**
+ * The start of the /encodings array.
+ */
+ public int enc_start = -1;
+
+ /**
+ * The length of the /encodings array.
+ */
+ public int enc_length = -1;
+
+ /**
+ * The start of the first /Cert
+ */
+ public int cert_start = -1;
+
+ /**
+ * The length of the /cert placeholder.
+ */
+ public int cert_length = -1;
+
+ /**
+ * The list of strings of the KZ.
+ */
+ public List kz_list;
+
+ /**
+ * The table position.
+ */
+ public TablePos pos;
+
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFPage.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFPage.java
new file mode 100644
index 0000000..bed1b65
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFPage.java
@@ -0,0 +1,539 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: PDFPage.java,v 1.5 2006/10/31 08:09:33 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.pdmodel.PDPage;
+import org.pdfbox.pdmodel.PDResources;
+import org.pdfbox.pdmodel.common.PDStream;
+import org.pdfbox.pdmodel.graphics.xobject.PDXObject;
+import org.pdfbox.pdmodel.graphics.xobject.PDXObjectForm;
+import org.pdfbox.util.Matrix;
+import org.pdfbox.util.PDFOperator;
+import org.pdfbox.util.PDFTextStripper;
+import org.pdfbox.util.TextPosition;
+import org.pdfbox.util.operator.OperatorProcessor;
+
+import at.knowcenter.wag.egov.egiz.cfg.ConfigLogger;
+
+/**
+ * PDFPage is an inner class that is used to calculate the page length of a PDF
+ * Document page. It extends the PDFTextStripper class and implement one
+ * interested method: {@link PDFPage#showCharacter(TextPosition)}<br>
+ * This method is called when processing the FileStream. By calling the method
+ * {@link org.pdfbox.util.PDFStreamEngine#processStream(org.pdfbox.pdmodel.PDPage, org.pdfbox.pdmodel.PDResources, org.pdfbox.cos.COSStream)}
+ * the implemented method showCharacter is called.
+ *
+ * @author wlackner
+ * @see PDFTextStripper
+ */
+public class PDFPage extends PDFTextStripper
+{
+ /**
+ * The logger definition.
+ */
+ private static final Logger logger_ = ConfigLogger.getLogger(PDFPage.class);
+
+ /**
+ * The maximum (lowest) y position of a character.
+ */
+ protected float max_character_ypos = Float.NEGATIVE_INFINITY;
+
+ /**
+ * The maximum (lowest y position of an image.
+ */
+ protected float max_image_ypos = Float.NEGATIVE_INFINITY;
+
+ /**
+ * The empty constructor.
+ *
+ * @throws IOException
+ */
+ public PDFPage() throws IOException
+ {
+ super();
+
+ OperatorProcessor newInvoke = new MyInvoke();
+ newInvoke.setContext(this);
+ operators.put("Do", newInvoke);
+ }
+
+ // /**
+ // * You should override this method if you want to perform an action when a
+ // * string is being shown.
+ // *
+ // * @param string The string to display.
+ // *
+ // * @throws IOException If there is an error showing the string
+ // */
+ // public void showString( byte[] string ) throws IOException
+ // {
+ // float spaceWidth = 0;
+ // float spacing = 0;
+ // StringBuffer stringResult = new StringBuffer(string.length);
+ //
+ // float characterDisplacement = 0;
+ // float spaceDisplacement = 0;
+ //
+ // PDGraphicsState graphicsState = getGraphicsState();
+ // float fontSize = graphicsState.getTextState().getFontSize();
+ // float horizontalScaling =
+ // graphicsState.getTextState().getHorizontalScalingPercent()/100f;
+ // float rise = graphicsState.getTextState().getRise();
+ // final float wordSpacing = graphicsState.getTextState().getWordSpacing();
+ // final float characterSpacing =
+ // graphicsState.getTextState().getCharacterSpacing();
+ // float wordSpacingDisplacement = 0;
+ //
+ // PDFont font = graphicsState.getTextState().getFont();
+ //
+ // //This will typically be 1000 but in the case of a type3 font
+ // //this might be a different number
+ // float glyphSpaceToTextSpaceFactor = 1f/font.getFontMatrix().getValue( 0, 0
+ // );
+ // Float averageWidth = (Float)fontToAverageWidths.get( font );
+ // if( averageWidth == null )
+ // {
+ // averageWidth = new Float( font.getAverageFontWidth() );
+ // fontToAverageWidths.put( font, averageWidth );
+ // }
+ //
+ // Matrix initialMatrix = new Matrix();
+ // initialMatrix.setValue(0,0,1);
+ // initialMatrix.setValue(0,1,0);
+ // initialMatrix.setValue(0,2,0);
+ // initialMatrix.setValue(1,0,0);
+ // initialMatrix.setValue(1,1,1);
+ // initialMatrix.setValue(1,2,0);
+ // initialMatrix.setValue(2,0,0);
+ // initialMatrix.setValue(2,1,rise);
+ // initialMatrix.setValue(2,2,1);
+ //
+ //
+ // //this
+ // int codeLength = 1;
+ // Matrix ctm = graphicsState.getCurrentTransformationMatrix();
+ //
+ // //lets see what the space displacement should be
+ // spaceDisplacement = (font.getFontWidth( SPACE_BYTES, 0, 1
+ // )/glyphSpaceToTextSpaceFactor);
+ // if( spaceDisplacement == 0 )
+ // {
+ // spaceDisplacement =
+ // (averageWidth.floatValue()/glyphSpaceToTextSpaceFactor);
+ // //The average space width appears to be higher than necessary
+ // //so lets make it a little bit smaller.
+ // spaceDisplacement *= .80f;
+ // if( log.isDebugEnabled() )
+ // {
+ // log.debug( "Font: Space From Average=" + spaceDisplacement );
+ // }
+ // }
+ // int pageRotation = page.findRotation();
+ //
+ // // very strange.... the ctms are multiplied by right, but suddenly the
+ // textM is multiplied from the left.
+ // // but: PDF matrices are multiplied from left ==> ctm is wrong
+ // Matrix trm = initialMatrix.multiply( textMatrix ).multiply( ctm );
+ // float x = trm.getValue(2,0);
+ // float y = trm.getValue(2,1);
+ // float flipped_y = -y + page.findMediaBox().getHeight();
+ // if( pageRotation == 0 )
+ // {
+ // trm.setValue( 2,1, flipped_y );
+ // }
+ // else if( pageRotation == 90 )
+ // {
+ // trm.setValue( 2,0, y );
+ // trm.setValue( 2,1, x );
+ // }
+ // else if( pageRotation == 270 )
+ // {
+ // trm.setValue( 2,0, flipped_y );
+ // trm.setValue( 2,1, x );
+ // }
+ // for( int i=0; i<string.length; i+=codeLength )
+ // {
+ // if( log.isDebugEnabled() )
+ // {
+ // log.debug( "initialMatrix=" + initialMatrix );
+ // log.debug( "textMatrix=" + textMatrix );
+ // log.debug( "initialMatrix.multiply( textMatrix )=" +
+ // initialMatrix.multiply( textMatrix ) );
+ // log.debug( "ctm=" + ctm );
+ // log.debug( "trm=" + initialMatrix.multiply( textMatrix ).multiply( ctm ) );
+ // }
+ // codeLength = 1;
+ //
+ // String c = font.encode( string, i, codeLength );
+ //
+ // if( log.isDebugEnabled() )
+ // {
+ // log.debug( "Character Code=" + string[i] + "='" + c + "'" );
+ // }
+ // if( c == null && i+1<string.length)
+ // {
+ // //maybe a multibyte encoding
+ // codeLength++;
+ // if( log.isDebugEnabled() )
+ // {
+ // log.debug( "Multibyte Character Code=" + string[i] + string[i+1] );
+ // }
+ // c = font.encode( string, i, codeLength );
+ // }
+ // stringResult.append( c );
+ //
+ // //todo, handle horizontal displacement
+ // characterDisplacement += (font.getFontWidth( string, i, codeLength
+ // )/glyphSpaceToTextSpaceFactor);
+ //
+ //
+ // // PDF Spec - 5.5.2 Word Spacing
+ // //
+ // // Word spacing works the same was as character spacing, but applies
+ // // only to the space character, code 32.
+ // //
+ // // Note: Word spacing is applied to every occurrence of the single-byte
+ // // character code 32 in a string. This can occur when using a simple
+ // // font or a composite font that defines code 32 as a single-byte code.
+ // // It does not apply to occurrences of the byte value 32 in multiple-byte
+ // // codes.
+ // //
+ // // RDD - My interpretation of this is that only character code 32's that
+ // // encode to spaces should have word spacing applied. Cases have been
+ // // observed where a font has a space character with a character code
+ // // other than 32, and where word spacing (Tw) was used. In these cases,
+ // // applying word spacing to either the non-32 space or to the character
+ // // code 32 non-space resulted in errors consistent with this
+ // interpretation.
+ // //
+ //
+ // boolean withCS = false;
+ // if( (string[i] == 0x20) && c.equals( " " ) )
+ // {
+ // spacing += wordSpacing + characterSpacing;
+ // withCS = true;
+ // }
+ // else
+ // {
+ // spacing += characterSpacing;
+ // }
+ //
+ // if( log.isDebugEnabled() )
+ // {
+ // log.debug( "Checking code '" + c + "' font=" +
+ // graphicsState.getTextState().getFont() +
+ // " Tc=" + characterSpacing +
+ // " Tw=" + wordSpacing +
+ // " fontSize=" + fontSize +
+ // " horizontalScaling=" + horizontalScaling +
+ // " totalDisp=" + characterDisplacement +
+ // " spacing=" + spacing + "(" + withCS + ")" );
+ // }
+ // // We want to update the textMatrix using the width, in text space units.
+ // //
+ //
+ // }
+ //
+ // //The adjustment will always be zero. The adjustment as shown in the
+ // //TJ operator will be handled separately.
+ // float adjustment=0;
+ // //todo, need to compute the horizontal displacement
+ // float ty = 0;
+ // float tx =
+ // ((characterDisplacement-adjustment/glyphSpaceToTextSpaceFactor)*fontSize +
+ // spacing)
+ // *horizontalScaling;
+ //
+ // if( log.isDebugEnabled() )
+ // {
+ // log.debug( "disp=" + characterDisplacement + " adj=" + adjustment +
+ // " fSize=" + fontSize + " tx=" + tx );
+ // }
+ //
+ // float xScale = trm.getXScale();
+ // float yScale = trm.getYScale();
+ // float xPos = trm.getXPosition();
+ // float yPos = trm.getYPosition();
+ // spaceWidth = spaceDisplacement * xScale * fontSize;
+ // wordSpacingDisplacement = wordSpacing*xScale * fontSize;
+ // Matrix td = new Matrix();
+ // td.setValue( 2, 0, tx );
+ // td.setValue( 2, 1, ty );
+ //
+ // if( log.isDebugEnabled() )
+ // {
+ // log.debug( "TRM=" + trm );
+ // log.debug( "TextMatrix before " + textMatrix );
+ // }
+ // float xPosBefore = textMatrix.getXPosition();
+ // float yPosBefore = textMatrix.getYPosition();
+ // textMatrix = td.multiply( textMatrix );
+ // if( log.isDebugEnabled() )
+ // {
+ // log.debug( "TextMatrix after " + textMatrix );
+ // }
+ // float totalStringDisplacement = 0;
+ // if( pageRotation == 0 )
+ // {
+ // totalStringDisplacement = (textMatrix.getXPosition() - xPosBefore);
+ // }
+ // else if( pageRotation == 90 )
+ // {
+ // totalStringDisplacement = (textMatrix.getYPosition() - yPosBefore);
+ // }
+ // else if( pageRotation == 270 )
+ // {
+ // totalStringDisplacement = (yPosBefore - textMatrix.getYPosition());
+ // }
+ // showCharacter(
+ // new TextPosition(
+ // xPos,
+ // yPos,
+ // xScale,
+ // yScale,
+ // totalStringDisplacement,
+ // spaceWidth,
+ // stringResult.toString(),
+ // graphicsState.getTextState().getFont(),
+ // graphicsState.getTextState().getFontSize(),
+ // wordSpacingDisplacement ));
+ // }
+ //
+
+ protected void processOperator(PDFOperator operator, List arguments) throws IOException
+ {
+ logger_.debug("operator = " + operator);
+
+ super.processOperator(operator, arguments);
+ }
+
+ /**
+ * A method provided as an event interface to allow a subclass to perform some
+ * specific functionality when a character needs to be displayed. This method
+ * is used to calculate the latest position of a text in the page. Sorry for
+ * this missinterpretation of the method, but it is the only way to do this
+ * (provided by PDFBox)!!!
+ *
+ * @param text
+ * the character to be displayed -> calculate there y position.
+ */
+ protected void showCharacter(TextPosition text)
+ {
+ float current_y = text.getY();
+ String character = text.getCharacter();
+ // store ypos of the char if it is not empty
+ if (!character.equals(" ") && current_y > this.max_character_ypos)
+ {
+ this.max_character_ypos = current_y;
+ //logger_.debug("text.character=" + character + ", y=" + current_y);
+ // System.err.println(character + "|" + current_y);
+ }
+
+ // logger_.debug("text.character=" + character + ", y=" + current_y);
+ // System.err.println(character + "|" + current_y);
+ }
+
+ // use this funtion getting an unsorted text output
+ // public void showString(byte[] string) {
+ // logger_.debug(new String(string));
+ // }
+
+ /**
+ * Returns the calculated page length.
+ *
+ * @return the max page length value
+ */
+ public float getMaxPageLength()
+ {
+ float max_ypos = Float.NEGATIVE_INFINITY;
+
+ if (this.max_character_ypos > this.max_image_ypos)
+ {
+ max_ypos = this.max_character_ypos;
+ }
+ else
+ {
+ max_ypos = this.max_image_ypos;
+ }
+
+ return max_ypos;
+ }
+
+ public class MyInvoke extends OperatorProcessor
+ {
+
+ public void process(PDFOperator operator, List arguments) throws IOException
+ {
+ COSName name = (COSName) arguments.get(0);
+ logger_.debug("<Do name=\"" + name.getName() + "\">");
+
+ // PDResources res = context.getResources();
+
+ Map xobjects = context.getXObjects();
+ PDXObject xobject = (PDXObject) xobjects.get(name.getName());
+
+ PDStream stream = xobject.getPDStream();
+ COSStream cos_stream = stream.getStream();
+
+ COSName subtype = (COSName) cos_stream.getDictionaryObject(COSName.SUBTYPE);
+ if (subtype.equals(COSName.IMAGE))
+ {
+ logger_.debug("XObject Image");
+
+ Matrix ctm = context.getGraphicsState().getCurrentTransformationMatrix();
+ logger_.debug("ctm = " + ctm);
+
+ Pos [] coordinates = new Pos [] {
+ new Pos(0, 0, 1),
+ new Pos(1, 0, 1),
+ new Pos(0, 1, 1),
+ new Pos(1, 1, 1) };
+
+ Pos [] transformed_coordinates = transtormCoordinates(coordinates, ctm);
+
+ float actual_lowest_point = Float.NaN;
+
+ int pageRotation = page.findRotation();
+ logger_.debug("PageRotation = " + pageRotation);
+ if (pageRotation == 0)
+ {
+ float min_y = findMinY(transformed_coordinates);
+ logger_.debug("min_y = " + min_y);
+ float page_height = page.findMediaBox().getHeight();
+ logger_.debug("page_height = " + page_height);
+
+ actual_lowest_point = page_height - min_y;
+ }
+ if (pageRotation == 90)
+ {
+ float max_x = findMaxX(transformed_coordinates);
+ logger_.debug("max_x = " + max_x);
+// float page_width = page.findMediaBox().getWidth();
+// logger_.debug("page_width = " + page_width);
+
+ actual_lowest_point = max_x;
+ }
+ if (pageRotation == 180)
+ {
+ float min_y = findMinY(transformed_coordinates);
+ logger_.debug("min_y = " + min_y);
+ actual_lowest_point = min_y;
+ }
+ if (pageRotation == 270)
+ {
+ float min_x = findMinX(transformed_coordinates);
+ logger_.debug("min_x = " + min_x);
+// float page_width = page.findMediaBox().getWidth();
+// logger_.debug("page_width = " + page_width);
+
+ actual_lowest_point = min_x;
+ }
+
+
+ logger_.debug("actual_lowest_point = " + actual_lowest_point);
+
+ if (actual_lowest_point > PDFPage.this.max_image_ypos)
+ {
+ PDFPage.this.max_image_ypos = actual_lowest_point;
+ }
+
+ return;
+ }
+
+ if (xobject instanceof PDXObjectForm)
+ {
+ PDXObjectForm form = (PDXObjectForm) xobject;
+ COSStream invoke = (COSStream) form.getCOSObject();
+ PDResources pdResources = form.getResources();
+ PDPage page = context.getCurrentPage();
+ if (pdResources == null)
+ {
+ pdResources = page.findResources();
+ }
+
+ getContext().processSubStream(page, pdResources, invoke);
+ }
+ }
+ }
+
+ public static Pos [] transtormCoordinates (Pos [] coordinates, Matrix m)
+ {
+ Pos [] transformed = new Pos [coordinates.length];
+ for (int i = 0; i < coordinates.length; i++)
+ {
+ transformed[i] = transtormCoordinate(coordinates[i], m);
+ }
+ return transformed;
+ }
+
+ public static Pos transtormCoordinate (Pos pos, Matrix m)
+ {
+ Pos transformed = new Pos();
+ transformed.x = pos.x * m.getValue(0, 0) + pos.y * m.getValue(1, 0) + pos.z * m.getValue(2, 0);
+ transformed.y = pos.x * m.getValue(0, 1) + pos.y * m.getValue(1, 1) + pos.z * m.getValue(2, 1);
+ transformed.z = pos.x * m.getValue(0, 2) + pos.y * m.getValue(1, 2) + pos.z * m.getValue(2, 2);
+
+ logger_.debug(" transformed " + pos + " --> " + transformed);
+ return transformed;
+ }
+
+ public static float findMinY (Pos [] coordinates)
+ {
+ float min = Float.POSITIVE_INFINITY;
+ for (int i = 0; i < coordinates.length; i++)
+ {
+ if (coordinates[i].y < min)
+ {
+ min = coordinates[i].y;
+ }
+ }
+ return min;
+ }
+ public static float findMaxX (Pos [] coordinates)
+ {
+ float max = Float.NEGATIVE_INFINITY;
+ for (int i = 0; i < coordinates.length; i++)
+ {
+ if (coordinates[i].x > max)
+ {
+ max = coordinates[i].x;
+ }
+ }
+ return max;
+ }
+ public static float findMinX (Pos [] coordinates)
+ {
+ float min = Float.POSITIVE_INFINITY;
+ for (int i = 0; i < coordinates.length; i++)
+ {
+ if (coordinates[i].x < min)
+ {
+ min = coordinates[i].x;
+ }
+ }
+ return min;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureCreation.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureCreation.java
new file mode 100644
index 0000000..453103b
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureCreation.java
@@ -0,0 +1,170 @@
+/*
+ * <copyright>
+ * Copyright (c) 2006 by Know-Center, Graz, Austria
+ * </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ * $Id: PDFSignatureCreation.java,v 1.6 2006/10/31 08:09:33 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+import at.knowcenter.wag.egov.egiz.cfg.ConfigLogger;
+import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
+import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
+import at.knowcenter.wag.egov.egiz.exceptions.SettingsException;
+import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
+
+/**
+ * This class provides wrapper methods to get an access to abstract PDF documents (PDFSignator).
+ * There exists many open source libraries and commercial libraries that can implement the abstract
+ * interface. <br>
+ * This class is to load the corresponding implementation of an abstract PDFSignator class. Therefor
+ * it seams to be a factory. The factory settings are read from the configuration file calling the
+ * SettingsReader.
+ *
+ * @author wlackner
+ * @see at.knowcenter.wag.egov.egiz.cfg.SettingsReader
+ */
+public class PDFSignatureCreation {
+ /**
+ * The abstract signature object
+ */
+ private SignatureObject sigObject_ = null;
+ /**
+ * The abstract pdf siganture object
+ */
+ private PDFSignatureObject pdfSigObject_ = null;
+ /**
+ * The SettingsReader instance
+ */
+ private SettingsReader settings_ = null;
+ /**
+ * The factory class prefix
+ */
+ private final static String CLASS_PREFIX = ".PDFSignatureObject";
+ /**
+ * The factory class prefix of the default library
+ */
+ protected final static String DEFAULT_LIBRARY = "IText";
+ /**
+ * The settings key defined in the settings file
+ *
+ * @see SettingsReader
+ */
+ protected final static String SETTINGS_KEY = "pdf.signature.library";
+ /**
+ * The logger definition.
+ */
+ private static final Logger logger_ = ConfigLogger.getLogger(PDFSignatureCreation.class);
+
+ /**
+ * Load the configuration settings. Load the corresponding class implementation for the abstract
+ * PDFSignature class. Init with a signature object.
+ *
+ * @param sigObject the native signature object
+ * @throws PDFDocumentException ErrorCode:101
+ */
+ public PDFSignatureCreation(SignatureObject sigObject) throws PDFDocumentException {
+ try {
+ loadSettings();
+ } catch (PDFDocumentException e) {
+ e.setErrorCode(101);
+ throw e;
+ }
+ sigObject_ = sigObject;
+ }
+
+ /**
+ * Load the factory implementation. This method trys to load the configured PDF library.
+ *
+ * @throws PDFDocumentException
+ */
+ private PDFSignatureObject createPDFSignatureObject() throws PDFDocumentException {
+ PDFSignatureObject pdf_sig_object = null;
+ String class_name = this.getClass().getPackage().getName() + getClassName();
+ Class pdf_sig_obj_class = null;
+ try {
+ pdf_sig_obj_class = Class.forName(class_name);
+ } catch (ClassNotFoundException e) {
+ if (logger_.isEnabledFor(Level.FATAL)) {
+ logger_.fatal("Class not found:" + class_name);
+ }
+ throw new PDFDocumentException(203, "Can not load pdf signator library", e);
+ }
+ try {
+ pdf_sig_object = (PDFSignatureObject) pdf_sig_obj_class.newInstance();
+ } catch (InstantiationException e) {
+ if (logger_.isEnabledFor(Level.FATAL)) {
+ logger_.fatal("Can not instantiate:" + class_name);
+ }
+ throw new PDFDocumentException(203, "Can not load pdf signator library", e);
+ } catch (IllegalAccessException e) {
+ if (logger_.isEnabledFor(Level.FATAL)) {
+ logger_.fatal("Can not access:" + class_name);
+ }
+ throw new PDFDocumentException(203, "Can not load pdf signator library", e);
+ }
+ return pdf_sig_object;
+ }
+
+ /**
+ * load the class settings
+ *
+ * @throws PDFDocumentException
+ * @see SettingsReader
+ */
+ private void loadSettings() throws PDFDocumentException {
+ if (settings_ == null) {
+ try {
+ settings_ = SettingsReader.getInstance();
+ } catch (SettingsException e) {
+ String log_message = "Can not load pdf signature settings. Cause:\n" + e.getMessage();
+ logger_.error(log_message);
+ throw new PDFDocumentException(101, log_message, e);
+ }
+ }
+ }
+
+ /**
+ * Read the class postfix from the configuration file
+ *
+ * @return the full qualified class name
+ */
+ private String getClassName() {
+ String extract_class = settings_.getSetting(SETTINGS_KEY, DEFAULT_LIBRARY);
+ return CLASS_PREFIX + extract_class;
+ }
+
+ /**
+ * Creates a new pdf signature object using the configured pdf library.
+ *
+ * @return a new pdf signature object
+ * @throws PDFDocumentException ErrorCode:203
+ */
+ public PDFSignatureObject getPDFSignatureObject() throws PDFDocumentException {
+ if (pdfSigObject_ == null) {
+ try {
+ pdfSigObject_ = createPDFSignatureObject();
+ } catch (PDFDocumentException e) {
+ e.setErrorCode(203);
+ throw e;
+ }
+ pdfSigObject_.setSignatorObject(sigObject_);
+ }
+ return pdfSigObject_;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObject.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObject.java
new file mode 100644
index 0000000..3584820
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObject.java
@@ -0,0 +1,50 @@
+/*
+ * <copyright>
+ * Copyright (c) 2006 by Know-Center, Graz, Austria
+ * </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ * $Id: PDFSignatureObject.java,v 1.3 2006/10/31 08:09:33 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
+import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
+
+/**
+ * Defines an interface to get access to PDF documents. There exists many open source libraries and
+ * commercial libraries.
+ *
+ * @author wlackner
+ */
+public interface PDFSignatureObject {
+ public void setSignatorObject(SignatureObject signatorObject);
+
+ /**
+ * Converts the current abstract signature object in a pdf signature object implementation
+ *
+ * @return the converted pdf signature object
+ * @throws PDFDocumentException
+ */
+ public Object getSignatureObject() throws PDFDocumentException;
+
+ /**
+ * Converts a abstract signature object in a pdf signature object implementation
+ *
+ * @param signatorObject the abstract signatorObject to convert
+ * @return the converted pdf signature object
+ * @throws PDFDocumentException
+ */
+ public Object getSignatureObject(SignatureObject signatorObject) throws PDFDocumentException;
+} \ No newline at end of file
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObjectIText.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObjectIText.java
new file mode 100644
index 0000000..56b5f86
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObjectIText.java
@@ -0,0 +1,490 @@
+/*
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: PDFSignatureObjectIText.java,v 1.5 2006/10/31 08:09:33 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+import at.knowcenter.wag.egov.egiz.cfg.ConfigLogger;
+import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
+import at.knowcenter.wag.egov.egiz.exceptions.ErrorCodeException;
+import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
+import at.knowcenter.wag.egov.egiz.exceptions.SettingsException;
+import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
+import at.knowcenter.wag.egov.egiz.table.Entry;
+import at.knowcenter.wag.egov.egiz.table.Style;
+import at.knowcenter.wag.egov.egiz.table.Table;
+
+import com.lowagie.text.BadElementException;
+import com.lowagie.text.Element;
+import com.lowagie.text.Font;
+import com.lowagie.text.Image;
+import com.lowagie.text.Phrase;
+import com.lowagie.text.pdf.PdfPCell;
+import com.lowagie.text.pdf.PdfPTable;
+
+/**
+ * This class is the IText implementation of the PDFSignatureObject interface.
+ * The class takes an abstract definition of a signature object and convert them
+ * into a pdf table that is used to sign a pdf document.
+ *
+ * @author wlackner
+ * @see at.knowcenter.wag.egov.egiz.sig.SignatureObject
+ * @see at.knowcenter.wag.egov.egiz.table.Table
+ * @see at.knowcenter.wag.egov.egiz.table.Entry
+ * @see at.knowcenter.wag.egov.egiz.table.Style
+ * @see com.lowagie.text.pdf.PdfPTable
+ * @see at.knowcenter.wag.egov.egiz.cfg.SettingsReader
+ */
+public class PDFSignatureObjectIText implements PDFSignatureObject
+{
+
+ /**
+ * The default font definition
+ */
+ private static Font DEFAULT_FONT = new Font(Font.HELVETICA, 8, Font.NORMAL);
+
+ /**
+ * The abstract signature object
+ */
+ private SignatureObject sigObject_ = null;
+
+ /**
+ * The IText pdf table object
+ */
+ private PdfPTable pdfSigObject_ = null;
+
+ /**
+ * The SettingsReader instance
+ */
+ private SettingsReader settings_ = null;
+
+ /**
+ * The logger definition.
+ */
+ private static final Logger logger_ = ConfigLogger.getLogger(PDFSignatureObjectIText.class);
+
+ /**
+ * Map the style align definitions to IText's align statements
+ */
+ private static HashMap alignMap_ = new HashMap();
+
+ /**
+ * Map the font definitions to IText's font statements
+ */
+ private static HashMap fontMap_ = new HashMap();
+
+ /**
+ * The empty constructor. It loads the ui definitions from signature tables
+ * and init the align map.
+ *
+ * @throws PDFDocumentException
+ */
+ public PDFSignatureObjectIText() throws PDFDocumentException
+ {
+ loadSettings();
+ initStyleMaps();
+ }
+
+ /**
+ * load the class settings
+ *
+ * @throws PDFDocumentException
+ * @see SettingsReader
+ */
+ private void loadSettings() throws PDFDocumentException
+ {
+ if (settings_ == null)
+ {
+ try
+ {
+ settings_ = SettingsReader.getInstance();
+ }
+ catch (SettingsException e)
+ {
+ String log_message = "Can not load pdf signature settings. Cause:\n" + e.getMessage();
+ logger_.error(log_message);
+ throw new PDFDocumentException(101, log_message, e);
+ }
+ }
+ }
+
+ /**
+ * This method initialize the style maps. It maps the style style definitions
+ * to IText styles.
+ */
+ private void initStyleMaps()
+ {
+ alignMap_.put(Style.TOP, new Integer(Element.ALIGN_TOP));
+ alignMap_.put(Style.MIDDLE, new Integer(Element.ALIGN_MIDDLE));
+ alignMap_.put(Style.BOTTOM, new Integer(Element.ALIGN_BOTTOM));
+ alignMap_.put(Style.LEFT, new Integer(Element.ALIGN_LEFT));
+ alignMap_.put(Style.CENTER, new Integer(Element.ALIGN_CENTER));
+ alignMap_.put(Style.RIGHT, new Integer(Element.ALIGN_RIGHT));
+
+ fontMap_.put(Style.HELVETICA, new Integer(Font.HELVETICA));
+ fontMap_.put(Style.TIMES_ROMAN, new Integer(Font.TIMES_ROMAN));
+ fontMap_.put(Style.COURIER, new Integer(Font.COURIER));
+ fontMap_.put(Style.NORMAL, new Integer(Font.NORMAL));
+ fontMap_.put(Style.BOLD, new Integer(Font.BOLD));
+ fontMap_.put(Style.ITALIC, new Integer(Font.ITALIC));
+ fontMap_.put(Style.BOLDITALIC, new Integer(Font.BOLDITALIC));
+ fontMap_.put(Style.UNDERLINE, new Integer(Font.UNDERLINE));
+ fontMap_.put(Style.STRIKETHRU, new Integer(Font.STRIKETHRU));
+ }
+
+ /**
+ * Set the abstract signature definition.
+ *
+ * @param signatorObject
+ * the abstract signator object
+ * @see at.knowcenter.wag.egov.egiz.pdf.PDFSignatureObject#setSignatorObject(at.knowcenter.wag.egov.egiz.sig.SignatureObject)
+ */
+ public void setSignatorObject(SignatureObject signatorObject)
+ {
+ sigObject_ = signatorObject;
+ }
+
+ /**
+ * This method maps the table cell definitions to the pdfCell element.
+ *
+ * @param pdfCell
+ * the pdf cell to be styled
+ * @param cellStyle
+ * the abstract style definition
+ * @see com.lowagie.text.pdf.PdfPCell
+ * @see at.knowcenter.wag.egov.egiz.table.Style
+ */
+ private void setCellStyle(PdfPCell pdfCell, Style cellStyle)
+ {
+ if (cellStyle != null)
+ {
+ if (cellStyle.getBgColor() != null)
+ {
+ pdfCell.setBackgroundColor(cellStyle.getBgColor());
+ }
+ pdfCell.setPadding(cellStyle.getPadding());
+ if (cellStyle.getBorder() > 0)
+ {
+ pdfCell.setBorderWidth(cellStyle.getBorder());
+ }
+ else
+ {
+ pdfCell.setBorder(0);
+ }
+ if (cellStyle.getVAlign() != null)
+ {
+ int align = ((Integer) alignMap_.get(cellStyle.getVAlign())).intValue();
+ pdfCell.setVerticalAlignment(align);
+ }
+ if (cellStyle.getHAlign() != null)
+ {
+ int align = ((Integer) alignMap_.get(cellStyle.getHAlign())).intValue();
+ pdfCell.setHorizontalAlignment(align);
+ }
+ }
+ }
+
+ /**
+ * This method maps the cell font definition to the iText Font Object
+ *
+ * @param fontString
+ * @return the corresponding iText Font Object
+ * @see com.lowagie.text.Font
+ */
+ private Font getCellFont(String fontString)
+ {
+ Font font = DEFAULT_FONT;
+ if (fontString == null)
+ {
+ return font;
+ }
+ Object cache_font = fontMap_.get(fontString);
+ if (cache_font != null)
+ {
+ return (Font) cache_font;
+ }
+ String[] font_arr = fontString.split(",");
+ if (font_arr.length != 3)
+ {
+ return font;
+ }
+ Object font_face = fontMap_.get(font_arr[0]);
+ if (font_face == null)
+ {
+ return font;
+ }
+ Object font_weight = fontMap_.get(font_arr[2]);
+ if (font_weight == null)
+ {
+ return font;
+ }
+ int face = ((Integer) font_face).intValue();
+ float height = Float.parseFloat(font_arr[1]);
+ int weight = ((Integer) font_weight).intValue();
+
+ font = new Font(face, height, weight);
+ fontMap_.put(fontString, font);
+ return font;
+ }
+
+ /**
+ * This method visualize an abstract table cell into a corresponding pdf table
+ * cell. The new pdf table cell is redered and get the style information from
+ * the abstract cell. Following types can be rendered:
+ * <ul>
+ * <li>text statements</li>
+ * <li>images</li>
+ * <li>tables</li>
+ * </ul>
+ *
+ * @param abstractCell
+ * the abstract cell definition
+ * @return the new redererd pdf table cell
+ * @throws PDFDocumentException
+ * ErrorCode:220, 221, 222
+ * @see com.lowagie.text.pdf.PdfPCell
+ * @see at.knowcenter.wag.egov.egiz.table.Entry
+ */
+ private PdfPCell renderCell(Entry abstractCell) throws PDFDocumentException
+ {
+ PdfPCell pdf_cell = null;
+ Style cell_style = abstractCell.getStyle();
+ switch (abstractCell.getType())
+ {
+ case Entry.TYPE_CAPTION:
+ case Entry.TYPE_VALUE:
+ String text = (String) abstractCell.getValue();
+ if (text == null)
+ {
+ text = "";
+ }
+ String font_string = cell_style.getFont();
+ if (abstractCell.getType() == Entry.TYPE_VALUE && cell_style.getValueFont() != null)
+ {
+ font_string = cell_style.getValueFont();
+ }
+ Font cell_font = getCellFont(font_string);
+ Phrase text_phrase = new Phrase(text, cell_font);
+ pdf_cell = new PdfPCell(text_phrase);
+ setCellStyle(pdf_cell, cell_style);
+ break;
+ case Entry.TYPE_IMAGE:
+ try
+ {
+ String img_ref = (String) abstractCell.getValue();
+ String img_location = SettingsReader.relocateFile(img_ref);
+ File img_file = new File (img_location);
+ if (!img_file.exists())
+ {
+ logger_.debug("Image file \"" + img_file.getCanonicalPath() + "\" doesn't exist.");
+ throw new PDFDocumentException(220, "Image file \"" + img_file.getCanonicalPath() + "\" doesn't exist.");
+ }
+ Image image = Image.getInstance(img_location);
+ pdf_cell = new PdfPCell(image, true);
+ setCellStyle(pdf_cell, cell_style);
+ }
+ catch (BadElementException e)
+ {
+ if (logger_.isDebugEnabled())
+ {
+ if (logger_.isDebugEnabled())
+ {
+ e.printStackTrace();
+ }
+ }
+ if (logger_.isEnabledFor(Level.ERROR))
+ {
+ logger_.error("BadElementException:" + e.getMessage());
+ }
+ PDFDocumentException pde = new PDFDocumentException(220, "PDF table can not created");
+ throw pde;
+ }
+ catch (MalformedURLException e)
+ {
+ if (logger_.isDebugEnabled())
+ {
+ e.printStackTrace();
+ }
+ if (logger_.isEnabledFor(Level.ERROR))
+ {
+ logger_.error("MalformedURLException:" + e.getMessage());
+ }
+ PDFDocumentException pde = new PDFDocumentException(221, "PDF table can not created");
+ throw pde;
+ }
+ catch (IOException e)
+ {
+ if (logger_.isDebugEnabled())
+ {
+ e.printStackTrace();
+ }
+ if (logger_.isEnabledFor(Level.ERROR))
+ {
+ logger_.error("Error Code:222 " + ErrorCodeException.getErrorCodeMessage(222) + ". IOException:" + e.getMessage());
+ }
+ PDFDocumentException pde = new PDFDocumentException(222, "PDF table can not created: Image can not loaded");
+ throw pde;
+ }
+ break;
+ case Entry.TYPE_TABLE:
+ Table table = (Table) abstractCell.getValue();
+ // inherit the style from the parent table
+ Style inherit_style = Style.doInherit(table.getStyle(), cell_style);
+ table.setStyle(inherit_style);
+ PdfPTable pdf_table = renderTable(table);
+ pdf_cell = new PdfPCell(pdf_table);
+ break;
+ }
+ return pdf_cell;
+ }
+
+ /**
+ * This method visualize an abstract table into a corresponding pdf table. The
+ * new pdf table is redered and get the style information from the abstract
+ * cell.
+ *
+ * @param abstractTable
+ * the abstract table definition
+ * @return the new redererd pdf table cell
+ * @throws PDFDocumentException
+ * ErrorCode:220, 221, 222, 223
+ * @see com.lowagie.text.pdf.PdfPTable
+ * @see at.knowcenter.wag.egov.egiz.table.Table
+ */
+ private PdfPTable renderTable(Table abstractTable) throws PDFDocumentException
+ {
+ if (abstractTable == null)
+ {
+ PDFDocumentException pde = new PDFDocumentException(223, "Table is not defined.");
+ throw pde;
+ }
+ PdfPTable pdf_table = null;
+ float[] cols = abstractTable.getColsRelativeWith();
+ int max_cols = abstractTable.getMaxCols();
+ if (cols == null)
+ {
+ cols = new float[max_cols];
+ // set the column ratio for all columns to 1
+ for (int cols_idx = 0; cols_idx < cols.length; cols_idx++)
+ {
+ cols[cols_idx] = 1;
+ }
+ }
+ pdf_table = new PdfPTable(cols);
+ pdf_table.setWidthPercentage(abstractTable.getWidth());
+ Style table_style = abstractTable.getStyle();
+ setCellStyle(pdf_table.getDefaultCell(), table_style);
+
+ ArrayList rows = abstractTable.getRows();
+ for (int row_idx = 0; row_idx < rows.size(); row_idx++)
+ {
+ ArrayList row = (ArrayList) rows.get(row_idx);
+ // logger_.debug("## Row:" + row_idx + " ## of table:" +
+ // abstractTable.getName());
+ for (int entry_idx = 0; entry_idx < row.size(); entry_idx++)
+ {
+ Entry cell = (Entry) row.get(entry_idx);
+ Style inherit_style = Style.doInherit(cell.getStyle(), table_style);
+ cell.setStyle(inherit_style);
+ // logger_.debug(cell.toString());
+ PdfPCell pdf_cell = renderCell(cell);
+ if (cell.getColSpan() > 1)
+ {
+ pdf_cell.setColspan(cell.getColSpan());
+ }
+ if (cell.isNoWrap())
+ {
+ pdf_cell.setNoWrap(true);
+ }
+ // System.err.println("valign:" + pdf_cell.getVerticalAlignment() + "
+ // halign:" +
+ // pdf_cell.getHorizontalAlignment());
+ pdf_table.addCell(pdf_cell);
+ }
+ }
+ // logger_.debug("render table:" + abstractTable.getName());
+ return pdf_table;
+ }
+
+ /**
+ * This method creates the pdf table object. It takes the abstract table
+ * definition from the signature object and render the abstract table.
+ *
+ * @param sigObject
+ * the signature object, the base for the abstract table definition
+ * @return R
+ * @throws PDFDocumentException
+ * ErrorCode:220, 221, 222, 223
+ */
+ private PdfPTable createPDFSignatureObject(SignatureObject sigObject) throws PDFDocumentException
+ {
+ Table table = sigObject.getAbstractTable();
+ PdfPTable pdf_table = renderTable(table);
+ return pdf_table;
+ }
+
+ /*
+ * This method search for the table definitions in the settings file an init
+ * @param sigObject
+ */
+ /*
+ * private void initTableSettings(SignatureObject sigObject) { String sig_type =
+ * sigObject.getSignationType(); String table_key = SignatureObject.SIG_OBJ +
+ * sig_type + ".table."; ArrayList main_rows = settings_.getKeys(table_key +
+ * "main"); }
+ */
+
+ /**
+ * Converts the current abstract signature object in a pdf signature object
+ * implementation
+ *
+ * @return the converted pdf signature object
+ * @see at.knowcenter.wag.egov.egiz.pdf.PDFSignatureObject#getSignatureObject()
+ */
+ public Object getSignatureObject() throws PDFDocumentException
+ {
+ if (pdfSigObject_ == null)
+ {
+ pdfSigObject_ = (PdfPTable) getSignatureObject(sigObject_);
+ }
+ return pdfSigObject_;
+ }
+
+ /**
+ * Converts a abstract signature object in a pdf signature object
+ * implementation
+ *
+ * @param sigObject
+ * the abstract signatorObject to convert
+ * @return the converted pdf signature object
+ * @throws PDFDocumentException
+ * @see at.knowcenter.wag.egov.egiz.pdf.PDFSignatureObject#getSignatureObject(at.knowcenter.wag.egov.egiz.sig.SignatureObject)
+ */
+ public Object getSignatureObject(SignatureObject sigObject) throws PDFDocumentException
+ {
+ // initTableSettings(sigObject);
+ return createPDFSignatureObject(sigObject);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFUtilities.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFUtilities.java
new file mode 100644
index 0000000..8fa3b35
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFUtilities.java
@@ -0,0 +1,89 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: PDFUtilities.java,v 1.3 2006/10/31 08:09:33 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.pdfbox.pdfparser.PDFParser;
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.pdmodel.PDPage;
+
+import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
+import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
+
+import com.lowagie.text.DocumentException;
+
+/**
+ * Contains useful helpers for accessing PDF documents.
+ *
+ * @author wprinz
+ */
+public abstract class PDFUtilities
+{
+ public static float calculateLastPageLength(final byte[] pdf) throws PDFDocumentException
+ {
+ try
+ {
+ ByteArrayInputStream original_bais = new ByteArrayInputStream(pdf);
+ byte [] normalized_pdf = TextualSignature.normalizePDF(original_bais);
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(normalized_pdf);
+
+ PDFParser parser = new PDFParser(bais);
+ File temporary_dir = SettingsReader.getTemporaryDirectory();
+ parser.setTempDirectory(temporary_dir);
+ parser.parse();
+
+ PDDocument pdfDocument_ = parser.getPDDocument();
+ float last_page_length = calculateLastPageLength(pdfDocument_);
+ pdfDocument_.close();
+
+ return last_page_length;
+ }
+ catch (IOException e)
+ {
+ throw new PDFDocumentException(201, e);
+ }
+ catch (DocumentException e)
+ {
+ throw new PDFDocumentException(201, e);
+ }
+ }
+
+ public static float calculateLastPageLength(PDDocument document) throws IOException
+ {
+ int last_page_id = document.getNumberOfPages();
+ List allPages = document.getDocumentCatalog().getAllPages();
+ PDPage last_page = (PDPage) allPages.get(last_page_id - 1);
+
+ return calculatePageLength(last_page);
+ }
+
+ public static float calculatePageLength(PDPage page) throws IOException
+ {
+ // logger_.debug("Last Page id:" + last_page_id);
+ // PDPage last_page = (PDPage) allPages.get(0);
+ PDFPage my_page = new PDFPage();
+ my_page.processStream(page, page.findResources(), page.getContents().getStream());
+ return my_page.getMaxPageLength();
+ }
+
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/Placeholder.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/Placeholder.java
new file mode 100644
index 0000000..b9c3306
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/Placeholder.java
@@ -0,0 +1,552 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: Placeholder.java,v 1.5 2006/10/31 08:17:50 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.codec.net.URLCodec;
+import org.apache.log4j.Logger;
+
+import at.knowcenter.wag.egov.egiz.cfg.ConfigLogger;
+import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
+import at.knowcenter.wag.egov.egiz.exceptions.PlaceholderException;
+import at.knowcenter.wag.exactparser.ByteArrayUtils;
+
+/**
+ * Helper class that provides functionality for dealing with placeholders and
+ * replacements in pdf.
+ *
+ * @author wprinz
+ */
+public abstract class Placeholder
+{
+ /**
+ * The logger definition.
+ */
+ private static final Logger logger_ = ConfigLogger.getLogger(Placeholder.class);
+
+ /**
+ * Escapes the String to be a suitable Literal String..
+ *
+ * @param data
+ * The String to be escaped.
+ * @return Returns the escaped PDF String.
+ */
+ public static byte[] escapePDFString(byte[] data)
+ {
+ try
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (int i = 0; i < data.length; i++)
+ {
+ byte[] escaped_bytes = escapeByte(data[i]);
+ baos.write(escaped_bytes);
+ }
+ return baos.toByteArray();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Unescapes the PDF String.
+ *
+ * @param data
+ * The escaped String.
+ * @return Returns the unescaped String.
+ */
+ public static byte[] unescapePDFString(byte[] data)
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (int i = 0; i < data.length; i++)
+ {
+ if (data[i] == '\\' && data[i + 1] == '\\')
+ {
+ baos.write('\\');
+ i++;
+ continue;
+ }
+ if (data[i] == '\\' && data[i + 1] == '(')
+
+ {
+ baos.write('(');
+ i++;
+ continue;
+ }
+ if (data[i] == '\\' && data[i + 1] == ')')
+ {
+ baos.write(')');
+ i++;
+ continue;
+ }
+ baos.write(data[i]);
+ }
+ return baos.toByteArray();
+ }
+
+ /**
+ * Reconstructs the string from a partition of placeholders.
+ *
+ * @param pdf
+ * The PDF to read the string from.
+ * @param sis
+ * The list of StringInfo objects that specify the bytes of the
+ * string in the pdf.
+ * @return Returns the extracted and reconverted string.
+ * @throws IOException
+ * Forwarded exception.
+ */
+ public static String reconstructStringFromPartition(byte[] pdf, List sis,
+ byte[] enc) throws IOException
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ Iterator it = sis.iterator();
+ while (it.hasNext())
+ {
+ StringInfo si = (StringInfo) it.next();
+
+ for (int i = si.string_start; i < si.string_start + si.string_length; i++)
+ {
+ if (pdf[i] != 0)
+ {
+ baos.write(pdf[i]);
+ }
+ }
+ }
+
+ baos.close();
+ byte[] bytes = baos.toByteArray();
+
+ byte[] unescaped_bytes = unescapePDFString(bytes);
+
+ if (!ByteArrayUtils.compareByteArrays(enc, 0, BinarySignature.ENCODING_WIN) && !ByteArrayUtils.compareByteArrays(enc, 0, BinarySignature.ENCODING_URL))
+ {
+ String enc_str = new String(enc, "US-ASCII");
+ logger_.warn("The encoding " + enc_str + " is not known by this application - trying to proceed anyways.");
+ }
+
+ String text = new String(unescaped_bytes, "windows-1252");
+
+ String str = text;
+ if (ByteArrayUtils.compareByteArrays(enc, 0, BinarySignature.ENCODING_URL))
+ {
+ str = unapplyURLEncoding(str);
+ }
+
+ return str;
+ }
+
+ /**
+ * Prepares the given String to a byte array that can be substituted into the
+ * placeholder.
+ *
+ * @param text
+ * The text to be prepared for substitution.
+ * @return Returns the prepared byte array.
+ */
+ public static byte[] applyWinAnsiEncoding(String text)
+ {
+ // text = text.replace("\\", "\\\\");
+ // text = text.replace("(", "\\(");
+ // text = text.replace(")", "\\)");
+
+ byte[] replace_bytes;
+ try
+ {
+ replace_bytes = text.getBytes("windows-1252");// CP1252 = WinAnsiEncoding
+
+ // test the opposite way:
+ // String restored_string = new String (replace_bytes, "windows-1252");
+ // if (!restored_string.equals(text))
+ // {
+ // String url_encoded = URLEncoder.encode(text);
+ // replace_bytes = url_encoded.getBytes("windows-1252");
+ // }
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ return replace_bytes;
+ }
+
+ /**
+ * Unapplies the WinAnsi encoding.
+ *
+ * @param replace_bytes
+ * The bytes.
+ * @return Returns the decoded String.
+ */
+ public static String unapplyWinAnsiEncoding(byte[] replace_bytes)
+ {
+ try
+ {
+ String text = new String(replace_bytes, "windows-1252");
+
+ return text;
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+
+ }
+
+ /**
+ * Applies the URL encoding to the text.
+ *
+ * @param text
+ * The text
+ * @return Returns the URL and WinAnsi encoded text.
+ */
+ public static byte[] applyURLEncoding(String text)
+ {
+ URLCodec utf8_url_codec = new URLCodec("UTF-8");
+ String url_encoded = null;
+ try
+ {
+ url_encoded = utf8_url_codec.encode(text, "UTF-8");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new RuntimeException("Couldn't url encode : " + text, e);
+ }
+ // String url_encoded = URLEncoder.encode(text);
+ return applyWinAnsiEncoding(url_encoded);
+ }
+
+ /**
+ * Unapplies the WinAnsi and URL encoding.
+ *
+ * @param winansi_str
+ * The Winansi and URL text.
+ * @return Returns the decoded text.
+ */
+ public static String unapplyURLEncoding(String winansi_str)
+ {
+ URLCodec utf8_url_codec = new URLCodec("UTF-8");
+ String url_decoded = null;
+ try
+ {
+ url_decoded = utf8_url_codec.decode(winansi_str, "UTF-8");
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException("Couldn't url decode : " + winansi_str, e);
+ }
+ // String url_decoded = URLDecoder.decode(winansi_str);
+ return url_decoded;
+ }
+
+ /**
+ * Restores the String from a previously prepared byte array.
+ *
+ * @param pdf_string
+ * The byte array.
+ * @return Returns the unprepared String.
+ */
+ public static String unprepareAndUnescapeString(byte[] pdf_string)
+ {
+ try
+ {
+ String text = new String(pdf_string, "windows-1252");
+
+ // This makes problems when "+" appears.
+ // if (isURLEncoded(text))
+ // {
+ // text = URLDecoder.decode(text);
+ // }
+
+ text = text.replace("\\)", ")");
+ text = text.replace("\\(", "(");
+ text = text.replace("\\\\", "\\");
+
+ return text;
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * Checks the presence of typical URL encoded characters to tell if the string
+ * is URL encoded.
+ *
+ * <p>
+ * This heuristic checks if there are any non URL encoded characters in the
+ * String, like ASCII control characters, which aren't allowed in the
+ * URLEncoding characterset.
+ * </p>
+ *
+ * @param text
+ * The text under suspicion.
+ * @return Returns true if the String is URL encoded, false otherwise.
+ */
+ protected static boolean isURLEncoded(String text)
+ {
+ if (text.indexOf(' ') >= 0)
+ {
+ return false;
+ }
+ for (int i = 0; i < text.length(); i++)
+ {
+ char c = text.charAt(i);
+ if (0x00 <= c && c <= 0x1f)
+ {
+ return false;
+ }
+ if (c == 0x7F)
+ {
+ return false;
+ }
+ if (0x80 <= c)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Tells, if a break can occur behind the given character.
+ *
+ * @param character
+ * The character.
+ * @return Returns true, if a break may occur behind the character, false
+ * otherwise.
+ */
+ protected static boolean canBreakAfter(byte character)
+ {
+ return (character == ' ' || character == ',' || character == ';');
+ }
+
+ /**
+ * Scans the given PDF content stream for literal PDF strings.
+ *
+ * @param pdf
+ * The PDF.
+ * @param stream_start
+ * The start of the content stream to be scanned.
+ * @param stream_next
+ * The end of the content stream.
+ * @return Returns a list of StringInfo objects specifying the strings that
+ * could be found.
+ */
+ public static List parseStrings(byte[] pdf, int stream_start, int stream_next)
+ {
+ List strings = new ArrayList();
+ StringInfo cur_string = null;
+ for (int i = stream_start; i < stream_next; i++)
+ {
+ byte cur_byte = pdf[i];
+
+ if (cur_byte == '(' && pdf[i - 1] != '\\')
+ {
+ cur_string = new StringInfo();
+ cur_string.pdf = pdf;
+ cur_string.string_start = i + 1;
+ cur_string.string_length = -1;
+ // logger_.debug("String start = " + cur_string.string_start);
+ continue;
+ }
+ if (cur_byte == ')' && pdf[i - 1] != '\\')
+ {
+ cur_string.string_length = i - cur_string.string_start;
+ // logger_.debug("String length = " + cur_string.string_length);
+ strings.add(cur_string);
+
+ cur_string = null;
+ continue;
+ }
+ }
+
+ return strings;
+ }
+
+ /**
+ * Escapes the data byte if necessary.
+ *
+ * <p>
+ * Before bytes can be written into the pdf Strings, they have to be escaped.
+ * Special care has to be taken that escaped sequences are not split due to
+ * line breaks. This could have fatal consequences and usually renders the
+ * whole document invalid.
+ * </p>
+ *
+ * @param data
+ * The data byte to be escaped.
+ * @return Returns a new byte array escaping the data byte. If the byte needs
+ * not to be escaped, this new array will contain only the original
+ * data byte.
+ */
+ public static byte[] escapeByte(byte data)
+ {
+ if (data == '\\')
+ {
+ return new byte[] { '\\', '\\' };
+ }
+ if (data == '(')
+ {
+ return new byte[] { '\\', '(' };
+ }
+ if (data == ')')
+ {
+ return new byte[] { '\\', ')' };
+ }
+ return new byte[] { data };
+ }
+
+ /**
+ * Replaces the placeholder with the given String breaking lines with a given
+ * tolerance.
+ *
+ * @param pdf
+ * The PDF.
+ * @param sis
+ * The list of StringInfo objects describing the positions where the
+ * String should be filled in.
+ * @param replace_bytes
+ * The unescaped bytes to be filled in. Escaping is performed by this
+ * method.
+ * @param tolerance
+ * The tolerance for line wrapping. The tolerance counts from the end
+ * of a StringInfo backwards to its start. If a word that starts
+ * within the tolerance doesn't fit, it is wrapped into the next
+ * line.
+ * @throws PDFDocumentException
+ * Forwarded exception.
+ */
+ public static void replacePlaceholderWithTolerance(byte[] pdf, List sis,
+ byte[] replace_bytes, int tolerance) throws PDFDocumentException
+ {
+ try
+ {
+ // String rep_str = new String(replace_bytes);
+
+ SplitStrings ss = new SplitStrings(pdf, sis);
+
+ int read_index = 0;
+ while (read_index < replace_bytes.length)
+ {
+ if (!ss.isValidLine())
+ {
+ break;
+ }
+
+ byte[] token = readToken(replace_bytes, read_index);
+ // String token_str = new String(token);
+ byte[] escaped_token = escapeToken(token);
+
+ if (ss.fits(escaped_token))
+ {
+ ss.write(escaped_token);
+ read_index += token.length;
+ continue;
+ }
+ else
+ {
+ if (ss.getAvailable() < tolerance)
+ {
+ ss.newline();
+ continue;
+ }
+ else
+ {
+ // break the token
+ for (; read_index < replace_bytes.length; read_index++)
+ {
+ byte data = replace_bytes[read_index];
+
+ byte[] escaped_data = escapeByte(data);
+
+ if (ss.fits(escaped_data))
+ {
+ ss.write(escaped_data);
+ }
+ else
+ {
+ ss.newline();
+ break;
+ }
+ }
+ continue;
+
+ }
+ }
+ }
+ ss.fillRest();
+
+ if (read_index < replace_bytes.length)
+ {
+ logger_.error("The replace string was longer than the reserved placeholder.");
+ throw new PlaceholderException(null, replace_bytes.length - read_index);
+ }
+
+ }
+ catch (IOException e)
+ {
+ throw new PDFDocumentException(201, e);
+ }
+
+ }
+
+ protected static byte[] readToken(byte[] bytes, int index)
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (; index < bytes.length; index++)
+ {
+ byte data = bytes[index];
+
+ // byte [] escaped_data = escapeByte(data);
+ baos.write(data);
+
+ if (canBreakAfter(data))
+ {
+ break;
+ }
+ }
+
+ return baos.toByteArray();
+ }
+
+ protected static byte[] escapeToken(byte[] token) throws IOException
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ for (int i = 0; i < token.length; i++)
+ {
+ byte[] escaped_data = escapeByte(token[i]);
+ baos.write(escaped_data);
+ }
+
+ return baos.toByteArray();
+ }
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/Pos.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/Pos.java
new file mode 100644
index 0000000..4bc1a1a
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/Pos.java
@@ -0,0 +1,62 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: Pos.java,v 1.1 2006/08/25 17:10:08 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+/**
+ * Encapsulation of a position on a PDF page.
+ *
+ * @author wprinz
+ */
+public class Pos
+{
+
+ public float x;
+
+ public float y;
+
+ public float z;
+
+ /**
+ * Default constructor.
+ */
+ public Pos()
+ {
+ }
+
+ /**
+ * Constructor that sets the coordinates.
+ * @param xx
+ * @param yy
+ * @param zz
+ */
+ public Pos(float xx, float yy, float zz)
+ {
+ this.x = xx;
+ this.y = yy;
+ this.z = zz;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return "(" + this.x + "," + this.y + "," + this.z + ")";
+ }
+
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/ReplaceInfo.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/ReplaceInfo.java
new file mode 100644
index 0000000..849d224
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/ReplaceInfo.java
@@ -0,0 +1,64 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: ReplaceInfo.java,v 1.1 2006/08/25 17:10:08 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.Serializable;
+import java.util.List;
+
+import at.knowcenter.wag.egov.egiz.sig.SignatureFieldDefinition;
+
+/**
+ * Holds the information requeired to replace a certain value in the document
+ * completely.
+ *
+ * @author wprinz
+ */
+public class ReplaceInfo implements Serializable
+{
+
+ /**
+ * SVUID.
+ */
+ private static final long serialVersionUID = 7307210282876750431L;
+
+ /**
+ * The field definition of this value.
+ */
+ public SignatureFieldDefinition sfd = null;
+
+ /**
+ * The value itself.
+ */
+ public String value = null;
+
+ /**
+ * The list of Strings this value must be splitted to.
+ */
+ public List replaces = null;
+
+ /**
+ * The brev of this value.
+ */
+ public byte[] brev = null;
+
+ /**
+ * The encoding of this value.
+ */
+ public byte[] enc = null;
+
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/SignatureHolder.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/SignatureHolder.java
new file mode 100644
index 0000000..d7fcce9
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/SignatureHolder.java
@@ -0,0 +1,62 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: SignatureHolder.java,v 1.3 2006/10/11 07:57:58 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
+
+/**
+ * Data structure that holds the information of one signature block, which is
+ * the signed/signable text and the corresponding SignatureObject.
+ *
+ * <p>
+ * Signators and Verifiactors should implement own classes for this interface
+ * that generate the text to be signed from the underlying data. For example a
+ * binary signature holder could generate the text to be signed by Base64
+ * encoding the binary data. Furthermore this allows to cache the text to be
+ * signed.
+ * </p>
+ *
+ * @author wprinz
+ */
+public interface SignatureHolder
+{
+
+ /**
+ * Returns the signed text (verification) or the to-be-signed signable text
+ * (signation).
+ *
+ * <p>
+ * Note that this text must be the one that was actually signed. This text is
+ * directly passed to the connector for signation/verification. No
+ * normalization or modification will be / must be done to this text between
+ * reading out from the signature holder and passing the text to the
+ * connector.
+ * </p>
+ *
+ * @return Returns the signed text or the to-be-signed signable text.
+ */
+ public String getSignedText();
+
+ /**
+ *
+ * @return Returns the SignatureObject containing the issuer, serial number,
+ * etc.
+ */
+ public SignatureObject getSignatureObject();
+
+} \ No newline at end of file
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/SplitStrings.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/SplitStrings.java
new file mode 100644
index 0000000..e3f75f1
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/SplitStrings.java
@@ -0,0 +1,162 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: SplitStrings.java,v 1.1 2006/08/30 14:02:35 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.util.List;
+
+
+/**
+ * Class that helps filling out the placeholders.
+ *
+ * <p>
+ * This class treats a sequence of placeholder StringInfos like a continuous
+ * data area that can be filled out regarding the boundaries.
+ * </p>
+ *
+ * @author wprinz
+ */
+public class SplitStrings
+{
+ /**
+ * The byte used to fill unused bytes in the placeholders.
+ */
+ public static final byte FILL_BYTE = 0;
+
+ /**
+ * The underlying PDF.
+ */
+ protected byte[] pdf = null;
+
+ /**
+ * The strings to be filled out.
+ */
+ protected StringInfo[] strings = null;
+
+ /**
+ * The current string which is written to.
+ */
+ protected int cur_string = 0;
+
+ /**
+ * The current write position within the current string.
+ */
+ protected int cur_pos = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param pdf
+ * The underlying PDF.
+ * @param strings
+ * The strings to be filled out.
+ */
+ public SplitStrings(byte[] pdf, List strings)
+ {
+ this.pdf = pdf;
+ this.strings = new StringInfo[strings.size()];
+ for (int i = 0; i < strings.size(); i++)
+ {
+ StringInfo si = (StringInfo) strings.get(i);
+ this.strings[i] = si;
+ }
+ }
+
+ /**
+ * Returns how many bytes are still available in the current string.
+ *
+ * @return Returns the number of bytes that are still available. (positive
+ * integer, or zero if none are available)
+ */
+ public int getAvailable()
+ {
+ return this.strings[this.cur_string].string_length - this.cur_pos;
+ }
+
+ /**
+ * Tells, if the whole data would fit into the current string.
+ *
+ * @param data
+ * The data to be matched for fitting
+ * @return Returns true, if the whole data fits, false otherwise.
+ */
+ public boolean fits(byte[] data)
+ {
+ return getAvailable() >= data.length;
+ }
+
+ /**
+ * Writes the data into the current string.
+ *
+ * <p>
+ * Note that the data must fit in.
+ * </p>
+ * @param data The data to be written.
+ */
+ public void write(byte[] data)
+ {
+ if (!fits(data))
+ {
+ throw new IllegalArgumentException("The data doesn't fit in.");
+ }
+
+ System.arraycopy(data, 0, pdf, this.strings[this.cur_string].string_start + this.cur_pos, data.length);
+
+ this.cur_pos += data.length;
+ }
+
+ /**
+ * Fills the current string with the fill character and moves on to the next
+ * string.
+ *
+ */
+ public void newline()
+ {
+ int end = this.strings[this.cur_string].string_start + this.strings[this.cur_string].string_length;
+ for (int i = this.strings[this.cur_string].string_start + this.cur_pos; i < end; i++)
+ {
+ pdf[i] = FILL_BYTE;
+ }
+
+ this.cur_string++;
+ this.cur_pos = 0;
+ }
+
+ /**
+ * Fills all rest bytes with the fill character.
+ *
+ * <p>
+ * This should be called when everything is finished to fill all strings properly.
+ * </p>
+ */
+ public void fillRest()
+ {
+ while (this.cur_string < this.strings.length)
+ {
+ newline();
+ }
+ }
+
+ /**
+ * Tells, if the current line is valid.
+ * @return Returns true, if this is a line that can be written to.
+ */
+ public boolean isValidLine ()
+ {
+ return this.cur_string < this.strings.length;
+ }
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StringInfo.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StringInfo.java
new file mode 100644
index 0000000..5933e4b
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/StringInfo.java
@@ -0,0 +1,93 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: StringInfo.java,v 1.2 2006/10/11 07:57:58 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Specifies a certain data area within the pdf.
+ *
+ * <p>
+ * Actually this is a byte range, which is used to hold the placeholder ranges
+ * for later replacement.
+ * </p>
+ *
+ * @author wprinz
+ */
+public class StringInfo implements Serializable
+{
+ /**
+ * SVUID.
+ */
+ private static final long serialVersionUID = 5834801907046737048L;
+
+ /**
+ * The PDF document this range belongs to.
+ */
+ protected byte[] pdf = null;
+
+ /**
+ * The start offset of the range.
+ */
+ public int string_start = -1;
+
+ /**
+ * The length of the range.
+ */
+ public int string_length = -1;
+
+ /**
+ * Copies the bytes of this range to a new byte array.
+ *
+ * @return Returns the new byte array.
+ */
+ public byte[] copyStringBytes()
+ {
+ byte[] bytes = new byte[this.string_length];
+ System.arraycopy(this.pdf, this.string_start, bytes, 0, this.string_length);
+ return bytes;
+ }
+
+ /**
+ * Converts the range into a String.
+ *
+ * @return Returns the String.
+ * @throws UnsupportedEncodingException
+ * Forwarded exception.
+ */
+ public String getString(String encoding) throws UnsupportedEncodingException
+ {
+ byte[] bytes = copyStringBytes();
+ return new String(bytes, encoding);
+ }
+
+ public String toString()
+ {
+ try
+ {
+ return "(" + this.string_start + "," + this.string_length + ")" + getString("ISO-8859-1");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ e.printStackTrace();
+ return "(" + this.string_start + "," + this.string_length + ")";
+ }
+ }
+
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/TablePos.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/TablePos.java
new file mode 100644
index 0000000..ba55cdf
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/TablePos.java
@@ -0,0 +1,56 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: TablePos.java,v 1.1 2006/08/25 17:10:08 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.Serializable;
+
+/**
+ * Class that holds the exact position where the table should be written to the
+ * document.
+ *
+ * @author wprinz
+ */
+public class TablePos implements Serializable
+{
+
+ /**
+ * SVUID.
+ */
+ private static final long serialVersionUID = -5299027706623518059L;
+
+ /**
+ * The page on which the block should be displayed.
+ */
+ public int page = 0;
+
+ /**
+ * The x position.
+ */
+ public float pos_x = 0.0f;
+
+ /**
+ * The y position.
+ */
+ public float pos_y = 0.0f;
+
+ /**
+ * The width of the block.
+ */
+ public float width = 0.0f;
+
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/TextualSignature.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/TextualSignature.java
new file mode 100644
index 0000000..140a6c3
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/TextualSignature.java
@@ -0,0 +1,177 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: TextualSignature.java,v 1.4 2006/10/31 08:12:45 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.pdfbox.pdfparser.PDFParser;
+import org.pdfbox.pdmodel.PDDocument;
+import org.pdfbox.util.PDFTextStripper;
+
+import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
+import at.knowcenter.wag.egov.egiz.exceptions.PresentableException;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+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;
+
+/**
+ * Contains helper function for textual signatures.
+ *
+ * @author wprinz
+ */
+public class TextualSignature
+{
+
+ /**
+ * Extracts the document text from a given pdf.
+ *
+ * @param pdf_stream
+ * The pdf_input stream.
+ * @return Returns the extracted document text.
+ * @throws PresentableException
+ * Forwarded exception.
+ */
+ public static String extractTextTextual(InputStream pdf_stream) throws PresentableException
+ {
+ try
+ {
+ // logger_.debug("====================================================");
+ // logger_.debug("extractText:");
+
+ // For text extraction, create a temporary object with iText just as the
+ // one
+ // created
+ // when being signed, but of course without adding content.
+
+
+ byte[] bytes = normalizePDF(pdf_stream);
+
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+
+ PDFParser parser = new PDFParser(bais);
+ File temporary_dir = SettingsReader.getTemporaryDirectory();
+ parser.setTempDirectory(temporary_dir);
+ parser.parse();
+
+ PDDocument doc = parser.getPDDocument();
+
+ PDFTextStripper stripper = new PDFTextStripper();
+ stripper.setSortByPosition(false);
+ // stripper.setStartPage(4);
+ // stripper.setEndPage(4);
+ String text = stripper.getText(doc);
+
+ doc.close();
+
+ // logger_.debug("text.length = " + text.length());
+ // logger_.debug("====================================================");
+
+ return text;
+
+ }
+ catch (Exception e)
+ {
+ throw new PresentableException(e);
+ }
+ }
+
+ /**
+ * Normalizes a given binary PDF to a version PDFbox can handle correctly.
+ *
+ * <p>
+ * PDFbox has serious problems with documents that use incremental updates or
+ * XObject forms. Therefor use this to remove incremental updates and create a
+ * streamlined document.
+ * </p>
+ *
+ * <p>
+ * Note that this has nothing to do with text normalization. It just unifies
+ * the PDF documents that are fed into PDFbox for text extraction and page
+ * length determination.
+ * </p>
+ *
+ * @param input_pdf
+ * The input pdf to be normalized.
+ * @return Returns the normalized pdf.
+ * @throws IOException
+ * @throws DocumentException
+ */
+ public static byte[] normalizePDF(InputStream input_pdf) throws IOException, DocumentException
+ {
+ PdfReader reader = new PdfReader(input_pdf);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ // For some reason the Reader -> ImportPage -> Writer mechanism produces
+ // problems en mass.
+ // The text extractor may not be able to extract proper text from
+ // documents
+ // created with
+ // this method (although it works when a Table is appended)... very
+ // fragile.
+
+ Document document = new Document();
+
+ PdfWriter writer = PdfWriter.getInstance(document, baos);
+ document.open();
+
+ PdfContentByte cb = writer.getDirectContent();
+ for (int page_num = 1; page_num <= reader.getNumberOfPages(); page_num++)
+ {
+ Rectangle new_size = reader.getPageSize(page_num);
+ document.setPageSize(new_size);
+ document.newPage();
+
+ PdfImportedPage page = writer.getImportedPage(reader, page_num);
+
+ // note that this will add an xobject form to the doc.
+ // the xobject form contains the content of the page.
+ cb.addTemplate(page, 0, 0);
+
+ // wprinz: debugging
+ // cb.beginText();
+ // cb.setFontAndSize(BaseFont.createFont(BaseFont.HELVETICA,
+ // BaseFont.CP1252, BaseFont.NOT_EMBEDDED), 14);
+ // cb.showText("page " + page_num);
+ // cb.endText();
+ // wprinz: end debugging
+ }
+
+ document.close();
+
+ // for (int i = 1; i <= reader.getNumberOfPages(); i++)
+ // {
+ // Rectangle rect = reader.getBoxSize(i, "bleed");
+ // logger_.debug("rect[" + i + "] = " + rect);
+ // }
+
+ baos.close();
+ byte[] normalizedPDF = baos.toByteArray();
+
+ return normalizedPDF;
+ }
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/TextualSignatureHolder.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/TextualSignatureHolder.java
new file mode 100644
index 0000000..fd56125
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/TextualSignatureHolder.java
@@ -0,0 +1,73 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: TextualSignatureHolder.java,v 1.1 2006/10/11 07:58:17 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.Serializable;
+
+import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
+
+/**
+ * Data structure that holds the information of one signature block, which is
+ * the signed/signable text and the corresponding SignatureObject.
+ *
+ * @author wprinz
+ */
+public class TextualSignatureHolder implements Serializable, SignatureHolder
+{
+
+ /**
+ * SVUID.
+ */
+ private static final long serialVersionUID = -7208103904479272760L;
+
+ /**
+ * The signed text of this object.
+ *
+ * <p>
+ * This is the value that will be signed by the Connector.
+ * </p>
+ */
+ private String signed_text = null;
+
+ /**
+ * The signature object.
+ */
+ private SignatureObject signature_object = null;
+
+ public TextualSignatureHolder(String text, SignatureObject so)
+ {
+ this.signed_text = text;
+ this.signature_object = so;
+ }
+
+ /**
+ * @see at.knowcenter.wag.egov.egiz.pdf.SignatureHolder#getSignedText()
+ */
+ public String getSignedText()
+ {
+ return this.signed_text;
+ }
+
+ /**
+ * @see at.knowcenter.wag.egov.egiz.pdf.SignatureHolder#getSignatureObject()
+ */
+ public SignatureObject getSignatureObject()
+ {
+ return this.signature_object;
+ }
+}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/Utils.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/Utils.java
new file mode 100644
index 0000000..c075d45
--- /dev/null
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/Utils.java
@@ -0,0 +1,96 @@
+/**
+ * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
+ *
+ * This software is the confidential and proprietary information of Know-Center,
+ * Graz, Austria. 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 Know-Center.
+ *
+ * KNOW-CENTER 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. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
+ * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES.
+ *
+ * $Id: Utils.java,v 1.3 2006/10/31 08:13:02 wprinz Exp $
+ */
+package at.knowcenter.wag.egov.egiz.pdf;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Abstract class that contains helpful utility functions used by the digital
+ * signatures.
+ *
+ * @author wprinz
+ */
+public abstract class Utils
+{
+
+ /**
+ * Extracts the pure content text from a given content stream.
+ *
+ * <p>
+ * The pure content text is just an assembly of all strings that occur within the content stream in
+ * stream order.
+ * Each of these strings will be set on a new line.
+ * </p>
+ *
+ * @param stream_bytes The content stream.
+ * @return Returns the extracted string.
+ * @throws IOException Forwarded exception.
+ */
+ public static String extractPureTextFromContentStream(
+ final byte[] stream_bytes) throws IOException
+ {
+
+ // logger_.debug("stream_bytes:");
+ // logger_.debug(new String(stream_bytes, "US-ASCII"));
+ // logger_.debug(":end of stream_bytes");
+
+ final byte OPEN = '(';
+ final byte CLOSE = ')';
+
+ StringWriter strwrtr = new StringWriter();
+ PrintWriter printer = new PrintWriter(strwrtr);
+ int open_index = -1;
+ int close_index = -1;
+ for (int i = 0; i < stream_bytes.length; i++)
+ {
+ if (stream_bytes[i] == OPEN)
+ {
+ open_index = i;
+ continue;
+ }
+ if (stream_bytes[i] == CLOSE)
+ {
+ close_index = i;
+
+ // logger_.debug("open = " + open_index + ", close = " +
+ // close_index);
+
+ int len = close_index - open_index - 1;
+ // logger_.debug("len = " + len);
+
+ byte[] bytes = new byte[len];
+ System.arraycopy(stream_bytes, open_index + 1, bytes, 0, len);
+
+ String str = new String(bytes, "ISO-8859-1");
+ // logger_.debug("string = " + str);
+
+ printer.println(str);
+
+ continue;
+ }
+ }
+ strwrtr.close();
+ String signature_text = new String(strwrtr.getBuffer());
+ // logger_.debug(signature_text);
+
+ return signature_text;
+ }
+
+}