diff options
| author | Christian Maierhofer <cmaierhofer@iaik.tugraz.at> | 2016-06-08 08:12:21 +0200 | 
|---|---|---|
| committer | Andreas Fitzek <andreas.fitzek@iaik.tugraz.at> | 2016-08-17 16:45:49 +0200 | 
| commit | c4e41301d0746ce57044a3aa41375cff3a9f2b5e (patch) | |
| tree | bdf13d0a0d7fae97215a84286e31ff894bacc979 /pdf-as-pdfbox-2/src | |
| parent | a7945fff6ca14d35ac75039712736dddc6d48e90 (diff) | |
| download | pdf-as-4-c4e41301d0746ce57044a3aa41375cff3a9f2b5e.tar.gz pdf-as-4-c4e41301d0746ce57044a3aa41375cff3a9f2b5e.tar.bz2 pdf-as-4-c4e41301d0746ce57044a3aa41375cff3a9f2b5e.zip | |
initial pdfbox-2 commit
Diffstat (limited to 'pdf-as-pdfbox-2/src')
47 files changed, 8016 insertions, 0 deletions
| diff --git a/pdf-as-pdfbox-2/src/main/java/META-INF/MANIFEST.MF b/pdf-as-pdfbox-2/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 00000000..5e949512 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0
 +Class-Path: 
 +
 diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXBackend.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXBackend.java new file mode 100644 index 00000000..1a9424a6 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXBackend.java @@ -0,0 +1,53 @@ +package at.gv.egiz.pdfas.lib.impl.pdfbox2; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.lib.backend.PDFASBackend; +import at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder.PDFBoxPlaceholderExtractor; +import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractor; +import at.gv.egiz.pdfas.lib.impl.signing.IPdfSigner; +import at.gv.egiz.pdfas.lib.impl.signing.pdfbox2.PADESPDFBOXSigner; +import at.gv.egiz.pdfas.lib.impl.verify.VerifyBackend; +import at.gv.egiz.pdfas.lib.impl.verify.pdfbox2.PDFBOXVerifier; + +public class PDFBOXBackend implements PDFASBackend { + +	private static final String NAME = "PDFBOX_2_BACKEND"; +	 +	private static final Logger logger = LoggerFactory +			.getLogger(PDFBOXBackend.class); +	 +	static { +		logger.info(" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); +		logger.info(" + PDFBOX Backend created"); +		logger.info(" + PDFBOX Version used: " + org.apache.pdfbox.util.Version.getVersion()); +		logger.info(" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); +	} +	 +	@Override +	public String getName() { +		return NAME; +	} + +	@Override +	public boolean usedAsDefault() { +		return true; +	} + +	@Override +	public IPdfSigner getPdfSigner() { +		return new PADESPDFBOXSigner(); +	} + +	@Override +	public PlaceholderExtractor getPlaceholderExtractor() { +		return new PDFBoxPlaceholderExtractor(); +	} + +	@Override +	public VerifyBackend getVerifier() { +		return new PDFBOXVerifier(); +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXObject.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXObject.java new file mode 100644 index 00000000..0eee4cd3 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXObject.java @@ -0,0 +1,70 @@ +package at.gv.egiz.pdfas.lib.impl.pdfbox2; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.activation.DataSource; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.font.PDFont; + +import at.gv.egiz.pdfas.lib.impl.status.OperationStatus; +import at.gv.egiz.pdfas.lib.impl.status.PDFObject; + +public class PDFBOXObject extends PDFObject { + +	private PDDocument doc; +	 +	private Map<String, PDFont> fontCache = new HashMap<String, PDFont>(); +	 +	public PDFBOXObject(OperationStatus operationStatus) { +		super(operationStatus); +	} + +	@Override +	protected void finalize() throws Throwable { +		super.finalize(); +		if(doc != null) { +			doc.close(); +		} +	} +	 +	public void close() { +		if(doc != null) { +			try { +				doc.close(); +				//System.gc(); +			} catch(Throwable e) { +				// ignore! +			} +			doc = null; +		} +	} +	 +	public void setOriginalDocument(DataSource originalDocument) throws IOException { +		this.originalDocument = originalDocument; +		if(doc != null) { +			doc.close(); +		} +		synchronized(PDDocument.class) { +			this.doc = PDDocument.load(this.originalDocument.getInputStream()); +		} +		if(this.doc != null) { +			this.doc.getDocument().setWarnMissingClose(false); +		} +	} +	 +	public PDDocument getDocument() { +		return this.doc; +	} + +	@Override +	public String getPDFVersion() { +		return String.valueOf(getDocument().getDocument().getVersion()); +	} + +	public Map<String, PDFont> getFontCache() { +		return fontCache; +	} +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java new file mode 100644 index 00000000..730a6581 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java @@ -0,0 +1,24 @@ +package at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject; +import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractor; +import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData; +import at.gv.egiz.pdfas.lib.impl.status.PDFObject; + +public class PDFBoxPlaceholderExtractor implements PlaceholderExtractor { + +	@Override +	public SignaturePlaceholderData extract(PDFObject doc, +			String placeholderId, int matchMode) throws PdfAsException { + +		if (doc instanceof PDFBOXObject) { +			PDFBOXObject object = (PDFBOXObject) doc; +			return SignaturePlaceholderExtractor.extract(object.getDocument(), +					placeholderId, matchMode); +		} + +		throw new PdfAsException("INVALID STATE"); +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java new file mode 100644 index 00000000..39d66c3c --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java @@ -0,0 +1,474 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder; + +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import org.apache.pdfbox.contentstream.PDContentStream; +import org.apache.pdfbox.contentstream.PDFStreamEngine; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDFontFactory; +import org.apache.pdfbox.pdmodel.graphics.PDXObject; +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; +import org.apache.pdfbox.util.Matrix; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PDFIOException; +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.exceptions.PlaceholderExtractionException; +import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractorConstants; +import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderContext; +import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData; +import at.knowcenter.wag.egov.egiz.pdf.TablePos; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.BinaryBitmap; +import com.google.zxing.DecodeHintType; +import com.google.zxing.LuminanceSource; +import com.google.zxing.MultiFormatReader; +import com.google.zxing.NotFoundException; +import com.google.zxing.ReaderException; +import com.google.zxing.Result; +import com.google.zxing.client.j2se.BufferedImageLuminanceSource; +import com.google.zxing.common.HybridBinarizer; + +/** + * Extract all relevant information from a placeholder image. + * + * @author exthex + * + */ +public class SignaturePlaceholderExtractor extends PDFStreamEngine implements PlaceholderExtractorConstants{ +	/** +	 * The log. +	 */ +	private static Logger logger = LoggerFactory +			.getLogger(SignaturePlaceholderExtractor.class); + +	private List<SignaturePlaceholderData> placeholders = new Vector<SignaturePlaceholderData>(); +	private int currentPage = 0; +	private PDDocument doc; + +	private SignaturePlaceholderExtractor(String placeholderId, +			int placeholderMatchMode, PDDocument doc) throws IOException { +		super(); +		//super(ResourceLoader.loadProperties( //TODO: pdfbox2 - properties need to be set +		//		"placeholder/pdfbox-reader.properties", true)); +		this.doc = doc; +	} + +	/** +	 * Search the document for placeholder images and possibly included +	 * additional info.<br/> +	 * Searches only for the first placeholder page after page from top. +	 * +	 * @param inputStream +	 * @return all available info from the first found placeholder. +	 * @throws PDFDocumentException +	 *             if the document could not be read. +	 * @throws PlaceholderExtractionException +	 *             if STRICT matching mode was requested and no suitable +	 *             placeholder could be found. +	 */ +	public static SignaturePlaceholderData extract(PDDocument doc, +			String placeholderId, int matchMode) throws PdfAsException { +		SignaturePlaceholderContext.setSignaturePlaceholderData(null); + +		SignaturePlaceholderExtractor extractor; +		try { +			extractor = new SignaturePlaceholderExtractor(placeholderId, +					matchMode, doc); +		} catch (IOException e2) { +			throw new PDFIOException("error.pdf.io.04", e2); +		} + +		int pageNr = 0; +		for(PDPage page : doc.getPages()){ +			pageNr++; + +			try { +				extractor.setCurrentPage(pageNr); +				if(page.getContents() != null && page.getResources() != null && page.getContentStreams() != null) { +						extractor.processPage(page); //TODO: pdfbox2 - right? +					 +				} +				SignaturePlaceholderData ret = matchPlaceholderPage( +						extractor.placeholders, placeholderId, matchMode); +				if (ret != null) { +					SignaturePlaceholderContext +							.setSignaturePlaceholderData(ret); +					return ret; +				} +			} catch (IOException e1) { +				throw new PDFIOException("error.pdf.io.04", e1); +			} catch(Throwable e) { +				throw new PDFIOException("error.pdf.io.04", e); +			} +		} +		if (extractor.placeholders.size() > 0) { +			SignaturePlaceholderData ret = matchPlaceholderDocument( +					extractor.placeholders, placeholderId, matchMode); +			SignaturePlaceholderContext.setSignaturePlaceholderData(ret); +			return ret; +		} +		// no placeholders found, apply strict mode if set +		if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) { +			throw new PlaceholderExtractionException("error.pdf.stamp.09"); +		} + +		return null; +	} + +	private static SignaturePlaceholderData matchPlaceholderDocument( +			List<SignaturePlaceholderData> placeholders, String placeholderId, +			int matchMode) throws PlaceholderExtractionException { + +		if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) +			throw new PlaceholderExtractionException("error.pdf.stamp.09"); + +		if (placeholders.size() == 0) +			return null; + +		if (matchMode == PLACEHOLDER_MATCH_MODE_SORTED) { +			// sort all placeholders by the id string if all ids are null do nothing +			SignaturePlaceholderData currentFirstSpd = null; +			for (int i = 0; i < placeholders.size(); i++) { +				SignaturePlaceholderData spd = placeholders.get(i); +				if (spd.getId() != null) { +					if(currentFirstSpd == null) { +						currentFirstSpd = spd; +						logger.debug("Setting new current ID: {}",  +								currentFirstSpd.getId()); +					} else { +						String currentID = currentFirstSpd.getId(); +						String testID = spd.getId(); +						logger.debug("Testing placeholder current: {} compare to {}",  +								currentID, testID); +						if(testID.compareToIgnoreCase(currentID) < 0) { +							currentFirstSpd = spd; +							logger.debug("Setting new current ID: {}",  +									testID); +						} +					} +				} +			} +			 +			if(currentFirstSpd != null) { +				logger.info("Running Placeholder sorted mode: using id: {}", currentFirstSpd.getId()); +				return currentFirstSpd; +			} else { +				logger.info("Running Placeholder sorted mode: no placeholder with id found, fallback to first placeholder"); +			} +		} +		 +		for (int i = 0; i < placeholders.size(); i++) { +			SignaturePlaceholderData spd = placeholders.get(i); +			if (spd.getId() == null) +				return spd; +		} + +		if (matchMode == PLACEHOLDER_MATCH_MODE_LENIENT) +			return placeholders.get(0); + +		return null; +	} + +	private static SignaturePlaceholderData matchPlaceholderPage( +			List<SignaturePlaceholderData> placeholders, String placeholderId, +			int matchMode) { +		 +		if(matchMode == PLACEHOLDER_MATCH_MODE_SORTED) +			return null; +		 +		if (placeholders.size() == 0) +			return null; +		for (int i = 0; i < placeholders.size(); i++) { +			SignaturePlaceholderData data = placeholders.get(i); +			if (placeholderId != null && placeholderId.equals(data.getId())) +				return data; +			if (placeholderId == null && data.getId() == null) +				return data; +		} +		return null; +	} + +	private void setCurrentPage(int pageNr) { +		this.currentPage = pageNr; +	} + +	@Override +	protected void processOperator(Operator operator, List<COSBase> arguments) +			throws IOException { +		String operation = operator.getName(); +		if (operation.equals("Do")) { +			COSName objectName = (COSName) arguments.get(0); +			PDXObject xobject = (PDXObject) getResources().getXObject(objectName); +			if (xobject instanceof PDImageXObject) { +				try { +					PDImageXObject image = (PDImageXObject) xobject; +					SignaturePlaceholderData data = checkImage(image); +					if (data != null) { +						PDPage page = getCurrentPage(); +						Matrix ctm = getGraphicsState() +								.getCurrentTransformationMatrix(); +						int pageRotation = page.getRotation(); +						pageRotation = pageRotation % 360; +						double rotationInRadians = Math.toRadians(pageRotation);//(page.findRotation() * Math.PI) / 180; + +						AffineTransform rotation = new AffineTransform(); +						rotation.setToRotation(rotationInRadians); +						AffineTransform rotationInverse = rotation +								.createInverse(); +						Matrix rotationInverseMatrix = new Matrix(); +						rotationInverseMatrix +								.setFromAffineTransform(rotationInverse); +						Matrix rotationMatrix = new Matrix(); +						rotationMatrix.setFromAffineTransform(rotation); + +						Matrix unrotatedCTM = ctm +								.multiply(rotationInverseMatrix); + +						float x = unrotatedCTM.getXPosition(); +						float yPos = unrotatedCTM.getYPosition(); +						float yScale = unrotatedCTM.getScaleY(); +						float y = yPos + yScale; +						float w = unrotatedCTM.getScaleX();; +						 +						logger.debug("Page height: {}", page.getCropBox().getHeight()); +						logger.debug("Page width: {}", page.getCropBox().getWidth()); +						 +						if(pageRotation == 90) { +							y = page.getCropBox().getWidth() - (y * (-1)); +						} else if(pageRotation == 180) { +							x = page.getCropBox().getWidth() + x; +							y = page.getCropBox().getHeight() - (y * (-1)); +						} else if(pageRotation == 270) { +							x = page.getCropBox().getHeight() + x; +						} +						 +						String posString = "p:" + currentPage + ";x:" + x +								+ ";y:" + y + ";w:" + w; + +						logger.debug("Found Placeholder at: {}", posString); +						try { +							data.setTablePos(new TablePos(posString)); +							data.setPlaceholderName(objectName.getName()); +							placeholders.add(data); +						} catch (PdfAsException e) { +							throw new IOException(); +						} +					} +				} catch (NoninvertibleTransformException e) { +					throw new IOException(e); +				} +			} +		} else { +			super.processOperator(operator, arguments); +		} +	} + +	private  Map<String, PDFont> fonts; +	 +	//TODO: pdfbox2 - was override +	public Map<String, PDFont> getFonts() { +		if (fonts == null) +        { +            // at least an empty map will be returned +            // TODO we should return null instead of an empty map +            fonts = new HashMap<String, PDFont>(); +            if(this.getResources() != null && this.getResources().getCOSObject() != null) { +            COSDictionary fontsDictionary = (COSDictionary) this.getResources().getCOSObject().getDictionaryObject(COSName.FONT); +            if (fontsDictionary == null) +            { +            	// ignore we do not want to set anything, never when creating a signature!!!!! +                //fontsDictionary = new COSDictionary(); +                //this.getResources().getCOSDictionary().setItem(COSName.FONT, fontsDictionary); +            } +            else +            { +                for (COSName fontName : fontsDictionary.keySet()) +                { +                    COSBase font = fontsDictionary.getDictionaryObject(fontName); +                    // data-000174.pdf contains a font that is a COSArray, looks to be an error in the +                    // PDF, we will just ignore entries that are not dictionaries. +                    if (font instanceof COSDictionary) +                    { +                        PDFont newFont = null; +                        try +                        { +                            newFont = PDFontFactory.createFont((COSDictionary) font); +                        } +                        catch (IOException exception) +                        { +                            logger.error("error while creating a font", exception); +                        } +                        if (newFont != null) +                        { +                            fonts.put(fontName.getName(), newFont); +                        } +                    } +                } +            } +            } +        } +        return fonts; +	} + +	/** +	 * Checks an image if it is a placeholder for a signature. +	 * +	 * @param image +	 * @return +	 * @throws IOException +	 */ +	private SignaturePlaceholderData checkImage(PDImageXObject image) +			throws IOException { +		BufferedImage bimg = image.getImage(); +		if (bimg == null) { +			String type = image.getSuffix(); +			if (type != null) { +				type = type.toUpperCase() + " images"; +			} else { +				type = "Image type"; +			} +			logger.info("Unable to extract image for QRCode analysis. " +					+ type +					+ " not supported. Add additional JAI Image filters to your classpath. Refer to https://jai.dev.java.net. Skipping image."); +			return null; +		} +		if (bimg.getHeight() < 10 || bimg.getWidth() < 10) { +			logger.debug("Image too small for QRCode. Skipping image."); +			return null; +		} + +		LuminanceSource source = new BufferedImageLuminanceSource(bimg); +		BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); +		Result result; +		long before = System.currentTimeMillis(); +		try { +			Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(); +			Vector<BarcodeFormat> formats = new Vector<BarcodeFormat>(); +			formats.add(BarcodeFormat.QR_CODE); +			hints.put(DecodeHintType.POSSIBLE_FORMATS, formats); +			result = new MultiFormatReader().decode(bitmap, hints); + +			String text = result.getText(); +			String profile = null; +			String type = null; +			String sigKey = null; +			String id = null; +			if (text != null) { +				if (text.startsWith(QR_PLACEHOLDER_IDENTIFIER)) { +					String[] data = text.split(";"); +					if (data.length > 1) { +						for (int i = 1; i < data.length; i++) { +							String kvPair = data[i]; +							String[] kv = kvPair.split("="); +							if (kv.length != 2) { +								logger.debug("Invalid parameter in placeholder data: " +										+ kvPair); +							} else { +								if (kv[0] +										.equalsIgnoreCase(SignaturePlaceholderData.ID_KEY)) { +									id = kv[1]; +								} else if (kv[0] +										.equalsIgnoreCase(SignaturePlaceholderData.PROFILE_KEY)) { +									profile = kv[1]; +								} else if (kv[0] +										.equalsIgnoreCase(SignaturePlaceholderData.SIG_KEY_KEY)) { +									sigKey = kv[1]; +								} else if (kv[0] +										.equalsIgnoreCase(SignaturePlaceholderData.TYPE_KEY)) { +									type = kv[1]; +								} +							} +						} +					} +					return new SignaturePlaceholderData(profile, type, sigKey, +							id); +				} else { +					logger.warn("QR-Code found but does not start with \"" +							+ QR_PLACEHOLDER_IDENTIFIER +							+ "\". Ignoring QR placeholder."); +				} +			} +		} catch (ReaderException re) { +			if (logger.isDebugEnabled()) { +				logger.debug("Could not decode - not a placeholder. needed: " +						+ (System.currentTimeMillis() - before)); +			} +			if (!(re instanceof NotFoundException)) { +				if (logger.isInfoEnabled()) { +					logger.info("Failed to decode image", re); +				} +			} +		} catch (ArrayIndexOutOfBoundsException e) { +			if (logger.isInfoEnabled()) { +				logger.info("Failed to decode image. Probably a zxing bug", e); +			} +		} +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java new file mode 100644 index 00000000..429fe518 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java @@ -0,0 +1,308 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.pdfbox2.positioning; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.lib.impl.pdfbox2.utils.PdfBoxUtils; +import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject; +import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; +import at.knowcenter.wag.egov.egiz.pdf.TablePos; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFUtilities; + +/** + * Created with IntelliJ IDEA. User: afitzek Date: 8/29/13 Time: 4:30 PM To + * change this template use File | Settings | File Templates. + */ +public class Positioning { + +	private static final Logger logger = LoggerFactory +			.getLogger(Positioning.class); + +	/** +	 * The left/right margin. +	 */ +	public static final float SIGNATURE_MARGIN_HORIZONTAL = 50f; + +	/** +	 * The top/bottom margin. +	 */ +	public static final float SIGNATURE_MARGIN_VERTICAL = 20f; + +	/** +	 * Evalutates absolute positioning and prepares the PositioningInstruction +	 * for placing the table. +	 * +	 * @param pos +	 *            The absolute positioning parameter. If null it is sought in +	 *            the profile definition. +	 * @param signature_type +	 *            The profile definition of the table to be written. +	 * @param pdfDataSource +	 *            The pdf. +	 * @param pdf_table +	 *            The pdf table to be written. +	 * @return Returns the PositioningInformation. +	 * @throws PdfAsException +	 *             F.e. +	 */ +	public static PositioningInstruction determineTablePositioning( +			TablePos pos, String signature_type, PDDocument pdfDataSource, +			IPDFVisualObject pdf_table, boolean legacy32, boolean legacy40) throws PdfAsException { +		return adjustSignatureTableandCalculatePosition(pdfDataSource, +				pdf_table, pos, legacy32, legacy40); +	} + +	private static PDRectangle rotateBox(PDRectangle cropBox, int rotation) { +		if (rotation != 0) { +			Point2D upSrc = new Point2D.Float(); + +			upSrc.setLocation(cropBox.getUpperRightX(), +					cropBox.getUpperRightY()); + +			Point2D llSrc = new Point2D.Float(); +			llSrc.setLocation(cropBox.getLowerLeftX(), cropBox.getLowerLeftY()); +			AffineTransform transform = new AffineTransform(); +			transform.setToIdentity(); +			if (rotation % 360 != 0) { +				transform.setToRotation(Math.toRadians(rotation * -1), llSrc.getX(), +						llSrc.getY()); +			} +			Point2D upDst = new Point2D.Float(); +			transform.transform(upSrc, upDst); + +			Point2D llDst = new Point2D.Float(); +			transform.transform(llSrc, llDst); +			 +			float y1 = (float) upDst.getY(); +			float y2 = (float) llDst.getY(); +			 +			if(y1 > y2) { +				float t = y1; +				y1 = y2; +				y2 = t; +			} +			 +			if(y1 < 0) { +				y2 = y2 + -1 * y1; +				y1 = 0; +			} +			 +			float x1 = (float) upDst.getX(); +			float x2 = (float) llDst.getX(); +			 +			if(x1 > x2) { +				float t = x1; +				x1 = x2; +				x2 = t; +			} +			 +			if(x1 < 0) { +				x2 = x2 + -1 * x1; +				x1 = 0; +			} +			 +			cropBox.setUpperRightX(x2); +			cropBox.setUpperRightY(y2); +			cropBox.setLowerLeftY(y1); +			cropBox.setLowerLeftX(x1); +		} +		return cropBox; +	} + +	/** +	 * Sets the width of the table according to the layout of the document and +	 * calculates the y position where the PDFPTable should be placed. +	 * +	 * @param pdfDataSource +	 *            The PDF document. +	 * @param pdf_table +	 *            The PDFPTable to be placed. +	 * @return Returns the position where the PDFPTable should be placed. +	 * @throws PdfAsException +	 *             F.e. +	 */ +	public static PositioningInstruction adjustSignatureTableandCalculatePosition( +			final PDDocument pdfDataSource, IPDFVisualObject pdf_table, +			TablePos pos, boolean legacy32, boolean legacy40) throws PdfAsException { + +		PdfBoxUtils.checkPDFPermissions(pdfDataSource); +		// get pages of currentdocument + +		int doc_pages = pdfDataSource.getNumberOfPages(); +		int page = doc_pages; +		boolean make_new_page = pos.isNewPage(); +		if (!(pos.isNewPage() || pos.isPauto())) { +			// we should posit signaturtable on this page + +			page = pos.getPage(); +			// System.out.println("XXXXPAGE="+page+" doc_pages="+doc_pages); +			if (page > doc_pages) { +				make_new_page = true; +				page = doc_pages; +				// throw new PDFDocumentException(227, "Page number is to big(=" +				// + page+ +				// ") cannot be parsed."); +			} +		} + +		PDPage pdPage = pdfDataSource.getPage(page-1); +		 +		PDRectangle cropBox = pdPage.getCropBox(); + +		// fallback to MediaBox if Cropbox not available! + +		if (cropBox == null) { +			cropBox = pdPage.getCropBox(); +		} + +		if (cropBox == null) { +			cropBox = pdPage.getCropBox(); +		} + +		// getPagedimensions +		// Rectangle psize = reader.getPageSizeWithRotation(page); +		// int page_rotation = reader.getPageRotation(page); + +		// Integer rotation = pdPage.getRotation(); +		// int page_rotation = rotation.intValue(); + +		int rotation = pdPage.getRotation(); + +		logger.debug("Original CropBox: " + cropBox.toString()); +		 +		cropBox = rotateBox(cropBox, rotation); +		 +		logger.debug("Rotated CropBox: " + cropBox.toString()); +		 +		float page_width = cropBox.getWidth(); +		float page_height = cropBox.getHeight(); + +		logger.debug("CropBox width: " + page_width); +		logger.debug("CropBox heigth: " + page_height); +		 +		// now we can calculate x-position +		float pre_pos_x = SIGNATURE_MARGIN_HORIZONTAL; +		if (!pos.isXauto()) { +			// we do have absolute x +			pre_pos_x = pos.getPosX(); +		} +		// calculate width +		// center +		float pre_width = page_width - 2 * pre_pos_x; +		if (!pos.isWauto()) { +			// we do have absolute width +			pre_width = pos.getWidth(); +			if (pos.isXauto()) { // center x +				pre_pos_x = (page_width - pre_width) / 2; +			} +		} +		final float pos_x = pre_pos_x; +		final float width = pre_width; +		// Signatur table dimensions are complete +		pdf_table.setWidth(width); +		pdf_table.fixWidth(); +		// pdf_table.setTotalWidth(width); +		// pdf_table.setLockedWidth(true); + +		final float table_height = pdf_table.getHeight(); +		// now check pos_y +		float pos_y = pos.getPosY(); + +		// in case an absolute y position is already given OR +		// if the table is related to an invisible signature +		// there is no need for further calculations +		// (fixed adding new page in case of invisible signatures) +		if (!pos.isYauto() || table_height == 0) { +			// we do have y-position too --> all parameters but page ok +			if (make_new_page) { +				page++; +			} +			return new PositioningInstruction(make_new_page, page, pos_x, +					pos_y, pos.rotation); +		} +		// pos_y is auto +		if (make_new_page) { +			// ignore footer in new page +			page++; +			pos_y = page_height - SIGNATURE_MARGIN_VERTICAL; +			return new PositioningInstruction(make_new_page, page, pos_x, +					pos_y, pos.rotation); +		} +		// up to here no checks have to be made if Tablesize and Pagesize are +		// fit +		// Now we have to getfreespace in page and reguard footerline +		float footer_line = pos.getFooterLine(); + +		float pre_page_length = PDFUtilities.calculatePageLength(pdfDataSource, +				page - 1, page_height - footer_line, /* page_rotation, */ +				legacy32, legacy40); + +		if (pre_page_length == Float.NEGATIVE_INFINITY) { +			// we do have an empty page or nothing in area above footerline +			pre_page_length = page_height; +			// no text --> SIGNATURE_BORDER +			pos_y = page_height - SIGNATURE_MARGIN_VERTICAL; +			if (pos_y - footer_line <= table_height) { +				make_new_page = true; +				if (!pos.isPauto()) { +					// we have to correct pagenumber +					page = pdfDataSource.getNumberOfPages(); +				} +				page++; +				// no text --> SIGNATURE_BORDER +				pos_y = page_height - SIGNATURE_MARGIN_VERTICAL; +			} +			return new PositioningInstruction(make_new_page, page, pos_x, +					pos_y, pos.rotation); +		} +		final float page_length = pre_page_length; +		// we do have text take SIGNATURE_MARGIN +		pos_y = page_height - page_length - SIGNATURE_MARGIN_VERTICAL; +		if (pos_y - footer_line <= table_height) { +			make_new_page = true; +			if (!pos.isPauto()) { +				// we have to correct pagenumber in case of absolute page and +				// not enough +				// space +				page = pdfDataSource.getNumberOfPages(); +			} +			page++; +			// no text --> SIGNATURE_BORDER +			pos_y = page_height - SIGNATURE_MARGIN_VERTICAL; +		} +		return new PositioningInstruction(make_new_page, page, pos_x, pos_y, +				pos.rotation); + +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java new file mode 100644 index 00000000..85073568 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java @@ -0,0 +1,73 @@ +package at.gv.egiz.pdfas.lib.impl.pdfbox2.utils; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.encryption.AccessPermission; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsValidationException; + +public class PdfBoxUtils { +	private static final Logger logger = LoggerFactory +			.getLogger(PdfBoxUtils.class); +	 +	public static void checkPDFPermissions(PDDocument doc) +			throws PdfAsValidationException { + +		AccessPermission accessPermission = doc.getCurrentAccessPermission(); +		if (doc.isEncrypted()) { +			throw new PdfAsValidationException("error.pdf.sig.12", null); +		} + +		if (!accessPermission.isOwnerPermission()) { +			throw new PdfAsValidationException("error.pdf.sig.12", null); +		} + +	} + +	public static int countSignatures(PDDocument doc, String sigName) { +		int count = 0; +		COSDictionary trailer = doc.getDocument().getTrailer(); +		COSDictionary root = (COSDictionary) trailer +				.getDictionaryObject(COSName.ROOT); +		COSDictionary acroForm = (COSDictionary) root +				.getDictionaryObject(COSName.ACRO_FORM); +		COSArray fields = (COSArray) acroForm +				.getDictionaryObject(COSName.FIELDS); +		for (int i = 0; i < fields.size(); i++) { +			COSDictionary field = (COSDictionary) fields.getObject(i); +			String type = field.getNameAsString("FT"); +			if ("Sig".equals(type)) { +				String name = field.getString(COSName.T); +				if (name != null) { +					logger.debug("Found Sig: " + name); +					try { +						if (name.startsWith(sigName)) { +							String numberString = name.replace(sigName, ""); + +							logger.debug("Found Number: " + numberString); + +							int SigIDX = Integer.parseInt(numberString); +							if (SigIDX > count) { +								count = SigIDX; +							} +						} +					} catch (Throwable e) { +						logger.info("Found a different Signature, we do not need to count this."); +					} +				} +			} + +		} + +		count++; + +		logger.debug("Returning sig number: " + count); + +		return count; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java new file mode 100644 index 00000000..d105174b --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java @@ -0,0 +1,796 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.signing.pdfbox2; + +import iaik.x509.X509Certificate; + +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSDocument; +import org.apache.pdfbox.cos.COSInteger; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.pdfwriter.COSWriter; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDDocumentCatalog; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.common.PDNumberTreeNode; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement; +import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot; +import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent; +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions; +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.form.PDField; +import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; +import org.apache.pdfbox.rendering.ImageType; +import org.apache.pdfbox.rendering.PDFRenderer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PDFASError; +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.messages.MessageResolver; +import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; +import at.gv.egiz.pdfas.common.utils.StreamUtils; +import at.gv.egiz.pdfas.common.utils.TempFileHelper; +import at.gv.egiz.pdfas.lib.api.ByteArrayDataSource; +import at.gv.egiz.pdfas.lib.api.IConfigurationConstants; +import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; +import at.gv.egiz.pdfas.lib.api.sign.SignParameter; +import at.gv.egiz.pdfas.lib.impl.ErrorExtractor; +import at.gv.egiz.pdfas.lib.impl.SignaturePositionImpl; +import at.gv.egiz.pdfas.lib.impl.configuration.SignatureProfileConfiguration; +import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject; +import at.gv.egiz.pdfas.lib.impl.pdfbox2.positioning.Positioning; +import at.gv.egiz.pdfas.lib.impl.pdfbox2.utils.PdfBoxUtils; +import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderFilter; +import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData; +import at.gv.egiz.pdfas.lib.impl.signing.IPdfSigner; +import at.gv.egiz.pdfas.lib.impl.signing.PDFASSignatureExtractor; +import at.gv.egiz.pdfas.lib.impl.signing.PDFASSignatureInterface; +import at.gv.egiz.pdfas.lib.impl.stamping.IPDFStamper; +import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject; +import at.gv.egiz.pdfas.lib.impl.stamping.TableFactory; +import at.gv.egiz.pdfas.lib.impl.stamping.ValueResolver; +import at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2.PDFAsVisualSignatureProperties; +import at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2.PdfBoxVisualObject; +import at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2.StamperFactory; +import at.gv.egiz.pdfas.lib.impl.status.OperationStatus; +import at.gv.egiz.pdfas.lib.impl.status.PDFObject; +import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature; +import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; +import at.knowcenter.wag.egov.egiz.pdf.TablePos; +import at.knowcenter.wag.egov.egiz.table.Table; + +public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { + +	private static final Logger logger = LoggerFactory.getLogger(PADESPDFBOXSigner.class); + +	public void signPDF(PDFObject genericPdfObject, RequestedSignature requestedSignature, +			PDFASSignatureInterface genericSigner) throws PdfAsException { +		//String fisTmpFile = null; + +		PDFAsVisualSignatureProperties properties = null; + +		if (!(genericPdfObject instanceof PDFBOXObject)) { +			// tODO: +			throw new PdfAsException(); +		} + +		PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject; + +		if (!(genericSigner instanceof PDFASPDFBOXSignatureInterface)) { +			// tODO: +			throw new PdfAsException(); +		} + +		PDFASPDFBOXSignatureInterface signer = (PDFASPDFBOXSignatureInterface) genericSigner; + +		PDDocument doc = null; +		SignatureOptions options = new SignatureOptions(); +		COSDocument visualSignatureDocumentGuard = null; +		try { + + +			doc = pdfObject.getDocument(); + +			SignaturePlaceholderData signaturePlaceholderData = PlaceholderFilter +					.checkPlaceholderSignature(pdfObject.getStatus(), pdfObject.getStatus().getSettings()); + +			TablePos tablePos = null; + +			if (signaturePlaceholderData != null) { +				// Placeholder found! +				logger.info("Placeholder data found."); +				if (signaturePlaceholderData.getProfile() != null) { +					logger.debug("Placeholder Profile set to: " + signaturePlaceholderData.getProfile()); +					requestedSignature.setSignatureProfileID(signaturePlaceholderData.getProfile()); +				} + +				tablePos = signaturePlaceholderData.getTablePos(); +				if (tablePos != null) { + +					SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus() +							.getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID()); + +					float minWidth = signatureProfileConfiguration.getMinWidth(); + +					if(minWidth > 0) { +						if (tablePos.getWidth() < minWidth) { +							tablePos.width = minWidth; +							logger.debug("Correcting placeholder with to minimum width {}", minWidth); +						} +					} +					logger.debug("Placeholder Position set to: " + tablePos.toString()); +				} +			} + +			PDSignature signature = new PDSignature(); +			signature.setFilter(COSName.getPDFName(signer.getPDFFilter())); // default +			// filter +			signature.setSubFilter(COSName.getPDFName(signer.getPDFSubFilter())); + +			SignatureProfileSettings signatureProfileSettings = TableFactory +					.createProfile(requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings()); + +			ValueResolver resolver = new ValueResolver(requestedSignature, pdfObject.getStatus()); +			String signerName = resolver.resolve("SIG_SUBJECT", signatureProfileSettings.getValue("SIG_SUBJECT"), +					signatureProfileSettings); + +			signature.setName(signerName); +			signature.setSignDate(Calendar.getInstance()); +			String signerReason = signatureProfileSettings.getSigningReason(); + +			if (signerReason == null) { +				signerReason = "PAdES Signature"; +			} + +			signature.setReason(signerReason); +			logger.debug("Signing reason: " + signerReason); + +			logger.debug("Signing @ " + signer.getSigningDate().getTime().toString()); +			// the signing date, needed for valid signature +			// signature.setSignDate(signer.getSigningDate()); + +			signer.setPDSignature(signature); + +			int signatureSize = 0x1000; +			try { +				String reservedSignatureSizeString = signatureProfileSettings.getValue(SIG_RESERVED_SIZE); +				if (reservedSignatureSizeString != null) { +					signatureSize = Integer.parseInt(reservedSignatureSizeString); +				} +				logger.debug("Reserving {} bytes for signature", signatureSize); +			} catch (NumberFormatException e) { +				logger.warn("Invalid configuration value: {} should be a number using 0x1000", SIG_RESERVED_SIZE); +			} +			options.setPreferredSignatureSize(signatureSize); + +			// Is visible Signature +			if (requestedSignature.isVisual()) { +				logger.info("Creating visual siganture block"); + +				SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus() +						.getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID()); + +				if (tablePos == null) { +					// ================================================================ +					// PositioningStage (visual) -> find position or use +					// fixed +					// position + +					String posString = pdfObject.getStatus().getSignParamter().getSignaturePosition(); + +					TablePos signaturePos = null; + +					String signaturePosString = signatureProfileConfiguration.getDefaultPositioning(); + +					if (signaturePosString != null) { +						logger.debug("using signature Positioning: " + signaturePos); +						signaturePos = new TablePos(signaturePosString); +					} + +					logger.debug("using Positioning: " + posString); + +					if (posString != null) { +						// Merge Signature Position +						tablePos = new TablePos(posString, signaturePos); +					} else { +						// Fallback to signature Position! +						tablePos = signaturePos; +					} + +					if (tablePos == null) { +						// Last Fallback default position +						tablePos = new TablePos(); +					} +				} + +				boolean legacy32Position = signatureProfileConfiguration.getLegacy32Positioning(); +				boolean legacy40Position = signatureProfileConfiguration.getLegacy40Positioning(); + +				// create Table describtion +				Table main = TableFactory.createSigTable(signatureProfileSettings, MAIN, pdfObject.getStatus(), +						requestedSignature); + +				IPDFStamper stamper = StamperFactory.createDefaultStamper(pdfObject.getStatus().getSettings()); + +				IPDFVisualObject visualObject = stamper.createVisualPDFObject(pdfObject, main); + +				/* +				 * PDDocument originalDocument = PDDocument .load(new +				 * ByteArrayInputStream(pdfObject.getStatus() +				 * .getPdfObject().getOriginalDocument())); +				 */ + +				PositioningInstruction positioningInstruction = Positioning.determineTablePositioning(tablePos, "", +						doc, visualObject, legacy32Position, legacy40Position); + +				logger.debug("Positioning: {}", positioningInstruction.toString()); + +				if (positioningInstruction.isMakeNewPage()) { +					int last = doc.getNumberOfPages() - 1; +					PDDocumentCatalog root = doc.getDocumentCatalog(); +					PDPage lastPage = root.getPages().get(last); +					root.getPages().getCOSObject().setNeedToBeUpdated(true); +					PDPage p = new PDPage(lastPage.getMediaBox()); +					p.setResources(new PDResources()); +					p.setRotation(lastPage.getRotation()); +					doc.addPage(p); +				} + +				// handle rotated page +				int targetPageNumber = positioningInstruction.getPage(); +				logger.debug("Target Page: " + targetPageNumber); +				PDPage targetPage = doc.getPages().get(targetPageNumber - 1); +				int rot = targetPage.getRotation(); +				logger.debug("Page rotation: " + rot); +				// positioningInstruction.setRotation(positioningInstruction.getRotation() +				// + rot); +				logger.debug("resulting Sign rotation: " + positioningInstruction.getRotation()); + +				SignaturePositionImpl position = new SignaturePositionImpl(); +				position.setX(positioningInstruction.getX()); +				position.setY(positioningInstruction.getY()); +				position.setPage(positioningInstruction.getPage()); +				position.setHeight(visualObject.getHeight()); +				position.setWidth(visualObject.getWidth()); + +				requestedSignature.setSignaturePosition(position); + +				properties = new PDFAsVisualSignatureProperties(pdfObject.getStatus().getSettings(), pdfObject, +						(PdfBoxVisualObject) visualObject, positioningInstruction, signatureProfileSettings); + +				properties.buildSignature(); + +				/* +				 * ByteArrayOutputStream sigbos = new +				 * ByteArrayOutputStream(); +				 * sigbos.write(StreamUtils.inputStreamToByteArray +				 * (properties .getVisibleSignature())); sigbos.close(); +				 */ + +				if (signaturePlaceholderData != null) { +					// Placeholder found! +					// replace placeholder + +					PDImageXObject img = PDImageXObject.createFromFile("/placeholder/empty.jpg", doc); + +					img.getCOSObject().setNeedToBeUpdated(true); +					//							PDDocumentCatalog root = doc.getDocumentCatalog(); +					//							PDPageNode rootPages = root.getPages(); +					//							List<PDPage> kids = new ArrayList<PDPage>(); +					//							rootPages.getAllKids(kids); +					int pageNumber = positioningInstruction.getPage(); +					PDPage page = doc.getPages().get(pageNumber - 1); + +					logger.info("Placeholder name: " + signaturePlaceholderData.getPlaceholderName()); +					COSDictionary xobjectsDictionary = (COSDictionary) page.getResources().getCOSObject() +							.getDictionaryObject(COSName.XOBJECT); +					xobjectsDictionary.setItem(signaturePlaceholderData.getPlaceholderName(), img); +					xobjectsDictionary.setNeedToBeUpdated(true); +					page.getResources().getCOSObject().setNeedToBeUpdated(true); +					logger.info("Placeholder name: " + signaturePlaceholderData.getPlaceholderName()); + +				} + +				if (signatureProfileSettings.isPDFA()) { +					PDDocumentCatalog root = doc.getDocumentCatalog(); +					COSBase base = root.getCOSObject().getItem(COSName.OUTPUT_INTENTS); +					if (base == null) { +						InputStream colorProfile = null; +						try { +							colorProfile = PDDocumentCatalog.class +									.getResourceAsStream("/icm/sRGB Color Space Profile.icm"); + +							try { +								PDOutputIntent oi = new PDOutputIntent(doc, colorProfile); +								oi.setInfo("sRGB IEC61966-2.1"); +								oi.setOutputCondition("sRGB IEC61966-2.1"); +								oi.setOutputConditionIdentifier("sRGB IEC61966-2.1"); +								oi.setRegistryName("http://www.color.org"); + +								root.addOutputIntent(oi); +								root.getCOSObject().setNeedToBeUpdated(true); +								logger.info("added Output Intent"); +							} catch (Throwable e) { +								e.printStackTrace(); +								throw new PdfAsException("Failed to add Output Intent", e); +							} +						} finally { +							IOUtils.closeQuietly(colorProfile); +						} +					} +				} + +				options.setPage(positioningInstruction.getPage()); + +				options.setVisualSignature(properties.getVisibleSignature()); +			} + +			visualSignatureDocumentGuard = options.getVisualSignature(); + +			doc.addSignature(signature, signer, options); + +			String sigFieldName = signatureProfileSettings.getSignFieldValue(); + +			if (sigFieldName == null) { +				sigFieldName = "PDF-AS Signatur"; +			} + +			int count = PdfBoxUtils.countSignatures(doc, sigFieldName); + +			sigFieldName = sigFieldName + count; + +			PDAcroForm acroFormm = doc.getDocumentCatalog().getAcroForm(); + +			// PDStructureTreeRoot pdstRoot = +			// doc.getDocumentCatalog().getStructureTreeRoot(); +			// COSDictionary dic = +			// doc.getDocumentCatalog().getCOSDictionary(); +			// PDStructureElement el = new PDStructureElement("Widget", +			// pdstRoot); + +			PDSignatureField signatureField = null; +			if (acroFormm != null) { +				@SuppressWarnings("unchecked") +				List<PDField> fields = acroFormm.getFields(); + +				if (fields != null) { +					for (PDField pdField : fields) { +						if (pdField != null) { +							if (pdField instanceof PDSignatureField) { +								PDSignatureField tmpSigField = (PDSignatureField) pdField; + +								if (tmpSigField.getSignature() != null +										&& tmpSigField.getSignature().getCOSObject() != null) { +									if (tmpSigField.getSignature().getCOSObject() +											.equals(signature.getCOSObject())) { +										signatureField = (PDSignatureField) pdField; + +									} +								} +							} +						} +					} +				} else { +					logger.warn("Failed to name Signature Field! [Cannot find Field list in acroForm!]"); +				} + +				if (signatureField != null) { +					signatureField.setPartialName(sigFieldName); +				} +				if (properties != null) { +					signatureField.setAlternateFieldName(properties.getAlternativeTableCaption()); +				} else { +					signatureField.setAlternateFieldName(sigFieldName); +				} +			} else { +				logger.warn("Failed to name Signature Field! [Cannot find acroForm!]"); +			} + +			// PDF-UA +			logger.info("Adding pdf/ua content."); +			try { +				if(true) +					throw new Exception("skip"); +				PDDocumentCatalog root = doc.getDocumentCatalog(); +				PDStructureTreeRoot structureTreeRoot = root.getStructureTreeRoot(); +				if (structureTreeRoot != null) { +					logger.info("Tree Root: {}", structureTreeRoot.toString()); +					List<Object> kids = structureTreeRoot.getKids(); + +					if (kids == null) { +						logger.info("No kid-elements in structure tree Root, maybe not PDF/UA document"); +					} + +					PDStructureElement docElement = null; +					for (Object k : kids) { +						if (k instanceof PDStructureElement) { +							docElement = (PDStructureElement) k; +							break; + +						} +					} + +					PDStructureElement sigBlock = new PDStructureElement("Form", docElement); + +					// create object dictionary and add as child element +					COSDictionary objectDic = new COSDictionary(); +					objectDic.setName("Type", "OBJR"); +					objectDic.setItem("Pg", signatureField.getWidget().getPage()); +					objectDic.setItem("Obj", signatureField.getWidget()); + +					List<Object> l = new ArrayList<Object>(); +					l.add(objectDic); +					sigBlock.setKids(l); +					sigBlock.setPage(signatureField.getWidget().getPage()); + + +					sigBlock.setTitle("Signature Table"); +					sigBlock.setParent(docElement); +					docElement.appendKid(sigBlock); + +					// Create and add Attribute dictionary to mitigate PAC +					// warning +					COSDictionary sigBlockDic = (COSDictionary) sigBlock.getCOSObject(); +					COSDictionary sub = new COSDictionary(); + +					sub.setName("O", "Layout"); +					sub.setName("Placement", "Block"); +					sigBlockDic.setItem(COSName.A, sub); +					sigBlockDic.setNeedToBeUpdated(true); + +					// Modify number tree +					PDNumberTreeNode ntn = structureTreeRoot.getParentTree(); +					int parentTreeNextKey = structureTreeRoot.getParentTreeNextKey(); +					if (ntn == null) { +						ntn = new PDNumberTreeNode(objectDic, null); +						logger.info("No number-tree-node found!"); +					} + +					COSArray ntnKids = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.KIDS); +					COSArray ntnNumbers = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.NUMS); + +					if(ntnNumbers == null && ntnKids != null){//no number array, so continue with the kids array + +						//create dictionary with limits and nums array +						COSDictionary pTreeEntry = new COSDictionary(); +						COSArray limitsArray = new COSArray(); +						//limits for exact one entry +						limitsArray.add(COSInteger.get(parentTreeNextKey)); +						limitsArray.add(COSInteger.get(parentTreeNextKey)); + +						COSArray numsArray = new COSArray(); +						numsArray.add(COSInteger.get(parentTreeNextKey)); +						numsArray.add(sigBlock); + +						pTreeEntry.setItem(COSName.NUMS, numsArray); +						pTreeEntry.setItem(COSName.LIMITS, limitsArray); + +						PDNumberTreeNode newKidsElement = new PDNumberTreeNode(pTreeEntry, PDNumberTreeNode.class); + +						ntnKids.add(newKidsElement); +						ntnKids.setNeedToBeUpdated(true); + + +					}else if(ntnNumbers != null && ntnKids == null){ + +						int arrindex = ntnNumbers.size(); + +						ntnNumbers.add(arrindex, COSInteger.get(parentTreeNextKey)); +						ntnNumbers.add(arrindex + 1, sigBlock.getCOSObject()); + +						ntnNumbers.setNeedToBeUpdated(true); + +						structureTreeRoot.setParentTree(ntn); + +					}else if(ntnNumbers == null && ntnKids == null){ +						//document is not pdfua conform before signature creation +						throw new PdfAsException("error.pdf.sig.pdfua.1"); +					}else{ +						//this is not allowed +						throw new PdfAsException("error.pdf.sig.pdfua.1"); +					} + +					// set StructureParent for signature field annotation +					signatureField.getWidget().setStructParent(parentTreeNextKey); + +					//Increase the next Key value in the structure tree root +					structureTreeRoot.setParentTreeNextKey(parentTreeNextKey+1); + +					// add the Tabs /S Element for Tabbing through annots +					PDPage p = signatureField.getWidget().getPage(); +					p.getCOSObject().setName("Tabs", "S"); +					p.getCOSObject().setNeedToBeUpdated(true); + +					//check alternative signature field name +					if (signatureField != null) { +						if(signatureField.getAlternateFieldName().equals("")) +							signatureField.setAlternateFieldName(sigFieldName); +					} + + +					ntn.getCOSObject().setNeedToBeUpdated(true); +					sigBlock.getCOSObject().setNeedToBeUpdated(true); +					structureTreeRoot.getCOSObject().setNeedToBeUpdated(true); +					objectDic.setNeedToBeUpdated(true); +					docElement.getCOSObject().setNeedToBeUpdated(true); + +				} +			} catch (Throwable e) { +				if (signatureProfileSettings.isPDFUA() == true) { +					logger.error("Could not create PDF-UA conform document!"); +					throw new PdfAsException("error.pdf.sig.pdfua.1", e); +				} else { +					logger.info("Could not create PDF-UA conform signature"); +				} +			} + +			if (requestedSignature.isVisual()) { + +				// if(requestedSignature.getSignaturePosition().) +				/* +				 * PDAcroForm acroForm = +				 * doc.getDocumentCatalog().getAcroForm(); if (acroForm != +				 * null) { +				 *  +				 * @SuppressWarnings("unchecked") List<PDField> fields = +				 * acroForm.getFields(); PDSignatureField signatureField = +				 * null; +				 *  +				 * if (fields != null) { for (PDField pdField : fields) { if +				 * (pdField instanceof PDSignatureField) { if +				 * (((PDSignatureField) pdField).getSignature() +				 * .getDictionary() .equals(signature.getDictionary())) { +				 * signatureField = (PDSignatureField) pdField; } } } } else +				 * { logger.warn( +				 * "Failed to apply rotation! [Cannot find Field list in acroForm!]" +				 * ); } +				 *  +				 * if (signatureField != null) { if +				 * (signatureField.getWidget() != null) { if +				 * (signatureField.getWidget() +				 * .getAppearanceCharacteristics() == null) { +				 * PDAppearanceCharacteristicsDictionary dict = new +				 * PDAppearanceCharacteristicsDictionary( new +				 * COSDictionary()); signatureField.getWidget() +				 * .setAppearanceCharacteristics(dict); } +				 *  +				 * if (signatureField.getWidget() +				 * .getAppearanceCharacteristics() != null) { +				 * signatureField.getWidget() +				 * .getAppearanceCharacteristics() .setRotation(90); } } } +				 * else { logger.warn( +				 * "Failed to apply rotation! [Cannot find signature Field!]" +				 * ); } } else { logger.warn( +				 * "Failed to apply rotation! [Cannot find acroForm!]" ); } +				 */ +			} + + + + +			try { +				ByteArrayOutputStream bos = new ByteArrayOutputStream(); +				 +				synchronized (doc) { +					this.saveIncremental(bos, doc, pdfObject.getOriginalDocument().getInputStream(), signer); +					pdfObject.setSignedDocument(bos.toByteArray()); +				} +				 +			} finally { +				if (options != null) { +					if (options.getVisualSignature() != null) { +						options.getVisualSignature().close(); +					} +				} +			} + + +			System.gc(); +		} catch (IOException e) { +			logger.warn(MessageResolver.resolveMessage("error.pdf.sig.01"), e); +			throw new PdfAsException("error.pdf.sig.01", e); +		} finally { +			if (doc != null) { +				try { +					doc.close(); +				} catch (IOException e) { +					logger.debug("Failed to close COS Doc!", e); +					// Ignore +				} +			} + +			logger.debug("Signature done!"); + +		} +	} + +	public void saveIncremental(OutputStream outStream, PDDocument doc, InputStream inStream, SignatureInterface signer) throws IOException{ +		COSWriter writer = null; +		try +		{ +			writer = new COSWriter(outStream, inStream); +			writer.write(doc, signer); +			writer.close(); +		} +		finally +		{ +			if (writer != null) +			{ +				writer.close(); +			} +		} +	} + +	@Override +	public PDFObject buildPDFObject(OperationStatus operationStatus) { +		return new PDFBOXObject(operationStatus); +	} + +	@Override +	public PDFASSignatureInterface buildSignaturInterface(IPlainSigner signer, SignParameter parameters, +			RequestedSignature requestedSignature) { +		return new PdfboxSignerWrapper(signer, parameters, requestedSignature); +	} + +	@Override +	public PDFASSignatureExtractor buildBlindSignaturInterface(X509Certificate certificate, String filter, +			String subfilter, Calendar date) { +		return new SignatureDataExtractor(certificate, filter, subfilter, date); +	} + +	@Override +	public void checkPDFPermissions(PDFObject genericPdfObject) throws PdfAsException { +		if (!(genericPdfObject instanceof PDFBOXObject)) { +			// tODO: +			throw new PdfAsException(); +		} + +		PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject; +		PdfBoxUtils.checkPDFPermissions(pdfObject.getDocument()); +	} + +	@Override +	public byte[] rewritePlainSignature(byte[] plainSignature) { +		String signature = new COSString(plainSignature).toHexString(); +		byte[] pdfSignature = signature.getBytes(); +		return pdfSignature; +	} + +	@Override +	public Image generateVisibleSignaturePreview(SignParameter parameter, java.security.cert.X509Certificate cert, +			int resolution, OperationStatus status, RequestedSignature requestedSignature) throws PDFASError { +		try { +			PDFBOXObject pdfObject = (PDFBOXObject) status.getPdfObject(); + +			PDDocument origDoc = new PDDocument(); +			origDoc.addPage(new PDPage(PDRectangle.A4)); +			ByteArrayOutputStream baos = new ByteArrayOutputStream(); +			origDoc.save(baos); +			baos.close(); + +			pdfObject.setOriginalDocument(new ByteArrayDataSource(baos.toByteArray())); + +			SignatureProfileSettings signatureProfileSettings = TableFactory +					.createProfile(requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings()); + +			// create Table describtion +			Table main = TableFactory.createSigTable(signatureProfileSettings, MAIN, pdfObject.getStatus(), +					requestedSignature); + +			IPDFStamper stamper = StamperFactory.createDefaultStamper(pdfObject.getStatus().getSettings() ); + +			IPDFVisualObject visualObject = stamper.createVisualPDFObject(pdfObject, main); + +			SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus() +					.getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID()); + +			String signaturePosString = signatureProfileConfiguration.getDefaultPositioning(); +			PositioningInstruction positioningInstruction = null; +			if (signaturePosString != null) { +				positioningInstruction = Positioning.determineTablePositioning(new TablePos(signaturePosString), "", +						origDoc, visualObject, false, false); +			} else { +				positioningInstruction = Positioning.determineTablePositioning(new TablePos(), "", origDoc, +						visualObject, false, false); +			} + +			origDoc.close(); + +			SignaturePositionImpl position = new SignaturePositionImpl(); +			position.setX(positioningInstruction.getX()); +			position.setY(positioningInstruction.getY()); +			position.setPage(positioningInstruction.getPage()); +			position.setHeight(visualObject.getHeight()); +			position.setWidth(visualObject.getWidth()); + +			requestedSignature.setSignaturePosition(position); + +			PDFAsVisualSignatureProperties properties = new PDFAsVisualSignatureProperties( +					pdfObject.getStatus().getSettings(), pdfObject, (PdfBoxVisualObject) visualObject, +					positioningInstruction, signatureProfileSettings); + +			properties.buildSignature(); +			PDDocument visualDoc; +			synchronized (PDDocument.class) { +				visualDoc = PDDocument.load(properties.getVisibleSignature()); +			} +			// PDPageable pageable = new PDPageable(visualDoc); + +			PDPage firstPage = visualDoc.getDocumentCatalog().getPages().get(0); + +			float stdRes = 72; +			float targetRes = resolution; +			float factor = targetRes / stdRes; + + +			int targetPageNumber = 0;//TODO: is this always the case +			PDFRenderer pdfRenderer = new PDFRenderer(visualDoc); +			BufferedImage outputImage = pdfRenderer.renderImageWithDPI(targetPageNumber, targetRes, ImageType.ARGB); + +			//BufferedImage outputImage = firstPage.convertToImage(BufferedImage.TYPE_4BYTE_ABGR, (int) targetRes); + +			BufferedImage cutOut = new BufferedImage((int) (position.getWidth() * factor), +					(int) (position.getHeight() * factor), BufferedImage.TYPE_4BYTE_ABGR); + +			Graphics2D graphics = (Graphics2D) cutOut.getGraphics(); + +			graphics.drawImage(outputImage, 0, 0, cutOut.getWidth(), cutOut.getHeight(), (int) (1 * factor), +					(int) (outputImage.getHeight() - ((position.getHeight() + 1) * factor)), +					(int) ((1 + position.getWidth()) * factor), (int) (outputImage.getHeight() +							- ((position.getHeight() + 1) * factor) + (position.getHeight() * factor)), +							null); +			return cutOut; +		} catch (PdfAsException e) { +			logger.warn("PDF-AS  Exception", e); +			throw ErrorExtractor.searchPdfAsError(e, status); +		} catch (Throwable e) { +			logger.warn("Unexpected Throwable  Exception", e); +			throw ErrorExtractor.searchPdfAsError(e, status); +		} +	} +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXExtractorInterface.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXExtractorInterface.java new file mode 100644 index 00000000..c99e7c59 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXExtractorInterface.java @@ -0,0 +1,7 @@ +package at.gv.egiz.pdfas.lib.impl.signing.pdfbox2; + +import at.gv.egiz.pdfas.lib.impl.signing.PDFASSignatureExtractor; + +public interface PDFASPDFBOXExtractorInterface extends PDFASSignatureExtractor, PDFASPDFBOXSignatureInterface { + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXSignatureInterface.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXSignatureInterface.java new file mode 100644 index 00000000..cc260ece --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXSignatureInterface.java @@ -0,0 +1,10 @@ +package at.gv.egiz.pdfas.lib.impl.signing.pdfbox2; + +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface; + +import at.gv.egiz.pdfas.lib.impl.signing.PDFASSignatureInterface; + +public interface PDFASPDFBOXSignatureInterface extends PDFASSignatureInterface, SignatureInterface { +	public void setPDSignature(PDSignature signature); +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PdfboxSignerWrapper.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PdfboxSignerWrapper.java new file mode 100644 index 00000000..7aaf1510 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PdfboxSignerWrapper.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.signing.pdfbox2; + +import java.io.IOException; +import java.io.InputStream; +import java.security.SignatureException; +import java.util.Calendar; + +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.exceptions.PdfAsWrappedIOException; +import at.gv.egiz.pdfas.common.utils.PDFUtils; +import at.gv.egiz.pdfas.common.utils.StreamUtils; +import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; +import at.gv.egiz.pdfas.lib.api.sign.SignParameter; +import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature; + +public class PdfboxSignerWrapper implements PDFASPDFBOXSignatureInterface { + +	private static final Logger logger = LoggerFactory +			.getLogger(PdfboxSignerWrapper.class); + +	private IPlainSigner signer; +	private PDSignature signature; +	private int[] byteRange; +	private Calendar date; +	private SignParameter parameters; +	private RequestedSignature requestedSignature; + +	public PdfboxSignerWrapper(IPlainSigner signer, SignParameter parameters, RequestedSignature requestedSignature) { +		this.signer = signer; +		this.date = Calendar.getInstance(); +		this.parameters = parameters; +		this.requestedSignature = requestedSignature; +	} + +	public byte[] sign(InputStream inputStream) throws IOException { +		byte[] data = StreamUtils.inputStreamToByteArray(inputStream); +		byteRange = PDFUtils.extractSignatureByteRange(data); +		int[] byteRange2 = signature.getByteRange(); +		logger.debug("Byte Range 2: " + byteRange2); +		try { +			logger.debug("Signing with Pdfbox Wrapper"); +			byte[] signature = signer.sign(data, byteRange, this.parameters, this.requestedSignature); + +			return signature; +		} catch (PdfAsException e) { +			throw new PdfAsWrappedIOException(e); +		} +	} + +	public int[] getByteRange() { +		return byteRange; +	} + +	public String getPDFSubFilter() { +		return this.signer.getPDFSubFilter(); +	} + +	public String getPDFFilter() { +		return this.signer.getPDFFilter(); +	} + +	public void setPDSignature(PDSignature signature) { +		this.signature = signature; +	} + +	public Calendar getSigningDate() { +		return this.date; +	} +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/SignatureDataExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/SignatureDataExtractor.java new file mode 100644 index 00000000..78e48e5e --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/SignatureDataExtractor.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.signing.pdfbox2; + +import iaik.x509.X509Certificate; + +import java.io.IOException; +import java.io.InputStream; +import java.security.SignatureException; +import java.util.Calendar; + +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; + +import at.gv.egiz.pdfas.common.utils.StreamUtils; + +public class SignatureDataExtractor implements PDFASPDFBOXExtractorInterface { + +	protected X509Certificate certificate; +	protected byte[] signatureData; +	 +	protected String pdfSubFilter; +	protected String pdfFilter; +	protected PDSignature signature; +	protected int[] byteRange; +	protected Calendar date; +	 +	public SignatureDataExtractor(X509Certificate certificate,  +			String filter, String subfilter, Calendar date) { +		this.certificate = certificate; +		this.pdfFilter = filter; +		this.pdfSubFilter = subfilter; +		this.date = date; +	} +	 +	public X509Certificate getCertificate() { +		return certificate; +	} + +	public String getPDFSubFilter() { +		return this.pdfSubFilter; +	} + +	public String getPDFFilter() { +		return this.pdfFilter; +	} + +	public byte[] getSignatureData() { +		return this.signatureData; +	} + +	public byte[] sign(InputStream content) throws IOException { +		signatureData = StreamUtils.inputStreamToByteArray(content); +		byteRange = this.signature.getByteRange(); +		return new byte[] { 0 }; +	} + +	public void setPDSignature(PDSignature signature) { +		this.signature = signature; +	} + +	public int[] getByteRange() { +		return byteRange; +	} + +	public Calendar getSigningDate() { +		return this.date; +	} +	 +	 +	 +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/FontInfoCache.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/FontInfoCache.java new file mode 100644 index 00000000..c208820e --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/FontInfoCache.java @@ -0,0 +1,8 @@ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +public class FontInfoCache { +	String filename; +	String fontName; +	String fontFamily; +	String fontPath; +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/IDGenerator.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/IDGenerator.java new file mode 100644 index 00000000..ef898d74 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/IDGenerator.java @@ -0,0 +1,5 @@ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +public interface IDGenerator { +	public String createHashedId(String value); +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/ImageObject.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/ImageObject.java new file mode 100644 index 00000000..16af5cfb --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/ImageObject.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; + + +public class ImageObject { +	private PDImageXObject image; +	private float width; +	private float height; +	 +	public ImageObject(PDImageXObject image, float width, float height) { +		this.image = image; +		this.width = width; +		this.height = height; +	} + +	public PDImageXObject getImage() { +		return image; +	} + +	public void setImage(PDImageXObject image) { +		this.image = image; +	} + +	public float getWidth() { +		return width; +	} + +	public void setWidth(float width) { +		this.width = width; +	} + +	public float getHeight() { +		return height; +	} + +	public void setHeight(float height) { +		this.height = height; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsTemplateCreator.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsTemplateCreator.java new file mode 100644 index 00000000..b8c15119 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsTemplateCreator.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDFTemplateCreator; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDFTemplateStructure; +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; + +public class PDFAsTemplateCreator extends PDFTemplateCreator { + +	PDFAsVisualSignatureBuilder pdfBuilder; +    private static final Logger logger = LoggerFactory.getLogger(PDFAsTemplateCreator.class); +	 +	public PDFAsTemplateCreator(PDFAsVisualSignatureBuilder bookBuilder) { +		super(bookBuilder); +		this.pdfBuilder = bookBuilder; +	} + +	 +	public InputStream buildPDF(PDFAsVisualSignatureDesigner properties, PDDocument originalDocument) +			throws IOException, PdfAsException { +		logger.debug("pdf building has been started"); +        PDFTemplateStructure pdfStructure = pdfBuilder.getStructure(); + +        // we create array of [Text, ImageB, ImageC, ImageI] +        this.pdfBuilder.createProcSetArray(); +         +        //create page +        this.pdfBuilder.createPage(properties); +        PDPage page = pdfStructure.getPage(); + +        //create template +        this.pdfBuilder.createTemplate(page); +        PDDocument template = pdfStructure.getTemplate(); +         +        //create /AcroForm +        this.pdfBuilder.createAcroForm(template); +        PDAcroForm acroForm = pdfStructure.getAcroForm(); + +        // AcroForm contains singature fields +        this.pdfBuilder.createSignatureField(acroForm); +        PDSignatureField pdSignatureField = pdfStructure.getSignatureField(); +         +        // create signature +        this.pdfBuilder.createSignature(pdSignatureField, page, properties.getSignatureFieldName()); +        +        // that is /AcroForm/DR entry +        this.pdfBuilder.createAcroFormDictionary(acroForm, pdSignatureField); +         +        // create AffineTransform +        this.pdfBuilder.createAffineTransform(properties.getAffineTransformParams()); +        //AffineTransform transform = pdfStructure.getAffineTransform(); +        +        // rectangle, formatter, image. /AcroForm/DR/XObject contains that form +        this.pdfBuilder.createSignatureRectangle(pdSignatureField, properties, properties.getRotation() + properties.getPageRotation()); +        this.pdfBuilder.createFormaterRectangle(properties.getFormaterRectangleParams()); +        PDRectangle formater = pdfStructure.getFormatterRectangle(); +         +        //this.pdfBuilder.createSignatureImage(template, properties.getImageStream()); + +        // create form stream, form and  resource.  +        this.pdfBuilder.createHolderFormStream(template); +        PDStream holderFormStream = pdfStructure.getHolderFormStream(); +        this.pdfBuilder.createHolderFormResources(); +        PDResources holderFormResources = pdfStructure.getHolderFormResources(); +        this.pdfBuilder.createHolderForm(holderFormResources, holderFormStream, formater); +         +        // that is /AP entry the appearance dictionary. +        this.pdfBuilder.createAppearanceDictionary(pdfStructure.getHolderForm(), pdSignatureField,  +        		properties.getRotation() + properties.getPageRotation()); +         +        // inner formstream, form and resource (hlder form containts inner form) +        this.pdfBuilder.createInnerFormStreamPdfAs(template, originalDocument); +        this.pdfBuilder.createInnerFormResource(); +        PDResources innerFormResource = pdfStructure.getInnerFormResources(); +        this.pdfBuilder.createInnerForm(innerFormResource, pdfStructure.getInnerFormStream(), formater); +        PDFormXObject innerForm = pdfStructure.getInnerForm(); +        +        // inner form must be in the holder form as we wrote +        this.pdfBuilder.insertInnerFormToHolerResources(innerForm, holderFormResources); +         +        //  Image form is in this structure: /AcroForm/DR/FRM0/Resources/XObject/n0 +        //this.pdfBuilder.createImageFormStream(template); +        //PDStream imageFormStream = pdfStructure.getImageFormStream(); +        //this.pdfBuilder.createImageFormResources(); +        //PDResources imageFormResources = pdfStructure.getImageFormResources(); +        //this.pdfBuilder.createImageForm(imageFormResources, innerFormResource, imageFormStream, formater, transform, +        //        pdfStructure.getJpedImage()); +        +        // now inject procSetArray +        /*this.pdfBuilder.injectProcSetArray(innerForm, page, innerFormResource, imageFormResources, holderFormResources, +                pdfStructure.getProcSet());*/ +        this.pdfBuilder.injectProcSetArray(innerForm, page, innerFormResource, null, holderFormResources, +                pdfStructure.getProcSet()); +         + +        /*String imgFormName = pdfStructure.getImageFormName(); +        String imgName = pdfStructure.getImageName();*/ +        String innerFormName = pdfStructure.getInnerFormName().getName(); + +        // now create Streams of AP +        /*this.pdfBuilder.injectAppearanceStreams(holderFormStream, imageFormStream, imageFormStream, imgFormName, +                imgName, innerFormName, properties);*/ +        this.pdfBuilder.injectAppearanceStreams(holderFormStream, null, null, null, +        		null, innerFormName, properties); +        this.pdfBuilder.createVisualSignature(template); +        this.pdfBuilder.createWidgetDictionary(pdSignatureField, holderFormResources); +         +        ByteArrayInputStream in = null; +     +        	//COSDocument doc = pdfStructure.getVisualSignature(); +        	//doc. +            //in = pdfStructure.getTemplateAppearanceStream(); +        	ByteArrayOutputStream baos = new ByteArrayOutputStream(); +        	template.save(baos); +        	baos.close(); +        	in = new ByteArrayInputStream(baos.toByteArray()); +         + +        logger.debug("stream returning started, size= " + in.available()); +         +        // we must close the document +        this.pdfBuilder.closeTemplate(template); +         +        // return result of the stream  +        return in; +	} +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java new file mode 100644 index 00000000..30487ead --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java @@ -0,0 +1,659 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.io.IOUtils; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdfparser.PDFParser; +import org.apache.pdfbox.pdfparser.PDFStreamParser; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; +import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory; +import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSigBuilder; +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.pdmodel.interactive.form.PDField; +import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.common.utils.ImageUtils; +import at.knowcenter.wag.egov.egiz.table.Entry; + +public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder implements +		IDGenerator { + +	private static final Logger logger = LoggerFactory +			.getLogger(PDFAsVisualSignatureBuilder.class); + +	private PDFAsVisualSignatureProperties properties; +	private PDFAsVisualSignatureDesigner designer; +	private ISettings settings; +	private PDResources innerFormResources; +	private Map<String, ImageObject> images = new HashMap<String, ImageObject>(); + +	public PDFAsVisualSignatureBuilder( +			PDFAsVisualSignatureProperties properties, ISettings settings, +			PDFAsVisualSignatureDesigner designer) { +		this.properties = properties; +		this.settings = settings; +		this.designer = designer; +	} + +	@Override +	public void createProcSetArray() { +		COSArray procSetArr = new COSArray(); +		procSetArr.add(COSName.getPDFName("PDF")); +		procSetArr.add(COSName.getPDFName("Text")); +		procSetArr.add(COSName.getPDFName("ImageC")); +		procSetArr.add(COSName.getPDFName("ImageB")); +		procSetArr.add(COSName.getPDFName("ImageI")); +		getStructure().setProcSet(procSetArr); +		logger.debug("ProcSet array has been created"); +	} + +	public void createMyPage(PDFAsVisualSignatureDesigner properties) { +		PDPage page = properties.getSignaturePage(); +		if (page == null) { +			page = new PDPage(); +			page.setMediaBox(new PDRectangle(properties.getPageWidth(), +					properties.getPageHeight())); + +		} +		getStructure().setPage(page); +		logger.info("PDF page has been created"); +	} + +	@Override +	public void createTemplate(PDPage page) throws IOException { +		PDDocument template = new PDDocument(); + +		template.addPage(page); +		getStructure().setTemplate(template); +	} + +	public String createHashedId(String value) { +		try { +			MessageDigest md = MessageDigest.getInstance("SHA-1"); +			md.reset(); +			return Hex.encodeHexString(md.digest(value.getBytes("UTF-8"))); +		} catch (Throwable e) { +			logger.warn("Failed to generate ID for Image using value", e); +			return value; +		} +	} + +	private void readTableResources(PDFBoxTable table, PDDocument template) +			throws PdfAsException, IOException { + +		float[] colsSizes = table.getColsRelativeWith(); +		int max_cols = table.getColCount(); +		float padding = table.getPadding(); +		if (colsSizes == null) { +			colsSizes = new float[max_cols]; +			// set the column ratio for all columns to 1 +			for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { +				colsSizes[cols_idx] = 1; +			} +		} + +		logger.debug("TOTAL Width: " + table.getWidth()); + +		float total = 0; + +		for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { +			total += colsSizes[cols_idx]; +		} + +		for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { +			colsSizes[cols_idx] = (colsSizes[cols_idx] / total) +					* table.getWidth(); +		} + +		for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { +			logger.debug("Col: " + cols_idx + " : " + colsSizes[cols_idx]); +		} + +		/* +		 * if(!addedFonts.contains(table.getFont().getFont(null))) { PDFont font +		 * = table.getFont().getFont(template); addedFonts.add(font); +		 * innerFormResources.addFont(font); } +		 *  +		 * if(!addedFonts.contains(table.getValueFont().getFont(null))) { PDFont +		 * font = table.getValueFont().getFont(template); addedFonts.add(font); +		 * innerFormResources.addFont(font); } +		 */ + +		for (int i = 0; i < table.getRowCount(); i++) { +			ArrayList<Entry> row = table.getRow(i); +			for (int j = 0; j < row.size(); j++) { +				Entry cell = (Entry) row.get(j); +				if (cell.getType() == Entry.TYPE_IMAGE) { +					String img_value = (String) cell.getValue(); +					String img_ref = createHashedId(img_value); +					if (!images.containsKey(img_ref)) { +						BufferedImage img = ImageUtils.getImage(img_value, +								settings); + +						float width = colsSizes[j]; +						float height = table.getRowHeights()[i] + padding * 2; + +						float iwidth = (int) Math.floor((double) width); +						iwidth -= 2 * padding; + +						float iheight = (int) Math.floor((double) height); +						iheight -= 2 * padding; + +						float origWidth = (float) img.getWidth(); +						float origHeight = (float) img.getHeight(); + +						if (table.style != null) { +							if (table.style.getImageScaleToFit() != null) { +								iwidth = table.style.getImageScaleToFit() +										.getWidth(); +								iheight = table.style.getImageScaleToFit() +										.getHeight(); +							} +						} + +						float wfactor = iwidth / origWidth; +						float hfactor = iheight / origHeight; +						float scaleFactor = wfactor; +						if (hfactor < wfactor) { +							scaleFactor = hfactor; +						} + +						iwidth = (float) Math +								.floor((double) (scaleFactor * origWidth)); +						iheight = (float) Math +								.floor((double) (scaleFactor * origHeight)); + +						logger.debug("Scaling image to: " + iwidth + " x " +								+ iheight); + +						if (this.designer.properties +								.getSignatureProfileSettings().isPDFA()) { +							img = ImageUtils.removeAlphaChannel(img); +						} else { +							if (img.getAlphaRaster() == null +									&& img.getColorModel().hasAlpha()) { +								img = ImageUtils.removeAlphaChannel(img); +							} +						} +						// img = ImageUtils.convertRGBAToIndexed(img); + +						PDImageXObject pdImage = LosslessFactory.createFromImage(template, img); +						 + +						ImageObject image = new ImageObject(pdImage, iwidth, +								iheight); +						images.put(img_ref, image); +						innerFormResources.add(pdImage, "Im"); +					} +				} else if (cell.getType() == Entry.TYPE_TABLE) { +					PDFBoxTable tbl_value = (PDFBoxTable) cell.getValue(); +					readTableResources(tbl_value, template); +				} +			} +		} +	} + +	public void createInnerFormStreamPdfAs(PDDocument template, PDDocument origDoc) +			throws PdfAsException { +		try { + +			// Hint we have to create all PDXObjectImages before creating the +			// PDPageContentStream +			// only PDFbox developers know why ... +			// if (getStructure().getPage().getResources() != null) { +			// innerFormResources = getStructure().getPage().getResources(); +			// } else { +			innerFormResources = new PDResources(); +			getStructure().getPage().setResources(innerFormResources); +			// } +			readTableResources(properties.getMainTable(), template); + +			PDPageContentStream stream = new PDPageContentStream(template, +					getStructure().getPage()); +			// stream.setFont(PDType1Font.COURIER, 5); +			TableDrawUtils.drawTable(getStructure().getPage(), stream, 1, 1, +					designer.getWidth(), designer.getHeight(), +					properties.getMainTable(), template, false, +					innerFormResources, images, settings, this, properties); +			stream.close(); +			PDStream innterFormStream = new PDStream(template,getStructure().getPage().getContents()); + +			getStructure().setInnterFormStream(innterFormStream); +			logger.debug("Strean of another form (inner form - it would be inside holder form) has been created"); + +		} catch (Throwable e) { +			logger.warn("Failed to create visual signature block", e); +			throw new PdfAsException("Failed to create visual signature block", +					e); +		} +	} + +	@Override +	public void injectProcSetArray(PDFormXObject innerForm, PDPage page, +			PDResources innerFormResources, PDResources imageFormResources, +			PDResources holderFormResources, COSArray procSet) { +		innerForm.getResources().getCOSObject() +				.setItem(COSName.PROC_SET, procSet); // +		page.getCOSObject().setItem(COSName.PROC_SET, procSet); +		innerFormResources.getCOSObject() +				.setItem(COSName.PROC_SET, procSet); +		/* +		 * imageFormResources.getCOSDictionary() .setItem(COSName.PROC_SET, +		 * procSet); +		 */ +		holderFormResources.getCOSObject().setItem(COSName.PROC_SET, +				procSet); +		logger.debug("inserted ProcSet to PDF"); +	} + +	public void injectAppearanceStreams(PDStream holderFormStream, +			PDStream innterFormStream, PDStream imageFormStream, +			String imageObjectName, String imageName, String innerFormName, +			PDFAsVisualSignatureDesigner properties) throws IOException { + +		// 100 means that document width is 100% via the rectangle. if rectangle +		// is 500px, images 100% is 500px. +		// String imgFormComment = "q "+imageWidthSize+ " 0 0 50 0 0 cm /" + +		// imageName + " Do Q\n" + builder.toString(); +		/* +		 * String imgFormComment = "q " + 100 + " 0 0 50 0 0 cm /" + imageName + +		 * " Do Q\n"; +		 */ +		double m00 = getStructure().getAffineTransform().getScaleX(); +		double m10 = getStructure().getAffineTransform().getShearY(); +		double m01 = getStructure().getAffineTransform().getShearX(); +		double m11 = getStructure().getAffineTransform().getScaleY(); +		double m02 = getStructure().getAffineTransform().getTranslateX(); +		double m12 = getStructure().getAffineTransform().getTranslateY(); + +		String holderFormComment = "q " + m00 + " " + m10 + " " + m01 + " " +				+ m11 + " " + m02 + " " + m12 + " cm /" + innerFormName +				+ " Do Q"; + +		logger.debug("Holder Form Stream: " + holderFormComment); + +		// String innerFormComment = "q 1 0 0 1 0 0 cm /" + imageObjectName + +		// " Do Q\n"; +		String innerFormComment = IOUtils.toString(getStructure().getInnerFormStream().toByteArray()); +	 +		//		.getInputStreamAsString();//TODO: pdfbox2 - get the string from the stream + +		// logger.debug("Inner Form Stream: " + innerFormComment); + +		// appendRawCommands(getStructure().getInnterFormStream().createOutputStream(), +		// getStructure().getInnterFormStream().getInputStreamAsString()); + +		appendRawCommands(getStructure().getHolderFormStream() +				.createOutputStream(), holderFormComment.trim().replace("\n", "").replace("\r", "")); +		appendRawCommands(getStructure().getInnerFormStream() +				.createOutputStream(), innerFormComment/*.trim().replace("\n", "").replace("\r", "")*/); +		// appendRawCommands(getStructure().getImageFormStream().createOutputStream(), +		// imgFormComment); +		logger.debug("Injected apereance stream to pdf"); + +	} + +	public void createPage(PDFAsVisualSignatureDesigner properties) { +		PDPage page = new PDPage(); +		page.setMediaBox(new PDRectangle(properties.getPageWidth(), properties +				.getPageHeight())); +		page.setRotation(properties.getPageRotation()); +		getStructure().setPage(page); +		logger.debug("PDF page has been created"); +	} + +	public void createAcroForm(PDDocument template) { +		PDAcroForm theAcroForm = new PDAcroForm(template); +		template.getDocumentCatalog().setAcroForm(theAcroForm); +		getStructure().setAcroForm(theAcroForm); +		logger.debug("Acro form page has been created"); +	} + +	public void createSignatureField(PDAcroForm acroForm) throws IOException { +		PDSignatureField sf = new PDSignatureField(acroForm); +		getStructure().setSignatureField(sf); +		logger.debug("Signature field has been created"); +	} + +	public void createSignature(PDSignatureField pdSignatureField, PDPage page, +			String signatureName) throws IOException { +		PDSignature pdSignature = new PDSignature(); +		pdSignatureField.setSignature(pdSignature); +		pdSignatureField.getWidget().setPage(page); +		page.getAnnotations().add(pdSignatureField.getWidget()); +		pdSignature.setName(signatureName); +		pdSignature.setByteRange(new int[] { 0, 0, 0, 0 }); +		pdSignature.setContents(new byte[4096]); +		getStructure().setPdSignature(pdSignature); +		logger.debug("PDSignatur has been created"); +	} + +	public void createAcroFormDictionary(PDAcroForm acroForm, +			PDSignatureField signatureField) throws IOException { +		@SuppressWarnings("unchecked") +		List<PDField> acroFormFields = acroForm.getFields(); +		COSDictionary acroFormDict = acroForm.getCOSObject(); +		acroFormDict.setDirect(true); +		acroFormDict.setInt(COSName.SIG_FLAGS, 3); +		acroFormFields.add(signatureField); +		acroFormDict.setString(COSName.DA, "/sylfaen 0 Tf 0 g"); +		getStructure().setAcroFormFields(acroFormFields); +		getStructure().setAcroFormDictionary(acroFormDict); +		logger.debug("AcroForm dictionary has been created"); +	} + +	public void createSignatureRectangle(PDSignatureField signatureField, +			PDFAsVisualSignatureDesigner properties, float degrees) +			throws IOException { + +		PDRectangle rect = new PDRectangle(); + +		Point2D upSrc = new Point2D.Float(); +		upSrc.setLocation(properties.getxAxis() + properties.getWidth(), +				properties.getPageHeight() - properties.getyAxis()); + +		Point2D llSrc = new Point2D.Float(); +		llSrc.setLocation(properties.getxAxis(), properties.getPageHeight() +				- properties.getyAxis() - properties.getHeight()); + +		rect.setUpperRightX((float) upSrc.getX()); +		rect.setUpperRightY((float) upSrc.getY()); +		rect.setLowerLeftY((float) llSrc.getY()); +		rect.setLowerLeftX((float) llSrc.getX()); +		logger.debug("orig rectangle of signature has been created: {}", +				rect.toString()); + +		AffineTransform transform = new AffineTransform(); +		transform.setToIdentity(); +		if (degrees % 360 != 0) { +			transform.setToRotation(Math.toRadians(degrees), llSrc.getX(), +					llSrc.getY()); +		} + +		Point2D upDst = new Point2D.Float(); +		transform.transform(upSrc, upDst); + +		Point2D llDst = new Point2D.Float(); +		transform.transform(llSrc, llDst); + +		float xPos = properties.getxAxis(); +		float yPos = properties.getPageHeight() - properties.getyAxis(); +		logger.debug("POS {} x {}", xPos, yPos); +		logger.debug("SIZE {} x {}", properties.getWidth(), +				properties.getHeight()); +		// translate according to page! rotation +		int pageRotation = properties.getPageRotation(); +		AffineTransform translate = new AffineTransform(); +		switch (pageRotation) { +		case 90: +			translate.setToTranslation( +					properties.getPageHeight() +							- (properties.getPageHeight() - properties +									.getyAxis()) - properties.getxAxis() +							+ properties.getHeight(), +					properties.getxAxis() +							+ properties.getHeight() +							- (properties.getPageHeight() - properties +									.getyAxis())); +			break; +		case 180: +			// translate.setToTranslation(properties.getPageWidth() - +			// properties.getxAxis() - properties.getxAxis(), +			// properties.getPageHeight() - properties.getyAxis() + +			// properties.getHeight()); +			translate.setToTranslation( +					properties.getPageWidth() - 2 * xPos, +					properties.getPageHeight() - 2 +							* (yPos - properties.getHeight())); +			break; +		case 270: +			translate.setToTranslation(-properties.getHeight() + yPos - xPos, +					properties.getPageWidth() - (yPos - properties.getHeight()) +							- xPos); +			break; +		} + +		translate.transform(upDst, upDst); +		translate.transform(llDst, llDst); + +		rect.setUpperRightX((float) upDst.getX()); +		rect.setUpperRightY((float) upDst.getY()); +		rect.setLowerLeftY((float) llDst.getY()); +		rect.setLowerLeftX((float) llDst.getX()); +		logger.debug("rectangle of signature has been created: {}", +				rect.toString()); +		signatureField.getWidget().setRectangle(rect); +		getStructure().setSignatureRectangle(rect); +		logger.debug("rectangle of signature has been created"); +	} + +	public void createAffineTransform(float[] params) { +		AffineTransform transform = new AffineTransform(params[0], params[1], +				params[2], params[3], params[4], params[5]); +		// transform.rotate(90); +		getStructure().setAffineTransform(transform); +		logger.debug("Matrix has been added"); +	} + +	public void createSignatureImage(PDDocument template, +			InputStream inputStream) throws IOException { +		PDImageXObject img = JPEGFactory.createFromStream(template, inputStream); +		getStructure().setImage(img); +		logger.debug("Visible Signature Image has been created"); +		// pdfStructure.setTemplate(template); +		inputStream.close(); + +	} + +	public void createFormaterRectangle(float[] params) { + +		PDRectangle formrect = new PDRectangle(); +		float[] translated = new float[4]; +		getStructure().getAffineTransform().transform(params, 0, translated, 0, +				2); + +		formrect.setUpperRightX(translated[0]); +		formrect.setUpperRightY(translated[1]); +		formrect.setLowerLeftX(translated[2]); +		formrect.setLowerLeftY(translated[3]); + +		getStructure().setFormatterRectangle(formrect); +		logger.debug("Formater rectangle has been created"); + +	} + +	public void createHolderFormStream(PDDocument template) { +		PDStream holderForm = new PDStream(template); +		getStructure().setHolderFormStream(holderForm); +		logger.debug("Holder form Stream has been created"); +	} + +	public void createHolderFormResources() { +		PDResources holderFormResources = new PDResources(); +		getStructure().setHolderFormResources(holderFormResources); +		logger.debug("Holder form resources have been created"); + +	} + +	public void createHolderForm(PDResources holderFormResources, +			PDStream holderFormStream, PDRectangle formrect) { + +		PDFormXObject holderForm = new PDFormXObject(holderFormStream); +		holderForm.setResources(holderFormResources); +		holderForm.setBBox(formrect); +		holderForm.setFormType(1); +		getStructure().setHolderForm(holderForm); +		logger.debug("Holder form has been created"); + +	} + +	public void createAppearanceDictionary(PDFormXObject holderForml, +			PDSignatureField signatureField, float degrees) throws IOException { + +		PDAppearanceDictionary appearance = new PDAppearanceDictionary(); +		appearance.getCOSObject().setDirect(true); + +		PDAppearanceStream appearanceStream = new PDAppearanceStream( +				holderForml.getCOSStream()); +		AffineTransform transform = new AffineTransform(); +		transform.setToIdentity(); +		transform.rotate(Math.toRadians(degrees)); +		appearanceStream.setMatrix(transform); +		appearance.setNormalAppearance(appearanceStream); +		signatureField.getWidget().setAppearance(appearance); + +		getStructure().setAppearanceDictionary(appearance); +		logger.debug("PDF appereance Dictionary has been created"); + +	} + +	public void createInnerFormResource() { +		getStructure().setInnerFormResources(innerFormResources); +		logger.debug("Resources of another form (inner form - it would be inside holder form) have been created"); +	} + +	public void createInnerForm(PDResources innerFormResources, +			PDStream innerFormStream, PDRectangle formrect) { +		PDFormXObject innerForm = new PDFormXObject(innerFormStream); +		innerForm.setResources(innerFormResources); +		innerForm.setBBox(formrect); +		innerForm.setFormType(1); +		getStructure().setInnerForm(innerForm); +		logger.debug("Another form (inner form - it would be inside holder form) have been created"); + +	} + +	public void insertInnerFormToHolerResources(PDFormXObject innerForm, +			PDResources holderFormResources) { +		COSName name = holderFormResources.add(innerForm, "FRM");//TODO: pdfbox2 - is this right? +		getStructure().setInnerFormName(name); +		logger.debug("Alerady inserted inner form  inside holder form"); +	} + +	public void createImageFormStream(PDDocument template) { +		PDStream imageFormStream = new PDStream(template); +		getStructure().setImageFormStream(imageFormStream); +		logger.debug("Created image form Stream"); +	} + +	public void createImageFormResources() { +		PDResources imageFormResources = new PDResources(); +		getStructure().setImageFormResources(imageFormResources); +		logger.debug("Created image form Resources"); +	} + +	public void createImageForm(PDResources imageFormResources, +			PDResources innerFormResource, PDStream imageFormStream, +			PDRectangle formrect, AffineTransform affineTransform, PDFormXObject img) +			throws IOException { + +		/* +		 * if you need text on the visible signature +		 *  +		 * PDFont font = PDTrueTypeFont.loadTTF(this.pdfStructure.getTemplate(), +		 * new File("D:\\arial.ttf")); font.setFontEncoding(new +		 * WinAnsiEncoding()); +		 *  +		 * Map<String, PDFont> fonts = new HashMap<String, PDFont>(); +		 * fonts.put("arial", font); +		 */ +		PDFormXObject imageForm = new PDFormXObject(imageFormStream); +		imageForm.setBBox(formrect); +		imageForm.setMatrix(affineTransform); +		imageForm.setResources(imageFormResources); +		imageForm.setFormType(1); +		/* +		 * imageForm.getResources().addFont(font); +		 * imageForm.getResources().setFonts(fonts); +		 */ + +		imageFormResources.getCOSObject().setDirect(true); +		COSName imageFormName = innerFormResource.add(imageForm, "n");//TODO: pdfbox2 - is this right? +		COSName imageName = imageFormResources.add(img, "img"); +		this.getStructure().setImageForm(imageForm); +		this.getStructure().setImageFormName(imageFormName); +		this.getStructure().setImageName(imageName); +		logger.debug("Created image form"); +	} + +	public void appendRawCommands(OutputStream os, String commands) +			throws IOException { +		os.write(commands.getBytes("UTF-8")); +		os.close(); +	} + +	public void createVisualSignature(PDDocument template) { +		this.getStructure().setVisualSignature(template.getDocument()); +		logger.debug("Visible signature has been created"); + +	} + +	public void createWidgetDictionary(PDSignatureField signatureField, +			PDResources holderFormResources) throws IOException { + +		COSDictionary widgetDict = signatureField.getWidgets().get(0).getCOSObject();//TODO: pdfbox2 - is this right was getWidget before? +		widgetDict.setNeedToBeUpdated(true); +		widgetDict.setItem(COSName.DR, holderFormResources.getCOSObject()); + +		getStructure().setWidgetDictionary(widgetDict); +		logger.debug("WidgetDictionary has been crated"); +	} + +	public void closeTemplate(PDDocument template) throws IOException { +		template.close(); +		this.getStructure().getTemplate().close(); +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureDesigner.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureDesigner.java new file mode 100644 index 00000000..33450b56 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureDesigner.java @@ -0,0 +1,472 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import javax.imageio.ImageIO; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageTree; +import org.apache.pdfbox.pdmodel.common.PDRectangle; + +public class PDFAsVisualSignatureDesigner { + +//	private static final Logger logger = LoggerFactory.getLogger(PDFAsVisualSignatureDesigner.class); + +	private Float sigImgWidth; +	private Float sigImgHeight; +	private float xAxis; +	private float yAxis; +	private float pageHeight; +	private float pageWidth; +	private InputStream imgageStream; +	private String signatureFieldName = "sig"; // default +	private float[] formaterRectangleParams = { 0, 0, 100, 50 }; // default +	//private float[] AffineTransformParams = { 0, 1, -1, 0, 0, 0 }; // default +	private float[] AffineTransformParams = { 1, 0, 0, 1, 0, 0 }; // default +	private float imageSizeInPercents; +	private PDDocument document = null; +	private int page = 0; +	private int pageRotation = 0; +	private boolean newpage = false; +	PDFAsVisualSignatureProperties properties; + +	/** +	 *  +	 * @param doc +	 *            - Already created PDDocument of your PDF document +	 * @param imageStream +	 * @param page +	 * @throws IOException +	 *             - If we can't read, flush, or can't close stream +	 */ +	public PDFAsVisualSignatureDesigner(PDDocument doc, int page, +			PDFAsVisualSignatureProperties properties, boolean newpage) throws IOException { +		this.properties = properties; +		calculatePageSize(doc, page, newpage); +		document = doc; +		this.page = page; +		this.newpage = newpage; +	} + +	/** +	 * Each page of document can be different sizes. +	 *  +	 * @param document +	 * @param page +	 */ +	private void calculatePageSize(PDDocument document, int page, boolean newpage) { + +		if (page < 1) { +			throw new IllegalArgumentException("First page of pdf is 1, not " +					+ page); +		} +		 +		PDPageTree pages = document.getDocumentCatalog().getPages(); +		if(newpage) { +			PDPage lastPage = (PDPage) pages.get(pages.getCount()-1); +			PDRectangle mediaBox = lastPage.getMediaBox(); +			pageRotation = lastPage.getRotation() % 360; +			if(pageRotation == 90 || pageRotation == 270) { +				this.pageHeight(mediaBox.getWidth()); +				this.pageWidth = mediaBox.getHeight(); +			} else { +				this.pageHeight(mediaBox.getHeight()); +				this.pageWidth = mediaBox.getWidth(); +			} +		} else { +			PDPage firstPage = (PDPage) pages.get(page - 1); +			PDRectangle mediaBox = firstPage.getMediaBox(); +			pageRotation = firstPage.getRotation() % 360; +			if(pageRotation == 90 || pageRotation == 270) { +				this.pageHeight(mediaBox.getWidth()); +				this.pageWidth = mediaBox.getHeight(); +			} else { +				this.pageHeight(mediaBox.getHeight()); +				this.pageWidth = mediaBox.getWidth(); +			} +		} +		float x = this.pageWidth; +		float y = 0; +		this.pageWidth = this.pageWidth + y; +		float tPercent = (100 * y / (x + y)); +		this.imageSizeInPercents = 100 - tPercent; +	} + +	/** +	 *  +	 * @param path +	 *            of image location +	 * @return image Stream +	 * @throws IOException +	 */ +	public PDFAsVisualSignatureDesigner signatureImage(String path) +			throws IOException { +		InputStream fin = new FileInputStream(path); +		return signatureImageStream(fin); +	} + +	/** +	 * zoom signature image with some percent. +	 *  +	 * @param percent +	 *            - x % increase image with x percent. +	 * @return Visible Signature Configuration Object +	 */ +	public PDFAsVisualSignatureDesigner zoom(float percent) { +		sigImgHeight = sigImgHeight + (sigImgHeight * percent) / 100; +		sigImgWidth = sigImgWidth + (sigImgWidth * percent) / 100; +		return this; +	} + +	/** +	 *  +	 * @param xAxis +	 *            - x coordinate +	 * @param yAxis +	 *            - y coordinate +	 * @return Visible Signature Configuration Object +	 */ +	public PDFAsVisualSignatureDesigner coordinates(float x, float y) { +		xAxis(x); +		yAxis(y); +		return this; +	} + +	/** +	 *  +	 * @return xAxis - gets x coordinates +	 */ +	public float getxAxis() { +		return xAxis; +	} + +	/** +	 *  +	 * @param xAxis +	 *            - x coordinate +	 * @return Visible Signature Configuration Object +	 */ +	public PDFAsVisualSignatureDesigner xAxis(float xAxis) { +		this.xAxis = xAxis; +		return this; +	} + +	/** +	 *  +	 * @return yAxis +	 */ +	public float getyAxis() { +		return yAxis; +	} + +	/** +	 *  +	 * @param yAxis +	 * @return Visible Signature Configuration Object +	 */ +	public PDFAsVisualSignatureDesigner yAxis(float yAxis) { +		this.yAxis = yAxis; +		return this; +	} + +	/** +	 *  +	 * @return signature image width +	 */ +	public float getWidth() { +		return this.properties.getMainTable().getWidth(); +	} + +	/** +	 *  +	 * @param sets +	 *            signature image width +	 * @return Visible Signature Configuration Object +	 */ +	public PDFAsVisualSignatureDesigner width(float signatureImgWidth) { +		this.sigImgWidth = signatureImgWidth; +		return this; +	} + +	/** +	 *  +	 * @return signature image height +	 */ +	public float getHeight() { +		return this.properties.getMainTable().getHeight(); +	} + +	/** +	 *  +	 * @param set +	 *            signature image Height +	 * @return Visible Signature Configuration Object +	 */ +	public PDFAsVisualSignatureDesigner height(float signatureImgHeight) { +		this.sigImgHeight = signatureImgHeight; +		return this; +	} + +	/** +	 *  +	 * @return template height +	 */ +	protected float getTemplateHeight() { +		return getPageHeight(); +	} + +	/** +	 *  +	 * @param templateHeight +	 * @return Visible Signature Configuration Object +	 */ +	private PDFAsVisualSignatureDesigner pageHeight(float templateHeight) { +		this.pageHeight = templateHeight; +		return this; +	} + +	/** +	 *  +	 * @return signature field name +	 */ +	public String getSignatureFieldName() { +		return signatureFieldName; +	} + +	/** +	 *  +	 * @param signatureFieldName +	 * @return Visible Signature Configuration Object +	 */ +	public PDFAsVisualSignatureDesigner signatureFieldName( +			String signatureFieldName) { +		this.signatureFieldName = signatureFieldName; +		return this; +	} + +	/** +	 *  +	 * @return image Stream +	 */ +	public InputStream getImageStream() { +		return imgageStream; +	} + +	/** +	 *  +	 * @param imgageStream +	 *            - stream of your visible signature image +	 * @return Visible Signature Configuration Object +	 * @throws IOException +	 *             - If we can't read, flush, or close stream of image +	 */ +	private PDFAsVisualSignatureDesigner signatureImageStream( +			InputStream imageStream) throws IOException { + +		ByteArrayOutputStream baos = new ByteArrayOutputStream(); +		byte[] buffer = new byte[1024]; +		int len; +		while ((len = imageStream.read(buffer)) > -1) { +			baos.write(buffer, 0, len); +		} +		baos.flush(); +		baos.close(); + +		byte[] byteArray = baos.toByteArray(); +		byte[] byteArraySecond = byteArray.clone(); + +		InputStream inputForBufferedImage = new ByteArrayInputStream(byteArray); +		InputStream revertInputStream = new ByteArrayInputStream( +				byteArraySecond); + +		if (sigImgHeight == null || sigImgWidth == null) { +			calcualteImageSize(inputForBufferedImage); +		} + +		this.imgageStream = revertInputStream; + +		return this; +	} + +	/** +	 * calculates image width and height. sported formats: all +	 *  +	 * @param fis +	 *            - input stream of image +	 * @throws IOException +	 *             - if can't read input stream +	 */ +	private void calcualteImageSize(InputStream fis) throws IOException { + +		BufferedImage bimg = ImageIO.read(fis); +		int width = bimg.getWidth(); +		int height = bimg.getHeight(); + +		sigImgHeight = (float) height; +		sigImgWidth = (float) width; + +	} + +	/** +	 *  +	 * @return Affine Transform parameters of for PDF Matrix +	 */ +	public float[] getAffineTransformParams() { +		return AffineTransformParams; +	} + +	/** +	 *  +	 * @param affineTransformParams +	 * @return Visible Signature Configuration Object +	 */ +	public PDFAsVisualSignatureDesigner affineTransformParams( +			float[] affineTransformParams) { +		AffineTransformParams = affineTransformParams; +		return this; +	} + +	/** +	 *  +	 * @return formatter PDRectanle parameters +	 */ +	public float[] getFormaterRectangleParams() { +		return formaterRectangleParams; +	} + +	/** +	 * sets formatter PDRectangle; +	 *  +	 * @param formaterRectangleParams +	 * @return Visible Signature Configuration Object +	 */ +	public PDFAsVisualSignatureDesigner formaterRectangleParams( +			float[] formaterRectangleParams) { +		this.formaterRectangleParams = formaterRectangleParams; +		return this; +	} + +	/** +	 *  +	 * @return page width +	 */ +	public float getPageWidth() { +		return pageWidth; +	} +	 +	public PDPage getSignaturePage() { +		if (page < 1) { +			throw new IllegalArgumentException("First page of pdf is 1, not " +					+ page); +		} +		PDPage pdPage = null; +		PDPageTree pages = document.getDocumentCatalog().getPages(); +		if(newpage) { +			pdPage = new PDPage(); +		} else { +			pdPage = (PDPage) pages.get(page - 1); +		} +		 +		return pdPage; +	} + +	/** +	 *  +	 * @param sets +	 *            pageWidth +	 * @return Visible Signature Configuration Object +	 */ +	public PDFAsVisualSignatureDesigner pageWidth(float pageWidth) { +		this.pageWidth = pageWidth; +		return this; +	} + +	/** +	 *  +	 * @return page height +	 */ +	public float getPageHeight() { +		return pageHeight; +	} + +	/** +	 *  +	 * @return page height +	 */ +	public int getPageRotation() { +		return pageRotation; +	} +	 +	/** +	 * get image size in percents +	 *  +	 * @return +	 */ +	public float getImageSizeInPercents() { +		return imageSizeInPercents; +	} + +	/** +	 *  +	 * @param imageSizeInPercents +	 */ +	public void imageSizeInPercents(float imageSizeInPercents) { +		this.imageSizeInPercents = imageSizeInPercents; +	} + +	/** +	 * returns visible signature text +	 *  +	 * @return +	 */ +	public String getSignatureText() { +		throw new UnsupportedOperationException( +				"That method is not yet implemented"); +	} + +	/** +	 *  +	 * @param signatureText +	 *            - adds the text on visible signature +	 * @return +	 */ +	public PDFAsVisualSignatureDesigner signatureText(String signatureText) { +		throw new UnsupportedOperationException( +				"That method is not yet implemented"); +	} + +	public float getRotation() { +		return this.properties.getRotation(); +	} +	 +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java new file mode 100644 index 00000000..db96767a --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +import java.io.IOException; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageTree; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSigProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.exceptions.PdfAsWrappedIOException; +import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; +import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject; +import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; + +public class PDFAsVisualSignatureProperties extends PDVisibleSigProperties { + +	private static final Logger logger = LoggerFactory.getLogger(PDFAsVisualSignatureProperties.class); +	 +	private ISettings settings; + +	private PDFBoxTable main; +	 +	private PDFAsVisualSignatureDesigner designer; +	 +	private float rotationAngle = 0; +	 +	private PDDocument origDoc; +	 +	private SignatureProfileSettings signatureProfileSettings; + +	private String alternativeTableCaption=""; + +	public PDFAsVisualSignatureProperties(ISettings settings, PDFBOXObject object,  +			PdfBoxVisualObject visObj, PositioningInstruction pos, SignatureProfileSettings signatureProfileSettings) { +		this.settings = settings; +		this.signatureProfileSettings = signatureProfileSettings; +		try { +			main = visObj.getTable(); +		} catch (Throwable e) { +			e.printStackTrace(); +		} +		this.rotationAngle = pos.getRotation(); +		try { +			origDoc = object.getDocument(); + +			designer = new PDFAsVisualSignatureDesigner(origDoc, pos.getPage(), this, pos.isMakeNewPage()); +			PDPageTree pages = origDoc.getDocumentCatalog().getPages(); +			PDPage page = null; +			if(pos.isMakeNewPage()) { +				page = (PDPage) pages.get(pages.getCount()-1); +			} else { +				page = (PDPage) pages.get(pos.getPage() - 1); +			} +			logger.debug("PAGE width {} HEIGHT {}", designer.getPageWidth(), designer.getPageHeight()); +			logger.debug("POS X {} Y {}", pos.getX(), pos.getY()); +			int rot = page.getRotation(); +			float posy = designer.getPageHeight() - pos.getY(); +			float posx = pos.getX(); +			/*switch (rot) { +			case 90: // CW +				posx = designer.getPageHeight() - pos.getY(); +				posy = designer.getPageWidth() - main.getWidth(); +				break; +			case 180: +				posy = pos.getY(); +				posx = designer.getPageWidth() - pos.getX(); +				break; +			case 270: // CCW +				posx = pos.getY(); +				posy = designer.getPageWidth() - pos.getX(); +				break; +			}*/ +			logger.debug("ROT {}", rot); +			logger.debug("COORD X {} Y {}", posx, posy); +			designer.coordinates(posx, posy); +			float[] form_rect = new float[] {0,0, main.getWidth() + 2, main.getHeight() + 2}; +			logger.debug("AP Rect: {} {} {} {}", form_rect[0], form_rect[1], form_rect[2], form_rect[3]); +			designer.formaterRectangleParams(form_rect); +			//this.setPdVisibleSignature(designer); +		} catch (Throwable e) { +			e.printStackTrace(); +		} +	} + +	@Override +	public void buildSignature() throws IOException { +		PDFAsVisualSignatureBuilder builder = new PDFAsVisualSignatureBuilder(this, this.settings, designer); +		PDFAsTemplateCreator creator = new PDFAsTemplateCreator(builder); +		try { +			setVisibleSignature(creator.buildPDF(designer, this.origDoc)); +		} catch (PdfAsException e) { +			throw new PdfAsWrappedIOException(e); +		} +	} + +	public PDFBoxTable getMainTable() { +		return main; +	} +	 +	 +	public float getRotation() { +		return this.rotationAngle; +	} + +	public PDFAsVisualSignatureDesigner getDesigner() { +		return designer; +	} + +	public SignatureProfileSettings getSignatureProfileSettings() { +		return signatureProfileSettings; +	} +	 +	public String getAlternativeTableCaption() { +		return alternativeTableCaption; +	} + +	public void setAlternativeTableCaption(String alternativeTableCaption) { +		this.alternativeTableCaption = alternativeTableCaption; +	} +	 +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxFont.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxFont.java new file mode 100644 index 00000000..8b46d56a --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxFont.java @@ -0,0 +1,299 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.fontbox.ttf.NameRecord; +import org.apache.fontbox.ttf.NamingTable; +import org.apache.fontbox.ttf.TTFParser; +import org.apache.fontbox.ttf.TrueTypeFont; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont; +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject; + +public class PDFBoxFont { + +	private static final Logger logger = LoggerFactory +			.getLogger(PDFBoxFont.class); + +	private static final String HELVETICA = "HELVETICA"; +	private static final String COURIER = "COURIER"; +	private static final String TIMES_ROMAN = "TIMES_ROMAN"; +	private static final String BOLD = "BOLD"; +	private static final String NORMAL = "NORMAL"; +	private static final String ITALIC = "ITALIC"; +	private static final String SEP = ":"; + +	public static PDFont defaultFont = PDType1Font.HELVETICA; +	public static float defaultFontSize = 8; + +	private static Map<String, PDFont> fontStyleMap = new HashMap<String, PDFont>(); + +	private static Map<String, FontInfoCache> fontInfoCache = new HashMap<String, FontInfoCache>(); + +	static { +		fontStyleMap.put(HELVETICA + SEP + NORMAL, PDType1Font.HELVETICA); +		fontStyleMap.put(HELVETICA + SEP + BOLD, PDType1Font.HELVETICA_BOLD); + +		fontStyleMap.put(COURIER + SEP + NORMAL, PDType1Font.COURIER); +		fontStyleMap.put(COURIER + SEP + BOLD, PDType1Font.COURIER_BOLD); + +		fontStyleMap.put(TIMES_ROMAN + SEP + NORMAL, PDType1Font.TIMES_ROMAN); +		fontStyleMap.put(TIMES_ROMAN + SEP + BOLD, PDType1Font.TIMES_BOLD); +		fontStyleMap.put(TIMES_ROMAN + SEP + ITALIC, PDType1Font.TIMES_ITALIC); +	} + +	public static void showBuildinFonts() { +		Iterator<String> it = fontStyleMap.keySet().iterator(); +		logger.info("Available Fonts:"); +		while (it.hasNext()) { +			logger.info(it.next()); +		} +	} + +	PDFont font; +	PDFont cachedfont = null; +	float fontSize; +	String fontDesc; +	String ttfFontDesc; +	ISettings settings; + +	private FontInfoCache getFontInfo(String pathName) { +		synchronized (fontInfoCache) { + +			if (fontInfoCache.containsKey(pathName)) { +				return fontInfoCache.get(pathName); +			} else { +				try { +					String fontNameToLoad = null; +					String fontFamilyToLoad = null; +					InputStream ttfData = new FileInputStream(pathName); +					try { +						TrueTypeFont ttf = null; +						TTFParser parser = new TTFParser(); +						ttf = parser.parse(ttfData); +						NamingTable naming = ttf.getNaming(); +						List<NameRecord> records = naming.getNameRecords(); +						for (int i = 0; i < records.size(); i++) { +							NameRecord nr = records.get(i); +							if (nr.getNameId() == NameRecord.NAME_POSTSCRIPT_NAME) { +								fontNameToLoad = nr.getString(); +							} else if (nr.getNameId() == NameRecord.NAME_FONT_FAMILY_NAME) { +								fontFamilyToLoad = nr.getString(); +							} +						} +					} finally { +						ttfData.close(); +					} +					FontInfoCache fontInfo = new FontInfoCache(); +					fontInfo.filename = pathName; +					fontInfo.fontFamily = fontFamilyToLoad; +					fontInfo.fontName = fontNameToLoad; +					fontInfo.fontPath = pathName; +					fontInfoCache.put(pathName, fontInfo); +					return fontInfo; +				} catch (Throwable e) { +					logger.warn("Failed to generate FontInfo from file: {}", pathName); +				} +				return null; +			} +		} +	} + +	private PDFont findCachedFont(PDFBOXObject pdfObject, FontInfoCache fontInfo) { +		try { +			if(pdfObject.getFontCache().containsKey(fontInfo.fontPath)) { +				return pdfObject.getFontCache().get(fontInfo.fontPath); +			} +			 +			List<COSObject> cosObjects = pdfObject.getDocument().getDocument().getObjectsByType( +					COSName.FONT); + +			//COSName cosFontName = COSName.getPDFName(fontInfo.fontName); +			//COSName cosFontFamily = COSName.getPDFName(fontInfo.fontFamily); + +			Iterator<COSObject> cosObjectIt = cosObjects.iterator(); + +			while (cosObjectIt.hasNext()) { +				COSObject cosObject = cosObjectIt.next(); +				COSDictionary baseObject = (COSDictionary) cosObject +						.getObject(); +				if (baseObject instanceof COSDictionary) { +					COSDictionary fontDictionary = (COSDictionary) baseObject; +					COSBase subType = cosObject.getItem(COSName.SUBTYPE); +					COSDictionary fontDescriptor = (COSDictionary)cosObject.getDictionaryObject(COSName.FONT_DESC); +					String fontName = fontDescriptor.getNameAsString(COSName.FONT_NAME); +					String fontFamily = fontDescriptor.getNameAsString(COSName.FONT_FAMILY); +					logger.debug("Checking Font {} - {}", fontFamily, fontName); +					if (COSName.TRUE_TYPE.equals(subType)) { +						if (fontInfo.fontName != null && fontInfo.fontName.equals(fontName) &&  +							fontInfo.fontFamily != null && fontInfo.fontFamily.equals(fontFamily)) { +							// Found it! :) +							logger.info("Found Font {}", fontInfo.fontName); +							return new PDTrueTypeFont(fontDictionary); +						} else { +							logger.debug("Font not found: {} is {}", +									fontInfo.fontName, fontName); +						} +					} else { +						logger.debug("Font not a TTF"); +					} +				} else { +					logger.debug("Font not a COSDictionary"); +				} +			} +		} catch (Throwable e) { +			logger.info("Failed to find existing TTF fonts!", e); +		} +		return null; +	} + +	private PDFont generateTTF(String fonttype, PDFBOXObject pdfObject) +			throws IOException { +		/*boolean cacheNow = true; +		if (pdfObject == null) { +			if (this.doc == null) { +				this.doc = new PDDocument(); +			} +			doc = this.doc; +		} else { +			cacheNow = true; +		}*/ +		ttfFontDesc = fonttype; +		String fontName = fonttype.replaceFirst("TTF:", ""); +		String fontPath = this.settings.getWorkingDirectory() + File.separator +				+ "fonts" + File.separator + fontName; +		 +		logger.debug("Font from: \"" + fontPath + "\"."); + +		if(fontStyleMap.containsKey(fontPath)) { +			return fontStyleMap.get(fontPath); +		} +		 +		FontInfoCache fontInfo = getFontInfo(fontPath); +		 +		if(fontInfo != null) { +		 +			PDFont font = findCachedFont(pdfObject, fontInfo); + +			if (font != null) { +				return font; +			} +		}  +		 +		logger.debug("Instantiating font."); +		 +		//if (cacheNow) { +			cachedfont = PDTrueTypeFont.loadTTF(pdfObject.getDocument(), new File(fontPath)); + +			fontStyleMap.put(fontPath, cachedfont); +			return cachedfont; +		//} else { +		//	return PDTrueTypeFont.loadTTF(doc, fontPath); +		//} + +	} + +	private PDFont generateFont(String fonttype, String fontder, +			PDFBOXObject pdfObject) throws IOException { +		if (fonttype.startsWith("TTF:")) { +			// Load TTF Font +			return generateTTF(fonttype, pdfObject); +		} else { +			if (fontder == null) { +				fontder = NORMAL; +			} + +			String fontDesc = fonttype + SEP + fontder; +			PDFont font = fontStyleMap.get(fontDesc); +			if (font == null) { +				showBuildinFonts(); +				throw new IOException("Invalid font descriptor"); +			} +			return font; +		} +	} + +	private void setFont(String desc, PDFBOXObject pdfObject) +			throws IOException { +		String[] fontArr = desc.split(","); + +		if (fontArr.length == 3) { +			font = generateFont(fontArr[0], fontArr[2], pdfObject); +			fontSize = Float.parseFloat(fontArr[1]); +		} else if (fontArr.length == 2 && fontArr[0].startsWith("TTF:")) { +			font = generateFont(fontArr[0], null, pdfObject); +			fontSize = Float.parseFloat(fontArr[1]); +		} else { +			logger.warn( +					"Using default font because: {} is not a valid font descriptor.", +					desc); +			this.font = defaultFont; +			this.fontSize = defaultFontSize; +		} + +	} + +	public PDFBoxFont(String fontDesc, ISettings settings, +			PDFBOXObject pdfObject) throws IOException { +		this.settings = settings; +		this.fontDesc = fontDesc; +		logger.debug("Creating Font: " + fontDesc); +		this.setFont(fontDesc, pdfObject); +	} + +	public PDFont getFont(/*PDFBOXObject pdfObject*/) throws IOException { +		if (cachedfont != null) { +			return cachedfont; +		} +		return font; +		/* +		if (font instanceof PDTrueTypeFont && pdfObject != null) { +			return generateTTF(ttfFontDesc, pdfObject); +		} else { +			return font; +		}*/ +	} + +	public float getFontSize() { +		return fontSize; +	} +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxTable.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxTable.java new file mode 100644 index 00000000..415e1665 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxTable.java @@ -0,0 +1,722 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +import java.awt.Color; +import java.awt.Dimension; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.exceptions.PdfAsWrappedIOException; +import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.common.utils.ImageUtils; +import at.gv.egiz.pdfas.common.utils.StringUtils; +import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject; +import at.knowcenter.wag.egov.egiz.table.Entry; +import at.knowcenter.wag.egov.egiz.table.Style; +import at.knowcenter.wag.egov.egiz.table.Table; + +public class PDFBoxTable { + +	private static final Logger logger = LoggerFactory +			.getLogger(PDFBoxTable.class); + +	Table table; +	Style style; +	PDFBoxFont font; +	PDFBoxFont valueFont; +	ISettings settings; + +	float padding; +	int positionX = 0; +	int positionY = 0; +	float tableWidth; +	float tableHeight; +	Color bgColor; + +	boolean[] addPadding; +	float[] rowHeights; +	float[] colWidths; + +	PDDocument originalDoc; + +	PDFBOXObject pdfBoxObject; +	 +	private void normalizeContent(Table abstractTable) throws PdfAsException { +		try { +			int rows = abstractTable.getRows().size(); +			for (int i = 0; i < rows; i++) { +				ArrayList<Entry> row = this.table.getRows().get(i); +				for (int j = 0; j < row.size(); j++) { +					Entry cell = (Entry) row.get(j); + +					switch (cell.getType()) { +					case Entry.TYPE_CAPTION: +					case Entry.TYPE_VALUE: +						String value = (String) cell.getValue(); +						cell.setValue(StringUtils +								.convertStringToPDFFormat(value)); +						break; +					} +				} +			} +		} catch (UnsupportedEncodingException e) { +			throw new PdfAsException("Unsupported Encoding", e); +		} +	} + +	private void initializeStyle(Table abstractTable, PDFBoxTable parent, +			PDFBOXObject pdfBoxObject) throws IOException { +		this.table = abstractTable; +		try { +			normalizeContent(abstractTable); +		} catch (PdfAsException e) { +			throw new PdfAsWrappedIOException(e); +		} + +		if (parent != null) { +			style = Style.doInherit(abstractTable.getStyle(), parent.style); +		} else { +			style = abstractTable.getStyle(); +		} + +		if (style == null) { +			throw new IOException("Failed to determine Table style, for table " +					+ abstractTable.getName()); +		} + +		String fontString = style.getFont(); + +		String vfontString = style.getValueFont(); + +		if (parent != null && style == parent.style) { +			font = parent.getFont(); + +			valueFont = parent.getValueFont(); +		} else { +			if (fontString == null && parent != null && parent.style != null) { +				fontString = parent.style.getFont(); +			} else if (fontString == null) { +				throw new IOException( +						"Failed to determine Table font style, for table " +								+ abstractTable.getName()); +			} + +			font = new PDFBoxFont(fontString, settings, pdfBoxObject); + +			if (vfontString == null && parent != null && parent.style != null) { +				vfontString = parent.style.getValueFont(); +			} else if (fontString == null) { +				throw new IOException( +						"Failed to determine value Table font style, for table " +								+ abstractTable.getName()); +			} + +			valueFont = new PDFBoxFont(vfontString, settings, pdfBoxObject); +		} +		padding = style.getPadding(); + +		bgColor = style.getBgColor(); +	} + +	public PDFBoxTable(Table abstractTable, PDFBoxTable parent, float fixSize, +			ISettings settings, PDFBOXObject pdfBoxObject) throws IOException, +			PdfAsException { +		this.settings = settings; +		this.pdfBoxObject = pdfBoxObject; +		this.originalDoc = pdfBoxObject.getDocument(); +		initializeStyle(abstractTable, parent, pdfBoxObject); +		float[] relativSizes = abstractTable.getColsRelativeWith(); +		if (relativSizes != null) { +			colWidths = new float[relativSizes.length]; +			float totalrel = 0; + +			for (int i = 0; i < relativSizes.length; i++) { +				totalrel += relativSizes[i]; +			} + +			float unit = (fixSize / totalrel); + +			for (int i = 0; i < relativSizes.length; i++) { + +				colWidths[i] = unit * relativSizes[i]; +			} +		} else { +			colWidths = new float[abstractTable.getMaxCols()]; +			float totalrel = abstractTable.getMaxCols(); +			float unit = (fixSize / totalrel); +			for (int i = 0; i < colWidths.length; i++) { + +				colWidths[i] = unit; +			} +		} +		calculateHeightsBasedOnWidths(); + +		logger.debug("Generating Table with fixed With {} got width {}", +				fixSize, getWidth()); +	} + +	public PDFBoxTable(Table abstractTable, PDFBoxTable parent, +			ISettings settings, PDFBOXObject pdfBoxObject) throws IOException, +			PdfAsException { +		this.settings = settings; +		this.pdfBoxObject = pdfBoxObject; +		this.originalDoc = pdfBoxObject.getDocument(); +		initializeStyle(abstractTable, parent, pdfBoxObject); +		this.calculateWidthHeight(); +	} + +	private void calculateHeightsBasedOnWidths() throws IOException, +			PdfAsException { +		int rows = this.table.getRows().size(); +		rowHeights = new float[rows]; +		addPadding = new boolean[rows]; + +		for (int i = 0; i < rows; i++) { +			rowHeights[i] = 0; +		} + +		for (int i = 0; i < rows; i++) { +			ArrayList<Entry> row = this.table.getRows().get(i); +			for (int j = 0; j < row.size(); j++) { +				Entry cell = (Entry) row.get(j); + +				float colWidth = 0;// colWidths[j]; + +				int colsleft = cell.getColSpan(); + +				if (j + colsleft > colWidths.length) { +					throw new IOException( +							"Configuration is wrong. Cannot determine column width!"); +				} + +				for (int k = 0; k < colsleft; k++) { +					colWidth = colWidth + colWidths[j + k]; +				} + +				float cellheight = getCellHeight(cell, colWidth); + +				if (rowHeights[i] < cellheight) { +					rowHeights[i] = cellheight; +				} + +				logger.debug("ROW: {} COL: {} Width: {} Height: {}", i, j, +						colWidth, cellheight); + +				int span = cell.getColSpan() - 1; +				j += span; +			} +		} + +		calcTotals(); +	} + +	private void calculateWidthHeight() throws IOException, PdfAsException { +		int cols = this.table.getMaxCols(); +		colWidths = new float[cols]; + +		for (int i = 0; i < cols; i++) { +			colWidths[i] = 0; +		} + +		int rows = this.table.getRows().size(); +		rowHeights = new float[rows]; + +		for (int i = 0; i < rows; i++) { +			rowHeights[i] = 0; +		} + +		for (int i = 0; i < rows; i++) { +			ArrayList<Entry> row = this.table.getRows().get(i); +			for (int j = 0; j < row.size(); j++) { +				Entry cell = (Entry) row.get(j); +				float cellWidth = getCellWidth(cell); + +				if (colWidths[j] < cellWidth) { +					colWidths[j] = cellWidth; +				} + +				float cellheight = getCellHeight(cell); + +				if (rowHeights[i] < cellheight) { +					rowHeights[i] = cellheight; +				} + +				logger.debug("ROW: {} COL: {} Width: {} Height: {}", i, j, +						cellWidth, cellheight); + +				int span = cell.getColSpan() - 1; +				j += span; +			} +		} + +		calcTotals(); +	} + +	private void calcTotals() { + +		this.tableHeight = 0; + +		for (int i = 0; i < rowHeights.length; i++) { +			this.tableHeight += rowHeights[i]; +		} + +		// Post Process heights for inner Tables ... +		for (int i = 0; i < rowHeights.length; i++) { +			ArrayList<Entry> row = this.table.getRows().get(i); +			for (int j = 0; j < row.size(); j++) { +				Entry cell = (Entry) row.get(j); +				if (cell.getType() == Entry.TYPE_TABLE) { +					PDFBoxTable tbl = (PDFBoxTable) cell.getValue(); +					if (rowHeights[i] != tbl.getHeight()) { +						tbl.setHeight(rowHeights[i]); +					} +				} +			} +		} + +		this.tableWidth = 0; + +		for (int i = 0; i < colWidths.length; i++) { +			this.tableWidth += colWidths[i]; +		} +	} + +	private float getCellWidth(Entry cell) throws IOException, PdfAsException { +		boolean isValue = true; +		switch (cell.getType()) { +		case Entry.TYPE_CAPTION: +			isValue = false; +		case Entry.TYPE_VALUE: +			PDFont c = null; +			float fontSize; +			String string = (String) cell.getValue(); +			if (isValue) { +				c = valueFont.getFont();//null +				fontSize = valueFont.getFontSize(); +			} else { +				c = font.getFont();//null +				fontSize = font.getFontSize(); +			} +			if (string == null) { +				string = ""; +				cell.setValue(string); +			} +			if (string.contains("\n")) { +				float maxWidth = 0; +				String[] lines = string.split("\n"); +				for (int i = 0; i < lines.length; i++) { +					float w = c.getStringWidth(lines[i]) / 1000 * fontSize; +					if (maxWidth < w) { +						maxWidth = w; +					} +				} +				return maxWidth; +			} else { +				return c.getStringWidth(string) / 1000 * fontSize; +			} +		case Entry.TYPE_IMAGE: +			if (style != null && style.getImageScaleToFit() != null) { +				return style.getImageScaleToFit().getWidth(); +			} +			return 80.f; +		case Entry.TYPE_TABLE: +			PDFBoxTable pdfBoxTable = null; +			if (cell.getValue() instanceof Table) { +				pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this, +						this.settings, pdfBoxObject); +				cell.setValue(pdfBoxTable); +			} else if (cell.getValue() instanceof PDFBoxTable) { +				pdfBoxTable = (PDFBoxTable) cell.getValue(); +			} else { +				throw new IOException("Failed to build PDFBox Table"); +			} +			return pdfBoxTable.getWidth(); +		default: +			logger.warn("Invalid Cell Entry Type: " + cell.getType()); +		} +		return 0; +	} + +	private String concatLines(String[] lines) { +		String v = ""; +		for (int i = 0; i < lines.length; i++) { +			v += lines[i]; +			if (i + 1 < lines.length) { +				v += "\n"; +			} +		} +		return v; +	} + +	private String[] breakString(String value, float maxwidth, PDFont font, +			float fontSize) throws IOException { +		String[] words = value.split(" "); +		List<String> lines = new ArrayList<String>(); +		String cLineValue = ""; +		for (int i = 0; i < words.length; i++) { +			String word = words[i]; +			String[] lineBreaks = word.split("\n"); +			if (lineBreaks.length > 1) { +				for (int j = 0; j < lineBreaks.length; j++) { +					String subword = lineBreaks[j]; +					// if (cLine + subword.length() > maxline) { +					if (j == 0 && word.startsWith("\n")) { +						lines.add(cLineValue.trim()); +						cLineValue = ""; +					} else if (j != 0) { +						lines.add(cLineValue.trim()); +						cLineValue = ""; +					} +					// } +					String tmpLine = cLineValue + subword; +					float size = font.getStringWidth(tmpLine) / 1000.0f +							* fontSize; +					if (size > maxwidth && cLineValue.length() != 0) { +						lines.add(cLineValue.trim()); +						cLineValue = ""; +					} +					cLineValue += subword + " "; +				} +			} else { +				String tmpLine = cLineValue + word; +				float size = font.getStringWidth(tmpLine) / 1000.0f * fontSize; +				if (size > maxwidth && cLineValue.length() != 0) { +					lines.add(cLineValue.trim()); +					cLineValue = ""; +				} +				cLineValue += word + " "; +			} +		} +		lines.add(cLineValue.trim()); +		return lines.toArray(new String[0]); +	} + +	private String[] breakString(String value, int maxline) { +		String[] words = value.split(" "); +		List<String> lines = new ArrayList<String>(); +		int cLine = 0; +		String cLineValue = ""; +		for (int i = 0; i < words.length; i++) { +			String word = words[i]; +			String[] lineBreaks = word.split("\n"); +			if (lineBreaks.length > 1) { +				for (int j = 0; j < lineBreaks.length; j++) { +					String subword = lineBreaks[j]; +					// if (cLine + subword.length() > maxline) { +					lines.add(cLineValue.trim()); +					cLineValue = ""; +					cLine = 0; +					// } +					cLineValue += subword + " "; +					cLine += subword.length(); +				} +			} else { +				if (cLine + word.length() > maxline && cLineValue.length() != 0) { +					lines.add(cLineValue.trim()); +					cLineValue = ""; +					cLine = 0; +				} +				cLineValue += word + " "; +				cLine += word.length(); +			} +		} +		lines.add(cLineValue.trim()); +		return lines.toArray(new String[0]); +	} + +	// private String[] breakString(String value, PDFont f, float maxwidth) +	// throws IOException { +	// String[] words = value.split(" "); +	// List<String> lines = new ArrayList<String>(); +	// int cLine = 0; +	// String cLineValue = ""; +	// for (int i = 0; i < words.length; i++) { +	// String word = words[i]; +	// String[] lineBreaks = word.split("\n"); +	// if (lineBreaks.length > 1) { +	// for (int j = 0; j < lineBreaks.length; j++) { +	// String subword = lineBreaks[j]; +	// // if (cLine + subword.length() > maxline) { +	// lines.add(cLineValue.trim()); +	// cLineValue = ""; +	// cLine = 0; +	// // } +	// cLineValue += subword + " "; +	// cLine += subword.length(); +	// } +	// } else { +	// if (f.getStringWidth(cLineValue + word) > maxwidth && cLineValue.length() +	// != 0) { +	// lines.add(cLineValue.trim()); +	// cLineValue = ""; +	// cLine = 0; +	// } +	// cLineValue += word + " "; +	// cLine += word.length(); +	// } +	// } +	// lines.add(cLineValue.trim()); +	// return lines.toArray(new String[0]); +	// } + +	private float[] getStringHeights(String[] lines, PDFont c, float fontSize) { +		float[] heights = new float[lines.length]; +		for (int i = 0; i < lines.length; i++) { +			float maxLineHeight = 0; +			try { +				byte[] linebytes = StringUtils.applyWinAnsiEncoding(lines[i]); +				for (int j = 0; j < linebytes.length; j++) { +					float he = c.getHeight(linebytes[j]) / 1000 +							* fontSize; +					if (he > maxLineHeight) { +						maxLineHeight = he; +					} +				} +			} catch (UnsupportedEncodingException e) { +				logger.warn("failed to determine String height", e); +				maxLineHeight = c.getFontDescriptor().getCapHeight() / 1000 +						* fontSize; +			} catch (IOException e) { +				logger.warn("failed to determine String height", e); +				maxLineHeight = c.getFontDescriptor().getCapHeight() / 1000 +						* fontSize; +			} + +			heights[i] = maxLineHeight; +		} + +		return heights; +	} + +	private float getCellHeight(Entry cell, float width) throws IOException, +			PdfAsException { +		boolean isValue = true; +		switch (cell.getType()) { +		case Entry.TYPE_CAPTION: +			isValue = false; +		case Entry.TYPE_VALUE: +			PDFont c = null; +			float fontSize; +			String string = (String) cell.getValue(); +			if (isValue) { +				c = valueFont.getFont();//null +				fontSize = valueFont.getFontSize(); +			} else { +				c = font.getFont();//null +				fontSize = font.getFontSize(); +			} + +			String[] lines = breakString(string, (width - padding * 2.0f), c, +					fontSize); +			cell.setValue(concatLines(lines)); +			float[] heights = getStringHeights(lines, c, fontSize); +			return fontSize * heights.length + padding * 2; +		case Entry.TYPE_IMAGE: +			String imageFile = (String) cell.getValue(); +			if (style != null && style.getImageScaleToFit() != null) { +				// if (style.getImageScaleToFit().getHeight() < width) { +				return style.getImageScaleToFit().getHeight() + padding * 2; +				// } +			} +			Dimension dim = ImageUtils.getImageDimensions(imageFile, settings); +			float wfactor = (float) ((width - padding * 2.0f) / dim.getWidth()); +			float scaleFactor = wfactor; +			float iheight = (float) Math +					.floor((double) (scaleFactor * dim.getHeight())); +			//if (dim.getHeight() > 80.0f) { +			//	return width + padding * 2; +			//} +			return (float) iheight + padding * 2; +		case Entry.TYPE_TABLE: +			PDFBoxTable pdfBoxTable = null; +			if (cell.getValue() instanceof Table) { +				pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this, +						width, this.settings, this.pdfBoxObject); +				cell.setValue(pdfBoxTable); +			} else if (cell.getValue() instanceof PDFBoxTable) { +				// recreate here beacuse of fixed width! +				pdfBoxTable = (PDFBoxTable) cell.getValue(); +				pdfBoxTable = new PDFBoxTable(pdfBoxTable.table, this, width, +						this.settings, this.pdfBoxObject); +				cell.setValue(pdfBoxTable); +			} else { +				throw new IOException("Failed to build PDFBox Table"); +			} +			return pdfBoxTable.getHeight(); +		default: +			logger.warn("Invalid Cell Entry Type: " + cell.getType()); +		} +		return 0; +	} + +	private float getCellHeight(Entry cell) throws IOException, PdfAsException { +		boolean isValue = true; +		switch (cell.getType()) { +		case Entry.TYPE_CAPTION: +			isValue = false; +		case Entry.TYPE_VALUE: +			PDFont c = null; +			float fontSize; +			String string = (String) cell.getValue(); +			if (isValue) { +				c = valueFont.getFont();//null +				fontSize = valueFont.getFontSize(); +			} else { +				c = font.getFont();//null +				fontSize = font.getFontSize(); +			} + +			if (string.contains("\n")) { +				String[] lines = string.split("\n"); + +				return fontSize * lines.length + padding * 2; +			} else { +				return fontSize + padding * 2; +			} +		case Entry.TYPE_IMAGE: +			String imageFile = (String) cell.getValue(); +			if (style != null && style.getImageScaleToFit() != null) { +				return style.getImageScaleToFit().getHeight() + padding * 2; +			} +			Dimension dim = ImageUtils.getImageDimensions(imageFile, settings); +			if (dim.getHeight() > 80.0f) { +				return 80.0f + padding * 2; +			} +			return (float) dim.getHeight() + padding * 2; + +		case Entry.TYPE_TABLE: +			PDFBoxTable pdfBoxTable = null; +			if (cell.getValue() instanceof Table) { +				pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this, +						this.settings, pdfBoxObject); +				cell.setValue(pdfBoxTable); +			} else if (cell.getValue() instanceof PDFBoxTable) { +				pdfBoxTable = (PDFBoxTable) cell.getValue(); +			} else { +				throw new IOException("Failed to build PDFBox Table"); +			} +			return pdfBoxTable.getHeight(); +		default: +			logger.warn("Invalid Cell Entry Type: " + cell.getType()); +		} +		return 0; +	} + +	public int getX() { +		return this.positionX; +	} + +	public int getY() { +		return this.positionY; +	} + +	public float getWidth() { +		return tableWidth; +	} + +	public float getHeight() { +		return tableHeight; +	} + +	public void setHeight(float height) { +		float diff = height - this.getHeight(); +		if (diff > 0) { +			this.rowHeights[rowHeights.length - 1] += diff; +			calcTotals(); +		} else { +			logger.warn("Table cannot be this small!"); +		} +	} + +	public float[] getRowHeights() { +		return rowHeights; +	} + +	public int getRowCount() { +		return this.table.getRows().size(); +	} + +	public int getColCount() { +		return this.table.getMaxCols();// .getColsRelativeWith().length; +	} + +	public float[] getColsRelativeWith() { +		return this.table.getColsRelativeWith(); +	} + +	public float getPadding() { +		return this.padding; +	} + +	public void dumpTable() { +		logger.debug("====================================================================="); +		logger.debug("Information about: " + this.table.getName()); +		logger.debug("\tDimensions: {} x {} (W x H)", this.tableWidth, +				this.tableHeight); +		logger.debug("\tPadding: {}", padding); +		logger.debug("\t================================"); +		logger.debug("\tRow Heights:"); +		for (int i = 0; i < rowHeights.length; i++) { +			logger.debug("\t[{}] : {}", i, this.rowHeights[i]); +		} +		logger.debug("\t================================"); +		logger.debug("\tCol Widths:"); +		for (int i = 0; i < colWidths.length; i++) { +			logger.debug("\t[{}] : {}", i, this.colWidths[i]); +		} +		logger.debug("\t================================"); +		logger.debug("\tTable:"); +		logger.debug("\t" + this.table.toString()); +		logger.debug("====================================================================="); +	} + +	public Table getOrigTable() { +		return this.table; +	} + +	public ArrayList<Entry> getRow(int i) { +		return this.table.getRows().get(i); +	} + +	public PDFBoxFont getFont() { +		return font; +	} + +	public PDFBoxFont getValueFont() { +		return valueFont; +	} + +	public Color getBGColor() { +		return this.bgColor; +	} +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxStamper.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxStamper.java new file mode 100644 index 00000000..f89d53c5 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxStamper.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +import java.io.IOException; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.exceptions.PdfAsWrappedIOException; +import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject; +import at.gv.egiz.pdfas.lib.impl.stamping.IPDFStamper; +import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject; +import at.gv.egiz.pdfas.lib.impl.status.PDFObject; +import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; +import at.knowcenter.wag.egov.egiz.table.Table; + +public class PdfBoxStamper implements IPDFStamper { + +//	private static final Logger logger = LoggerFactory.getLogger(PdfBoxStamper.class); + +//	private PDFTemplateBuilder pdfBuilder; + +	public PdfBoxStamper() { +//		this.pdfBuilder = new PDVisibleSigBuilder(); +	} +	 +	public IPDFVisualObject createVisualPDFObject(PDFObject pdf, Table table) throws IOException { +		try { +			PDFBOXObject pdfboxObject = (PDFBOXObject)pdf; +			return new PdfBoxVisualObject(table, pdf.getStatus().getSettings(), pdfboxObject); +		} catch (PdfAsException e) { +			throw new PdfAsWrappedIOException(e); +		} +	} + +	public byte[] writeVisualObject(IPDFVisualObject visualObject, +			PositioningInstruction positioningInstruction, byte[] pdfData, +			String placeholderName) throws PdfAsException { +		return null; +	} + +	public void setSettings(ISettings settings) { +		// not needed currently +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxVisualObject.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxVisualObject.java new file mode 100644 index 00000000..3a4c3957 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxVisualObject.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject; +import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject; +import at.knowcenter.wag.egov.egiz.table.Table; + +public class PdfBoxVisualObject implements IPDFVisualObject { + +	private static final Logger logger = LoggerFactory +			.getLogger(PdfBoxVisualObject.class); +	 +	private Table abstractTable; +	private PDFBoxTable table; +	private float width; +	private float x; +	private float y; +	private int page; +	private ISettings settings; +	private PDFBOXObject pdfBoxObject; + +	public PdfBoxVisualObject(Table table, ISettings settings, PDFBOXObject pdfBoxObject) +			throws IOException, PdfAsException { +		this.abstractTable = table; +		this.pdfBoxObject = pdfBoxObject; +		this.table = new PDFBoxTable(table, null, settings, pdfBoxObject); +		this.settings = settings; +	} + +	public void setWidth(float width) { +		this.width = width; +	} + +	public void fixWidth() { +		try { +			table = new PDFBoxTable(abstractTable, null, this.width,  settings, this.pdfBoxObject); +		} catch (IOException e) { +			logger.warn("Failed to fix width of Table!", e); +		} catch (PdfAsException e) { +			logger.warn("Failed to fix width of Table!", e); +		} +	} + +	public float getHeight() { +		return table.getHeight(); +	} + +	public float getWidth() { +		return table.getWidth(); +	} + +	public void setXPos(float x) { +		this.x = x; +	} + +	public void setYPos(float y) { +		this.y = y; +	} +	 +	public float getX() { +        return x; +    } + +    public float getY() { +        return y; +    } + +	public int getPage() { +		return page; +	} + +	public void setPage(int page) { +		this.page = page; +	} + +	public PDFBoxTable getTable() { +		return this.table; +	} +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/StamperFactory.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/StamperFactory.java new file mode 100644 index 00000000..90561740 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/StamperFactory.java @@ -0,0 +1,25 @@ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.lib.impl.stamping.IPDFStamper; + +public class StamperFactory { + +	//public static final String DEFAULT_STAMPER_CLASS = "at.gv.egiz.pdfas.stmp.itext.ITextStamper"; +	public static final String DEFAULT_STAMPER_CLASS = "at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2.PdfBoxStamper"; + +	public static IPDFStamper createDefaultStamper(ISettings settings) throws PdfAsException { +		try { +			Class<?> cls = Class.forName(DEFAULT_STAMPER_CLASS); +			Object st = cls.newInstance(); +			if (!(st instanceof IPDFStamper)) +				throw new ClassCastException(); +			IPDFStamper stamper = (IPDFStamper) st; +			stamper.setSettings(settings); +			return stamper; +		} catch (Throwable e) { +			throw new PdfAsException("error.pdf.stamp.10", e); +		} +	} +}
\ No newline at end of file diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/TableDrawUtils.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/TableDrawUtils.java new file mode 100644 index 00000000..aa2a397d --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/TableDrawUtils.java @@ -0,0 +1,609 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; + +import java.awt.Color; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; + +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.settings.ISettings; +import at.knowcenter.wag.egov.egiz.table.Entry; +import at.knowcenter.wag.egov.egiz.table.Style; + +public class TableDrawUtils { + +	private static final Logger logger = LoggerFactory +			.getLogger(TableDrawUtils.class); + +	public static final String TABLE_DEBUG = "debug.table"; +	 +	public static void drawTable(PDPage page, +			PDPageContentStream contentStream, float x, float y, float width, +			float height, PDFBoxTable abstractTable, PDDocument doc, +			boolean subtable, PDResources formResources, +			Map<String, ImageObject> images, ISettings settings, IDGenerator generator, PDFAsVisualSignatureProperties properties) +			throws PdfAsException { +		 +		logger.debug("Drawing Table: X {} Y {} WIDTH {} HEIGHT {} \n{}", x, y, +				width, height, abstractTable.getOrigTable().toString()); + +		abstractTable.getOrigTable().setWidth(width); + +		drawTableBackground(page, contentStream, x, y, width, height, +				abstractTable, settings); + +		drawBorder(page, contentStream, x, y, width, height, abstractTable, +				doc, subtable, settings); +//append strings +		drawContent(page, contentStream, x, y, width, height, abstractTable, +				doc, subtable, formResources, images, settings, generator, properties); +	} + +	public static void drawContent(PDPage page, +			PDPageContentStream contentStream, float x, float y, float width, +			float height, PDFBoxTable abstractTable, PDDocument doc, +			boolean subtable, PDResources formResources, +			Map<String, ImageObject> images, ISettings settings, IDGenerator generator, PDFAsVisualSignatureProperties properties) +			throws PdfAsException { + +		float contentx = x; +		float contenty = y + height; +		float padding = abstractTable.getPadding(); +		float[] colsSizes = getColSizes(abstractTable); +		StringBuilder alternateTableCaption = new StringBuilder(); +		for (int i = 0; i < abstractTable.getRowCount(); i++) { +			ArrayList<Entry> row = abstractTable.getRow(i); +			for (int j = 0; j < row.size(); j++) { +				Entry cell = (Entry) row.get(j); + +				// Cell only contains default values so table style is the primary style +				Style inherit_style = Style.doInherit(abstractTable.style, cell.getStyle()); +				cell.setStyle(inherit_style); + +				float colWidth = 0;//colWidths[j]; + +				int colsleft = cell.getColSpan(); + +				if (j + colsleft > colsSizes.length) { +					throw new PdfAsException( +							"Configuration is wrong. Cannot determine column width!"); +				} + +				for (int k = 0; k < colsleft; k++) { +					colWidth = colWidth + colsSizes[j + k]; +				} +				 +				drawDebugPadding(contentStream, contentx, contenty, padding, +						colWidth, abstractTable.getRowHeights()[i], settings); + +				switch (cell.getType()) { +				case Entry.TYPE_CAPTION: +					drawCaption(page, contentStream, contentx, contenty, +							colWidth, abstractTable.getRowHeights()[i], +							padding, abstractTable, doc, cell, formResources, settings); +					addToAlternateTableCaption(cell, alternateTableCaption); +					break; +				case Entry.TYPE_VALUE: +					drawValue(page, contentStream, contentx, contenty, +							colWidth, abstractTable.getRowHeights()[i], +							padding, abstractTable, doc, cell, formResources, settings); +					addToAlternateTableCaption(cell, alternateTableCaption); +					break; +				case Entry.TYPE_IMAGE: +					drawImage(page, contentStream, contentx, contenty, +							colWidth, abstractTable.getRowHeights()[i], +							padding, abstractTable, doc, cell, formResources, +							images, settings, generator); +					break; +				case Entry.TYPE_TABLE: + +					PDFBoxTable tbl_value = (PDFBoxTable) cell.getValue(); + +					Style inherit_styletab = Style.doInherit( +							abstractTable.style, cell.getStyle()); +					tbl_value.table.setStyle(inherit_styletab); + +					drawTable(page, contentStream, contentx, contenty +							- abstractTable.getRowHeights()[i], colWidth, +							abstractTable.getRowHeights()[i], tbl_value, doc, +							true, formResources, images, settings, generator,properties); +					break; +				default: +					logger.warn("Unknown Cell entry type: " + cell.getType()); +					break; +				} + +				// Move content pointer +				contentx += colWidth; +				 +				int span = cell.getColSpan() - 1; +				j += span; +			} + +			// Move content pointer +			contenty -= abstractTable.getRowHeights()[i]; +			contentx = x; +		} +		properties.setAlternativeTableCaption(alternateTableCaption.toString()); +	} + +	private static void drawString(PDPage page, +			PDPageContentStream contentStream, float contentx, float contenty, +			float width, float height, float padding, +			PDFBoxTable abstractTable, PDDocument doc, Entry cell, +			float fontSize, float textHeight, String valign, String halign, +			String[] tlines, PDFont textFont, PDResources formResources, +			ISettings settings) throws PdfAsException { +		try { +			float ty = contenty - padding; +			float tx = contentx + padding; +			float innerHeight = height - (2 * padding); +			float innerWidth = width - (2 * padding); +			if (Style.BOTTOM.equals(valign)) { +				float bottom_offset = innerHeight - textHeight; +				ty -= bottom_offset; +			} else if (Style.MIDDLE.equals(valign)) { +				float bottom_offset = innerHeight - textHeight; +				bottom_offset = bottom_offset / 2.0f; +				ty -= bottom_offset; +			} + +			// calculate the max with of the text content +			float maxWidth = 0; +			for (int k = 0; k < tlines.length; k++) { +				float lineWidth; +				// if (textFont instanceof PDType1Font) { +				lineWidth = textFont.getStringWidth(tlines[k]) / 1000.0f +						* fontSize; +				/* +				 * } else { float fwidth = textFont +				 * .getStringWidth("abcdefghijklmnopqrstuvwxyz ") / 1000.0f * +				 * fontSize; fwidth = fwidth / (float) +				 * "abcdefghijklmnopqrstuvwxyz" .length(); lineWidth = +				 * tlines[k].length() * fwidth; } +				 */ +				if (maxWidth < lineWidth) { +					maxWidth = lineWidth; +				} +			} + +			if (Style.CENTER.equals(halign)) { +				float offset = innerWidth - maxWidth; +				if (offset > 0) { +					offset = offset / 2.0f; +					tx += offset; +				} +			} else if (Style.RIGHT.equals(halign)) { +				float offset = innerWidth - maxWidth; +				if (offset > 0) { +					tx += offset; +				} +			} +			float ascent = textFont.getFontDescriptor().getAscent(); +			float descent = textFont.getFontDescriptor().getDescent(); +			 +			ascent = ascent / 1000.0f * fontSize; +			descent = descent / 1000.0f * fontSize; +			 +			//ty = ty + (descent * (-1)); +			 +			logger.debug("Text tx {} ty {} maxWidth {} textHeight {}", tx, ty, +					maxWidth, textHeight); +			logger.debug("Text ASCENT {} DESCENT {}", ascent, descent); + +			logger.debug("Text TRANSFORMED ASCENT {} DESCENT {}", ascent, descent); +			 +			drawDebugLineString(contentStream, tx, ty, maxWidth, textHeight, descent, settings); + +			contentStream.beginText(); +			 +		 +			if (formResources.getFont(COSName.getPDFName(textFont.getName())) != null) { +				String fontID = getFontID(textFont, formResources); +				logger.debug("Using Font: " + fontID); +				contentStream.appendRawCommands("/" + fontID + " " + fontSize +						+ " Tf\n"); +			} else { +				contentStream.setFont(textFont, fontSize); +			} + +			logger.debug("Writing: " + tx + " : " + (ty - fontSize + (descent * (-1))) + " as " +					+ cell.getType()); +			contentStream.moveTextPositionByAmount(tx, (ty - fontSize + (descent * (-1)))); + +			contentStream.appendRawCommands(fontSize + " TL\n"); +			for (int k = 0; k < tlines.length; k++) { +				contentStream.drawString(tlines[k]); +				if (k < tlines.length - 1) { +					contentStream.appendRawCommands("T*\n"); +				} +			} + +			contentStream.endText(); + +		} catch (IOException e) { +			logger.warn("IO Exception", e); +			throw new PdfAsException("Error", e); +		} +	} + +	public static void drawCaption(PDPage page, +			PDPageContentStream contentStream, float contentx, float contenty, +			float width, float height, float padding, +			PDFBoxTable abstractTable, PDDocument doc, Entry cell, +			PDResources formResources, ISettings settings) +			throws PdfAsException { + +		logger.debug("Drawing Caption @ X: {} Y: {}", contentx, contenty); + +		try { +			float fontSize = PDFBoxFont.defaultFontSize; +			PDFont textFont = PDFBoxFont.defaultFont; + +			textFont = abstractTable.getFont().getFont();//doc); +			fontSize = abstractTable.getFont().getFontSize(); + +			// get the cell Text +			String text = (String) cell.getValue(); +			String[] tlines = text.split("\n"); +			float textHeight = fontSize * tlines.length; + +			Style cellStyle = cell.getStyle(); +			String valign = cellStyle.getVAlign(); +			String halign = cellStyle.getHAlign(); +			 +			drawString(page, contentStream, contentx, contenty, width, height, +					padding, abstractTable, doc, cell, fontSize, textHeight, +					valign, halign, tlines, textFont, formResources, settings); +		} catch (IOException e) { +			logger.warn("IO Exception", e); +			throw new PdfAsException("Error", e); +		} +	} + +	public static void drawValue(PDPage page, +			PDPageContentStream contentStream, float contentx, float contenty, +			float width, float height, float padding, +			PDFBoxTable abstractTable, PDDocument doc, Entry cell, +			PDResources formResources, ISettings settings) +			throws PdfAsException { + +		logger.debug("Drawing Value @ X: {} Y: {}", contentx, contenty); + +		try { +			float fontSize = PDFBoxFont.defaultFontSize; +			PDFont textFont = PDFBoxFont.defaultFont; + +			textFont = abstractTable.getValueFont().getFont();//doc); +			fontSize = abstractTable.getValueFont().getFontSize(); + +			// get the cell Text +			String text = (String) cell.getValue(); +			String[] tlines = text.split("\n"); +			float textHeight = fontSize * tlines.length; + +			Style cellStyle = cell.getStyle(); +			String valign = cellStyle.getValueVAlign(); +			String halign = cellStyle.getValueHAlign(); + +			drawString(page, contentStream, contentx, contenty, width, height, +					padding, abstractTable, doc, cell, fontSize, textHeight, +					valign, halign, tlines, textFont, formResources, settings); +		} catch (IOException e) { +			logger.warn("IO Exception", e); +			throw new PdfAsException("Error", e); +		} +	} + +	public static void drawImage(PDPage page, +			PDPageContentStream contentStream, float contentx, float contenty, +			float width, float height, float padding, +			PDFBoxTable abstractTable, PDDocument doc, Entry cell, +			PDResources formResources, Map<String, ImageObject> images, +			ISettings settings, IDGenerator generator) throws PdfAsException { +		try { +			float innerHeight = height; +			float innerWidth = width; +						 +			String img_ref = generator.createHashedId((String) cell.getValue()); +			if (!images.containsKey(img_ref)) { +				logger.warn("Image not prepared! : " + img_ref); +				throw new PdfAsException("Image not prepared! : " + img_ref); +			} +			ImageObject image = images.get(img_ref); +			PDImageXObject pdImage = image.getImage(); + + +			float imgx = contentx; +			float hoffset = innerWidth - image.getWidth(); +			if (cell.getStyle().getImageHAlign() != null +					&& cell.getStyle().getImageHAlign().equals(Style.LEFT)) { +				hoffset = hoffset / 2.0f; +				imgx += hoffset; +			} else if (cell.getStyle().getImageHAlign() != null +					&& cell.getStyle().getImageHAlign().equals(Style.RIGHT)) { +				imgx += hoffset; +			} else { +				hoffset = hoffset / 2.0f; +				imgx += hoffset; +			} + +			float imgy = contenty; +			float voffset = innerHeight - image.getHeight(); +			if (cell.getStyle().getImageVAlign() != null +					&& cell.getStyle().getImageVAlign().equals(Style.MIDDLE)) { +				voffset = voffset / 2.0f; +				imgy -= voffset; +			} else if (cell.getStyle().getImageVAlign() != null +					&& cell.getStyle().getImageVAlign().equals(Style.BOTTOM)) { +				imgy -= voffset; +			} + +			drawDebugLine(contentStream, imgx, imgy, image.getWidth(), +					image.getHeight(), settings); + +			// logger.debug("Image: " + imgx + " : " + (imgy - +			// image.getHeight())); +			contentStream.drawXObject(pdImage, imgx, imgy - image.getHeight(), +					image.getWidth(), image.getHeight()); +		} catch (IOException e) { +			logger.warn("IO Exception", e); +			throw new PdfAsException("Error", e); +		} + +	} + +	public static float[] getColSizes(PDFBoxTable abstractTable) { +		float[] origcolsSizes = abstractTable.getColsRelativeWith(); +		int max_cols = abstractTable.getColCount(); +		float[] colsSizes = new float[max_cols]; +		if (origcolsSizes == null) { +			// set the column ratio for all columns to 1 +			for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { +				colsSizes[cols_idx] = 1; +			} +		} else { +			// set the column ratio for all columns to 1 +			for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { +				colsSizes[cols_idx] = origcolsSizes[cols_idx]; +			} +		} + +		// adapt +		float total = 0; + +		for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { +			total += colsSizes[cols_idx]; +		} + +		for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { +			colsSizes[cols_idx] = (colsSizes[cols_idx] / total) +					* abstractTable.getWidth(); +		} + +		float sum = 0; + +		for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { +			sum += colsSizes[cols_idx]; +		} + +		logger.debug("Table Col Sizes SUM {} Table Width {}", sum, +				abstractTable.getWidth()); +		logger.debug("Table Table Height {}", abstractTable.getHeight()); + +		return colsSizes; +	} + +	public static void drawBorder(PDPage page, +			PDPageContentStream contentStream, float x, float y, float width, +			float height, PDFBoxTable abstractTable, PDDocument doc, +			boolean subtable, ISettings settings) throws PdfAsException { +		try { + +			logger.debug("Drawing Table borders for " +					+ abstractTable.getOrigTable().getName()); + +			final int rows = abstractTable.getRowCount(); +			float border = abstractTable.style.getBorder(); +			float[] colsSizes = getColSizes(abstractTable); + +			if (border > 0) { +				contentStream.setLineWidth(border); + +				float x_from = x; +				float x_to = x + width; +				float y_from = y + height; +				float y_to = y + height; + +				// draw first line +				logger.debug("ROW LINE: {} {} {} {}", x_from, y_from, x_to, +						y_from); +				contentStream.drawLine(x, y_from, x_to, y_from); + +				// Draw all row borders +				for (int i = 0; i < rows; i++) { +					y_from -= abstractTable.getRowHeights()[i]; + +					// Draw row border! +					logger.debug("ROW LINE: {} {} {} {}", x_from, y_from, x_to, +							y_from); +					contentStream.drawLine(x, y_from, x_to, y_from); + +				} + +				// reset y for "line feed" +				y_from = y + height; +				y_to = y_from - abstractTable.getRowHeights()[0]; + +				// Draw all column borders +				for (int i = 0; i < rows; i++) { +					ArrayList<Entry> row = abstractTable.getRow(i); + +					// reset x for "line feed" +					x_from = x; + +					// draw first line +					logger.debug("COL LINE: {} {} {} {}", x_from, y_from, +							x_from, y_to); + +					contentStream.drawLine(x_from, y_from, x_from, y_to); + +					for (int j = 0; j < row.size(); j++) { +						Entry cell = (Entry) row.get(j); + +						for (int k = 0; k < cell.getColSpan(); k++) { +							if (k + j < colsSizes.length) { +								x_from += colsSizes[k + j]; +							} +						} +						logger.debug("COL LINE: {} {} {} {}", x_from, y_from, +								x_from, y_to); +						contentStream.drawLine(x_from, y_from, x_from, y_to); +					} + +					if (i + 1 < rows) { +						y_from = y_to; +						y_to = y_from - abstractTable.getRowHeights()[i + 1]; +					} +				} + +			} +		} catch (Throwable e) { +			logger.warn("drawing table borders", e); +			throw new PdfAsException("drawing table borders", e); +		} +	} + +	public static void drawTableBackground(PDPage page, +			PDPageContentStream contentStream, float x, float y, float width, +			float height, PDFBoxTable abstractTable, ISettings settings) +			throws PdfAsException { +		try { +			if (abstractTable.getBGColor() != null) { +				contentStream.setNonStrokingColor(abstractTable.getBGColor()); +				contentStream.fillRect(x, y, abstractTable.getWidth(), +						abstractTable.getHeight()); +				contentStream.setNonStrokingColor(Color.BLACK); +			} +		} catch (Throwable e) { +			logger.warn("drawing table borders", e); +			throw new PdfAsException("drawing table borders", e); +		} +	} + +	private static void drawDebugLine(PDPageContentStream contentStream, +			float x, float y, float width, float height, ISettings settings) { +		if ("true".equals(settings.getValue(TABLE_DEBUG))) { +			try { +				contentStream.setStrokingColor(Color.RED); +				contentStream.drawLine(x, y, x + width, y); +				contentStream.setStrokingColor(Color.BLUE); +				contentStream.drawLine(x, y, x, y - height); +				contentStream.setStrokingColor(Color.GREEN); +				contentStream.drawLine(x + width, y, x + width, y - height); +				contentStream.setStrokingColor(Color.ORANGE); +				contentStream.drawLine(x, y - height, x + width, y - height); + +				contentStream.setStrokingColor(Color.BLACK); +			} catch (Throwable e) { +				e.printStackTrace(); +			} +		} +	} +	 +	private static void drawDebugLineString(PDPageContentStream contentStream, +			float x, float y, float width, float height, float descent, ISettings settings) { +		if ("true".equals(settings.getValue(TABLE_DEBUG))) { +			try { +				contentStream.setStrokingColor(Color.RED); +				contentStream.drawLine(x, y, x + width, y); +				contentStream.setStrokingColor(Color.BLUE); +				contentStream.drawLine(x, y, x, y - height); +				contentStream.setStrokingColor(Color.GREEN); +				contentStream.drawLine(x + width, y, x + width, y - height); +				contentStream.setStrokingColor(Color.ORANGE); +				contentStream.drawLine(x, y - height, x + width, y - height); +				contentStream.setStrokingColor(Color.MAGENTA); +				contentStream.drawLine(x, y + (descent * (-1)) - height, x + width, y + (descent * (-1)) - height); + +				contentStream.setStrokingColor(Color.BLACK); +			} catch (Throwable e) { +				e.printStackTrace(); +			} +		} +	} + +	private static void drawDebugPadding(PDPageContentStream contentStream, +			float x, float y, float padding, float width, float height, +			ISettings settings) { +		if ("true".equals(settings.getValue(TABLE_DEBUG))) { +			try { +				contentStream.setStrokingColor(Color.RED); +				contentStream.drawLine(x, y, x + padding, y - padding); +				contentStream.drawLine(x + width, y, x + width - padding, y +						- padding); +				contentStream.drawLine(x + width, y - height, x + width +						- padding, y - height + padding); +				contentStream.drawLine(x, y - height, x + padding, y - height +						+ padding); +				contentStream.setStrokingColor(Color.BLACK); +			} catch (Throwable e) { +				e.printStackTrace(); +			} +		} +	} + +	private static String getFontID(PDFont font, PDResources resources) { +//		Iterator<java.util.Map.Entry<String, PDFont>> it = resources.getfon +//				.entrySet().iterator(); +		Iterator<COSName> it = resources.getFontNames().iterator(); +		while (it.hasNext()) { +			COSName entry = it.next(); +			if (entry.getName().equals(font.getName())) { +				return entry.getName(); +			} +		} +		return ""; +	} +	 +	private static void addToAlternateTableCaption(Entry cell, StringBuilder alternateTableCaption){ +		alternateTableCaption.append(cell.getValue()); +		alternateTableCaption.append("\n");//better for screen reader +	} + + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/pdfbox2/PDFBOXVerifier.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/pdfbox2/PDFBOXVerifier.java new file mode 100644 index 00000000..270e9e28 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/pdfbox2/PDFBOXVerifier.java @@ -0,0 +1,158 @@ +package at.gv.egiz.pdfas.lib.impl.verify.pdfbox2; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PDFASError; +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter; +import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; +import at.gv.egiz.pdfas.lib.impl.ErrorExtractor; +import at.gv.egiz.pdfas.lib.impl.verify.IVerifier; +import at.gv.egiz.pdfas.lib.impl.verify.IVerifyFilter; +import at.gv.egiz.pdfas.lib.impl.verify.VerifierDispatcher; +import at.gv.egiz.pdfas.lib.impl.verify.VerifyBackend; + +public class PDFBOXVerifier implements VerifyBackend { + +	private static final Logger logger = LoggerFactory.getLogger(PDFBOXVerifier.class); + +	@Override +	public List<VerifyResult> verify(VerifyParameter parameter) throws PDFASError { +		int signatureToVerify = parameter.getWhichSignature(); +		int currentSignature = 0; +		PDDocument doc = null; +		try { +			List<VerifyResult> result = new ArrayList<VerifyResult>(); +			ISettings settings = (ISettings) parameter.getConfiguration(); +			VerifierDispatcher verifier = new VerifierDispatcher(settings); +			doc = PDDocument.load(parameter.getDataSource().getInputStream()); + +			COSDictionary trailer = doc.getDocument().getTrailer(); +			if (trailer == null) { +				// No signatures ... +				return result; +			} +			COSDictionary root = (COSDictionary) trailer.getDictionaryObject(COSName.ROOT); +			if (root == null) { +				// No signatures ... +				return result; +			} +			COSDictionary acroForm = (COSDictionary) root.getDictionaryObject(COSName.ACRO_FORM); +			if (acroForm == null) { +				// No signatures ... +				return result; +			} +			COSArray fields = (COSArray) acroForm.getDictionaryObject(COSName.FIELDS); +			if (fields == null) { +				// No signatures ... +				return result; +			} + +			int lastSig = -1; +			for (int i = 0; i < fields.size(); i++) { +				COSDictionary field = (COSDictionary) fields.getObject(i); +				String type = field.getNameAsString("FT"); +				if ("Sig".equals(type)) { +					lastSig = i; +				} +			} + +			byte[] inputData = IOUtils.toByteArray(parameter.getDataSource().getInputStream()); + +			for (int i = 0; i < fields.size(); i++) { +				COSDictionary field = (COSDictionary) fields.getObject(i); +				String type = field.getNameAsString("FT"); +				if ("Sig".equals(type)) { +					boolean verifyThis = true; + +					if (signatureToVerify >= 0) { +						// verify only specific siganture! +						verifyThis = signatureToVerify == currentSignature; +					} + +					if (signatureToVerify == -2) { +						verifyThis = i == lastSig; +					} + +					if (verifyThis) { +						logger.trace("Found Signature: "); +						COSBase base = field.getDictionaryObject("V"); +						COSDictionary dict = (COSDictionary) base; + +						logger.debug("Signer: " + dict.getNameAsString("Name")); +						logger.debug("SubFilter: " + dict.getNameAsString("SubFilter")); +						logger.debug("Filter: " + dict.getNameAsString("Filter")); +						logger.debug("Modified: " + dict.getNameAsString("M")); +						COSArray byteRange = (COSArray) dict.getDictionaryObject("ByteRange"); + +						StringBuilder sb = new StringBuilder(); +						int[] bytes = new int[byteRange.size()]; +						for (int j = 0; j < byteRange.size(); j++) { +							bytes[j] = byteRange.getInt(j); +							sb.append(" " + bytes[j]); +						} + +						logger.debug("ByteRange" + sb.toString()); + +						COSString content = (COSString) dict.getDictionaryObject("Contents"); + +						ByteArrayOutputStream contentData = new ByteArrayOutputStream(); +						for (int j = 0; j < bytes.length; j = j + 2) { +							int offset = bytes[j]; +							int length = bytes[j + 1]; + +							contentData.write(inputData, offset, length); +						} +						contentData.close(); + +						IVerifyFilter verifyFilter = verifier.getVerifier(dict.getNameAsString("Filter"), +								dict.getNameAsString("SubFilter")); + +						IVerifier lvlVerifier = verifier.getVerifierByLevel(parameter.getSignatureVerificationLevel()); +						synchronized (lvlVerifier) { +							lvlVerifier.setConfiguration(parameter.getConfiguration()); +							if (verifyFilter != null) { +								List<VerifyResult> results = verifyFilter.verify(contentData.toByteArray(), +										content.getBytes(), parameter.getVerificationTime(), bytes, lvlVerifier); +								if (results != null && !results.isEmpty()) { +									result.addAll(results); +								} +							} +						} +					} +					currentSignature++; +				} +			} +			return result; +		} catch (IOException e) { +			logger.warn("Failed to verify document", e); +			throw ErrorExtractor.searchPdfAsError(e, null); +		} catch (PdfAsException e) { +			logger.warn("Failed to verify document", e); +			throw ErrorExtractor.searchPdfAsError(e, null); +		} finally { +			if (doc != null) { +				try { +					doc.close(); +				} catch (IOException e) { +					logger.info("Failed to close doc"); +				} +			} +		} +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/PDFPage.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/PDFPage.java new file mode 100644 index 00000000..a3e68c95 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/PDFPage.java @@ -0,0 +1,672 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + * + * $Id: PDFPage.java,v 1.5 2006/10/31 08:09:33 wprinz Exp $ + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf; + +import java.awt.Rectangle; +import java.awt.geom.GeneralPath; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.contentstream.operator.OperatorProcessor; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.apache.pdfbox.pdmodel.common.PDStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.graphics.PDXObject; +import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; +import org.apache.pdfbox.text.PDFTextStripper; +import org.apache.pdfbox.text.TextPosition; +import org.apache.pdfbox.util.Matrix; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.knowcenter.wag.egov.egiz.pdf.Pos; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction.ClosePath; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction.CurveTo; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction.CurveToReplicateFinalPoint; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction.CurveToReplicateInitialPoint; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction.LineTo; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction.MoveTo; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting.CloseAndStrokePath; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting.CloseFillEvenOddAndStrokePath; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting.CloseFillNonZeroAndStrokePath; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting.EndPath; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting.FillEvenOddAndStrokePath; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting.FillNonZeroAndStrokePath; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting.FillPathEvenOddRule; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting.FillPathNonZeroWindingNumberRule; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting.StrokePath; + +/** + * 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 at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage#showCharacter(TextPosition)}<br> + * This method is called when processing the FileStream. By calling the method + * {@link org.apache.pdfbox.util.PDFStreamEngine#processStream(org.apache.pdfbox.pdmodel.PDPage, org.apache.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 = LoggerFactory.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 effective page height. +	 */ +	protected float effectivePageHeight; + +	/** +	 * The path currently being constructed. +	 */ +	private GeneralPath currentPath = new GeneralPath(); + +	private boolean legacy40; +	 +	/** +	 * The lowest position of a drawn path (originating from top). +	 */ +	private float maxPathRelatedYPositionFromTop = Float.NEGATIVE_INFINITY; + +	/** +	 * Constructor. +	 *  +	 * @param effectivePageHeight +	 *            The height of the page to be evaluated. PDF elements outside +	 *            this height will not be considered. +	 *  +	 * @throws java.io.IOException +	 */ +	public PDFPage(float effectivePageHeight, boolean legacy32, boolean legacy40) +			throws IOException { +		super(); + +		this.legacy40 = legacy40; +		 +		this.effectivePageHeight = effectivePageHeight; + +		OperatorProcessor newInvoke = new MyInvoke(this); +		newInvoke.setContext(this); +		this.registerOperatorProcessor("Do", newInvoke); + +		if (!legacy32) { +			registerCustomPathOperators(); +		} +	} + +	/** +	 * Registers operators responsible for path construction and painting in +	 * order to fix auto positioning on pages with path elements. +	 *  +	 * @author Datentechnik Innovation GmbH +	 */ +	private void registerCustomPathOperators() { + +		// *** path construction +		this.registerOperatorProcessor("m", new MoveTo(this)); +		this.registerOperatorProcessor("l", new LineTo(this)); +		this.registerOperatorProcessor("c", new CurveTo(this)); +		this.registerOperatorProcessor("y", +				new CurveToReplicateFinalPoint(this)); +		this.registerOperatorProcessor("v", new CurveToReplicateInitialPoint( +				this)); +		this.registerOperatorProcessor("h", new ClosePath(this)); + +		// *** path painting + +		// "S": stroke path +		this.registerOperatorProcessor("S", new StrokePath(this)); +		this.registerOperatorProcessor("s", new CloseAndStrokePath(this)); +		this.registerOperatorProcessor("f", +				new FillPathNonZeroWindingNumberRule(this)); +		this.registerOperatorProcessor("F", +				new FillPathNonZeroWindingNumberRule(this)); +		this.registerOperatorProcessor("f*", new FillPathEvenOddRule(this)); +		this.registerOperatorProcessor("b", new CloseFillNonZeroAndStrokePath( +				this)); +		this.registerOperatorProcessor("B", new FillNonZeroAndStrokePath(this)); +		this.registerOperatorProcessor("b*", new CloseFillEvenOddAndStrokePath( +				this)); +		this.registerOperatorProcessor("B*", new FillEvenOddAndStrokePath(this)); +		this.registerOperatorProcessor("n", new EndPath(this)); + +		// Note: The graphic context +		// (org.pdfbox.pdmodel.graphics.PDGraphicsState) of the underlying +		// pdfbox library does +		// not yet support clipping. This prevents feasible usage of clipping +		// operators (W, W*). +		// operators.put("W", new ...(this)); +		// operators.put("W*", new ...(this)); + +	} + +	/** +	 * Returns the path currently being constructed. +	 *  +	 * @return The path currently being constructed. +	 */ +	public GeneralPath getCurrentPath() { +		return currentPath; +	} + +	/** +	 * Sets the current path. +	 *  +	 * @param currentPath +	 *            The new current path. +	 */ +	public void setCurrentPath(GeneralPath currentPath) { +		this.currentPath = currentPath; +	} + +	/** +	 * Registers a rectangle that bounds the path currently being drawn. +	 *  +	 * @param bounds +	 *            A rectangle depicting the bounds (coordinates originating from +	 *            bottom left). +	 * @author Datentechnik Innovation GmbH +	 */ +	public void registerPathBounds(Rectangle bounds) { +		if (!bounds.isEmpty()) { +			logger.debug("Registering path bounds: " + bounds); + +			// vertical start of rectangle (counting from top of page) +			float upperBoundYPositionFromTop; + +			// vertical end of rectangle (counting from top of page) +			// this depicts the current end of path-related page content +			float lowerBoundYPositionFromTop; + +			PDRectangle boundaryBox = this.getCurrentPage().getCropBox(); + +			if (boundaryBox == null) { +				boundaryBox = this.getCurrentPage().getMediaBox(); +			} + +			float pageHeight; + +			switch (this.getCurrentPage().getRotation()) { +			case 90: // CW +				pageHeight = boundaryBox.getWidth(); +				upperBoundYPositionFromTop = (float) bounds.getMinX(); +				lowerBoundYPositionFromTop = (float) bounds.getMaxX(); +				break; +			case 180: +				pageHeight = boundaryBox.getHeight(); +				upperBoundYPositionFromTop = (float) bounds.getMinY(); +				lowerBoundYPositionFromTop = (float) bounds.getMaxY(); +				break; +			case 270: // CCW +				pageHeight = boundaryBox.getWidth(); +				upperBoundYPositionFromTop = pageHeight +						- (float) bounds.getMaxX(); +				lowerBoundYPositionFromTop = pageHeight +						- (float) bounds.getMinX(); +				break; +			default: +				pageHeight = boundaryBox.getHeight(); +				upperBoundYPositionFromTop = pageHeight +						- (float) bounds.getMaxY(); +				lowerBoundYPositionFromTop = pageHeight +						- (float) bounds.getMinY(); +				break; +			} + +			// new maximum ? +			if (lowerBoundYPositionFromTop > maxPathRelatedYPositionFromTop) { +				// Is the rectangle (at least partly) located above the footer +				// line? +				// (effective page height := page height - footer line) +				if (upperBoundYPositionFromTop <= effectivePageHeight) { +					// yes: update current end of path-related page content +					maxPathRelatedYPositionFromTop = lowerBoundYPositionFromTop; +					logger.trace("New max path related y position (from top): " +							+ maxPathRelatedYPositionFromTop); +				} else { +					// no: rectangle is fully located below the footer line -> +					// ignore +					logger.trace("Ignoring path bound below the footer line."); +				} +			} +		} +	} + +	protected void processOperator(Operator operator, List<COSBase> arguments) +			throws IOException { +		logger.trace("operator = " + operator); +		super.processOperator(operator, arguments); +	} + +	@Override +	protected void processTextPosition(TextPosition text) { +		showCharacter(text); +	} + +	// exthex +	/** +	 * 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(); +		final String character = text.toString(); + +		if (at.gv.egiz.pdfas.common.utils.StringUtils.whiteSpaceTrim(character) +				.isEmpty()) { +			return; +		} + +		int pageRotation = this.getCurrentPage().getRotation(); +		// logger_.debug("PageRotation = " + pageRotation); +		/*if (pageRotation == 0) { +			current_y = text.getY(); +		} +		if (pageRotation == 90) { +			current_y = text.getY(); +		} +		if (pageRotation == 180) { +			current_y = text.getY(); +		} +		if (pageRotation == 270) { +			current_y = text.getY(); +		} +		 +		if (current_y > this.effectivePageHeight) { +			this.max_character_ypos = this.effectivePageHeight; +			return; +		} +		 +		// store ypos of the char if it is not empty +		if (current_y > this.max_character_ypos) { +			this.max_character_ypos = current_y; +		}*/ +		 +		if (pageRotation == 0) { +			current_y = text.getY(); +		} +		if (pageRotation == 90) { +			current_y = text.getX(); +		} +		if (pageRotation == 180) { +			float page_height = this.getCurrentPage().getMediaBox().getHeight(); +			current_y = page_height - text.getY(); +		} +		if (pageRotation == 270) { +			float page_height = this.getCurrentPage().getMediaBox().getHeight(); +			current_y = page_height - text.getX(); +		} + +		if (current_y > this.effectivePageHeight) { +			// logger_.debug("character is below footer_line. footer_line = " + +			// this.footer_line + ", text.character=" + character + ", y=" + +			// current_y); +			return; +		} + +		// store ypos of the char if it is not empty +		if (current_y > this.max_character_ypos) { +			this.max_character_ypos = 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() { +		if (logger.isDebugEnabled()) { +			logger.debug("Determining page content length: text=" +					+ max_character_ypos + ", image=" + max_image_ypos +					+ ", path=" + maxPathRelatedYPositionFromTop); +		} +		return NumberUtils.max(max_character_ypos, max_image_ypos, +				maxPathRelatedYPositionFromTop); +	} + +//	public Map<String, PDFont> getFonts() { //TODO: pdfbox2 - was @override +// +//		COSBase fontObj = null; +// +//		if (getCurrentPage().getResources() != null +//				&& getCurrentPage().getResources().getCOSObject() != null +//				&& getCurrentPage().getResources().getCOSObject() +//						.getDictionaryObject(COSName.FONT) != null) { +//			fontObj = getCurrentPage().getResources().getCOSObject() +//					.getDictionaryObject(COSName.FONT); +//		} +//		 +//		Map<String, PDFont> fontMap = new HashMap<String, PDFont>(); +//		 +//		Iterable<COSName> fontNames = getCurrentPage().getResources().getFontNames(); +//		for(COSName fn : fontNames){ +//			fontMap.put(fn.getName(), getCurrentPage().getResources().getFont(fn)); +//		} +//		 +//		if (fontObj != null) { +//			getCurrentPage().getResources().getCOSObject() +//					.setItem(COSName.FONT, fontObj); +//		} +//		 +//		return fontMap; +//	} + +	public class MyInvoke extends OperatorProcessor { + +		private PDFPage mypage; + +		public MyInvoke(PDFPage page) { +			this.mypage = page; +		} + +		public void process(Operator operator, List<COSBase> arguments) +				throws IOException { +			COSName name = (COSName) arguments.get(0); + +			// PDResources res = context.getResources(); + +			PDXObject xobject = context.getResources().getXObject(name); + +			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); + +				/********************************************************** +				 * pdf-as fix: calculating min and max point of an image to look +				 * where the signature should be placed fix solves problems with +				 * footer and images and placement of the signature in an image +				 * only pdf document +				 **********************************************************/ + +				float actual_lowest_point = Float.NaN; +				float actual_starting_point = Float.NaN; + +				int pageRotation = this.mypage.getCurrentPage().getRotation(); +				logger.debug("PageRotation = " + pageRotation); +				if (pageRotation == 0) { +					float min_y = findMinY(transformed_coordinates); +					logger.debug("min_y = " + min_y); +					float page_height = this.mypage.getCurrentPage() +							.getMediaBox().getHeight(); +					logger.debug("page_height = " + page_height); + +					actual_lowest_point = page_height - min_y; +					actual_starting_point = page_height +							- findMaxY(transformed_coordinates); +				} +				if (pageRotation == 90) { +					float max_x = findMaxX(transformed_coordinates); +					logger.debug("max_x = " + max_x); +					float page_width = this.mypage.getCurrentPage() +							.getMediaBox().getWidth(); +					logger.debug("page_width = " + page_width); + +					actual_lowest_point = max_x; +					actual_starting_point = findMinX(transformed_coordinates); +				} +				if (pageRotation == 180) { +					float min_y = findMinY(transformed_coordinates); +					logger.debug("min_y = " + min_y); +					actual_lowest_point = findMaxY(transformed_coordinates); +					actual_starting_point = actual_lowest_point + min_y; +				} +				if (pageRotation == 270) { +					float min_x = findMinX(transformed_coordinates); +					logger.debug("min_x = " + min_x); + +					float page_width = this.mypage.getCurrentPage() +							.getMediaBox().getWidth(); +					logger.debug("page_width = " + page_width); + +					actual_lowest_point = page_width - min_x; +					actual_starting_point = page_width +							- findMaxX(transformed_coordinates); +				} + +				logger.debug("actual_lowest_point = " + actual_lowest_point); + +				if (actual_lowest_point > PDFPage.this.effectivePageHeight +						&& actual_starting_point > PDFPage.this.effectivePageHeight) { +					logger.debug("image is below footer_line"); +					return; +				} + +				if (actual_lowest_point > PDFPage.this.max_image_ypos) { +					PDFPage.this.max_image_ypos = actual_lowest_point; +				} + +				return; +			} + +			if (xobject instanceof PDFormXObject) { +				PDFormXObject form = (PDFormXObject) xobject; +				COSStream invoke = (COSStream) form.getCOSObject(); +				PDResources pdResources = form.getResources(); +				PDPage page = context.getCurrentPage(); +				if (pdResources == null) { +					pdResources = page.getResources(); +				} + +				//getContext().processSubStream(page, pdResources, invoke);//TODO: pdfbox2 - correct? +				getContext().processPage(page); + +			} +		} + +		@Override +		public String getName() { +			// TODO Auto-generated method stub +			return null; +		} +	} + +	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 findMaxY(Pos[] coordinates) { +		float max = 0; +		for (int i = 0; i < coordinates.length; i++) { +			if (coordinates[i].y > max) { +				max = coordinates[i].y; +			} +		} +		return max; +	} + +	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; +	} + +	public void processAnnotation(PDAnnotation anno) { +		float current_y = anno.getRectangle().getLowerLeftY(); +		float upper_y = 0; +		PDPage page = anno.getPage(); + +		if (page == null) { +			page = getCurrentPage(); +		} + +		if (page == null) { +			logger.warn("Annotation without page! The position might not be correct!"); +			return; +		} + +		int pageRotation = page.getRotation(); +		// logger_.debug("PageRotation = " + pageRotation); +		if (pageRotation == 0) { +			float page_height = page.getMediaBox().getHeight(); +			current_y = page_height - anno.getRectangle().getLowerLeftY(); +			upper_y = page_height - anno.getRectangle().getUpperRightY(); +		} +		if (pageRotation == 90) { +			current_y = anno.getRectangle().getUpperRightX(); +			upper_y = anno.getRectangle().getLowerLeftX(); +		} +		if (pageRotation == 180) { +			current_y = anno.getRectangle().getUpperRightY(); +			upper_y = anno.getRectangle().getLowerLeftY(); +		} +		if (pageRotation == 270) { +			float page_width = page.getMediaBox().getWidth(); +			current_y = page_width - anno.getRectangle().getLowerLeftX(); +			upper_y = page_width - anno.getRectangle().getUpperRightX(); +		} + +		 +		 +		if (current_y > this.effectivePageHeight) { +			if(!this.legacy40 && upper_y < this.effectivePageHeight) { +				// Bottom of annotation is below footer line,  +				// but top of annotation is above footer line! +				// so no place left on this page! +				this.max_character_ypos = this.effectivePageHeight; +			} +			return; +		} + +		// store ypos of the char if it is not empty +		if (current_y > this.max_character_ypos) { +			this.max_character_ypos = current_y; +		} +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/PDFUtilities.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/PDFUtilities.java new file mode 100644 index 00000000..c1578120 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/PDFUtilities.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + * + * $Id: PDFUtilities.java,v 1.3 2006/10/31 08:09:33 wprinz Exp $ + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; + +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageTree; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation; + +import at.gv.egiz.pdfas.common.exceptions.PDFIOException; + +/** + * Contains useful helpers for accessing PDF documents. + * + * @author wprinz + * @author mruhmer + */ +public abstract class PDFUtilities { +	public static float calculatePageLength(PDDocument document, int page, +			float effectivePageHeight, /* int pagerotation, */boolean legacy32, boolean legacy40) +			throws PDFIOException { +		// int last_page_id = document.getNumberOfPages(); +		PDPageTree allPages = document.getDocumentCatalog().getPages(); +		PDPage pdpage = allPages.get(page); +		// pdpage.setRotation(pagerotation); +		return calculatePageLength(pdpage, effectivePageHeight, legacy32, legacy40); +	} + +	public static float calculatePageLength(PDPage page, +			float effectivePageHeight, boolean legacy32, boolean legacy40) throws PDFIOException { +		try { +			PDFPage my_page = new PDFPage(effectivePageHeight, legacy32, legacy40); +			//PDResources resources = page.getResources(); +			if (page.getContents() != null) { +				//COSStream stream = page.getContents(); +				// List<PDThreadBead> articles = page.getThreadBeads(); +				// my_page.processMyPage(page); +				//my_page.processStream(page, resources, stream); //TODO: pdfbox2 -> right? +				my_page.processPage(page); +			} +			if (!legacy32) { +				if (page.getAnnotations() != null) { +					Iterator<PDAnnotation> annotationsIt = page +							.getAnnotations().iterator(); + +					while (annotationsIt.hasNext()) { +						PDAnnotation annotation = annotationsIt.next(); +						if (!annotation.isInvisible()) { +							my_page.processAnnotation(annotation); +						} +					} +				} +			} +			return my_page.getMaxPageLength(); +		} catch (IOException e) { +			throw new PDFIOException("error.pdf.stamp.11", e); +		} +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/PathConstructionOperatorProcessor.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/PathConstructionOperatorProcessor.java new file mode 100644 index 00000000..ab273cd3 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/PathConstructionOperatorProcessor.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path; + +import java.awt.geom.Point2D; + +import org.apache.pdfbox.contentstream.operator.OperatorProcessor; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; + +/** + * Provides functions for path construction operators. + * + * @see "PDF 1.7 specification, Section 8.5.2 'Path Construction Operators'" + * @author Datentechnik Innovation GmbH + * + */ +public abstract class PathConstructionOperatorProcessor extends OperatorProcessor { + +	public PathConstructionOperatorProcessor(PDFPage context) { +		setContext(context); +	} + +	/** +	 * Transforms the given point from user space coordinates to device space coordinates based on the current +	 * transition matrix. +	 * +	 * @param x +	 *            The x axis value of the user space coordinates. +	 * @param y +	 *            The y axis value of the user space coordinates. +	 * @return The transformed point. +	 */ +	public Point2D transform(double x, double y) { +		double[] position = { x, y }; +		context.getGraphicsState().getCurrentTransformationMatrix().createAffineTransform() +				.transform(position, 0, position, 0, 1); +		return new Point2D.Double(position[0], position[1]); +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/PathPaintingOperatorProcessor.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/PathPaintingOperatorProcessor.java new file mode 100644 index 00000000..1d2cdd98 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/PathPaintingOperatorProcessor.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path; + +import org.apache.pdfbox.contentstream.operator.OperatorProcessor; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; + +/** + * Provides functions for path painting operators. + * + * @see "PDF 1.7 specification, Section 8.5.2 'Path Construction Operators'" + * @author Datentechnik Innovation GmbH + * + */ +public abstract class PathPaintingOperatorProcessor extends OperatorProcessor { + +	public PathPaintingOperatorProcessor(PDFPage context) { +		setContext(context); +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/ClosePath.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/ClosePath.java new file mode 100644 index 00000000..50b3b4d0 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/ClosePath.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction; + +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathConstructionOperatorProcessor; + +/** + * Close the current subpath by appending a straight line segment from the current point to the starting point of the + * subpath. If the current subpath is already closed, h shall donothing. This operator terminates the current subpath. + * Appending another segment to the current path shall begin a new subpath, even if the new segment begins at the + * endpoint reached by the h operation. + * + * @see "PDF 1.7 specification, Section 8.5.2 'Path Construction Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class ClosePath extends PathConstructionOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public ClosePath(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			pdfPage.getCurrentPath().closePath(); + +			if (log.isTraceEnabled()) { +				log.trace("Closing current path."); +			} +		} catch (Exception e) { +			log.warn("Error processing operator 'h'.", e); +		} +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/CurveTo.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/CurveTo.java new file mode 100644 index 00000000..da1288d3 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/CurveTo.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction; + +import java.awt.geom.Point2D; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathConstructionOperatorProcessor; + +/** + * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), + * using (x1, y1) and (x2, y2) as the Bezier control points (see 8.5.2.2, "Cubic Bezier Curves"). The new current point + * shall be (x3, y3). + * + * @see "PDF 1.7 specification, Section 8.5.2 'Path Construction Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class CurveTo extends PathConstructionOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public CurveTo(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			COSNumber x1 = (COSNumber) operands.get(0); +			COSNumber y1 = (COSNumber) operands.get(1); +			COSNumber x2 = (COSNumber) operands.get(2); +			COSNumber y2 = (COSNumber) operands.get(3); +			COSNumber x3 = (COSNumber) operands.get(4); +			COSNumber y3 = (COSNumber) operands.get(5); + +			Point2D p1 = transform(x1.doubleValue(), y1.doubleValue()); +			Point2D p2 = transform(x2.doubleValue(), y2.doubleValue()); +			Point2D p3 = transform(x3.doubleValue(), y3.doubleValue()); + +			pdfPage.getCurrentPath().curveTo( +					(float) p1.getX(), (float) p1.getY(), +					(float) p2.getX(), (float) p2.getY(), +					(float) p3.getX(), (float) p3.getY() +			); + +			if (log.isTraceEnabled()) { +				log.trace("Appending cubic Bezier curve with x1:" + p1.getX() + ",y1:" + p1.getY() + ", x2:" +						+ p2.getX() + ",y2:" + p2.getY() + ", x3:" + p3.getX() + ",y3:" + p3.getY()); +			} +		} catch (Exception e) { +			log.warn("Error processing operator 'c'.", e); +		} +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/CurveToReplicateFinalPoint.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/CurveToReplicateFinalPoint.java new file mode 100644 index 00000000..458e8b3e --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/CurveToReplicateFinalPoint.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction; + +import java.awt.geom.Point2D; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathConstructionOperatorProcessor; + +/** + * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), + * using (x1, y1) and (x3, y3) as the Bezier control points (see 8.5.2.2, "Cubic Bezier Curves"). The new current point + * shall be (x3, y3). + * + * @see "PDF 1.7 specification, Section 8.5.2 'Path Construction Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class CurveToReplicateFinalPoint extends PathConstructionOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public CurveToReplicateFinalPoint(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			COSNumber x1 = (COSNumber) operands.get(0); +			COSNumber y1 = (COSNumber) operands.get(1); +			COSNumber x3 = (COSNumber) operands.get(2); +			COSNumber y3 = (COSNumber) operands.get(3); + +			Point2D p1 = transform(x1.doubleValue(), y1.doubleValue()); +			Point2D p3 = transform(x3.doubleValue(), y3.doubleValue()); + +			pdfPage.getCurrentPath().curveTo( +					(float) p1.getX(), (float) p1.getY(), +					(float) p3.getX(), (float) p3.getY(), +					(float) p3.getX(), (float) p3.getY() +			); + +			if (log.isTraceEnabled()) { +				log.trace("Appending cubic Bezier curve with x1:" + p1.getX() + ",y1:" + p1.getY() + ", x3:" +						+ p3.getX() + ",y3:" + p3.getY()); +			} +		} catch (Exception e) { +			log.warn("Error processing operator 'y'.", e); +		} +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/CurveToReplicateInitialPoint.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/CurveToReplicateInitialPoint.java new file mode 100644 index 00000000..d3a4e5e3 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/CurveToReplicateInitialPoint.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction; + +import java.awt.geom.Point2D; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathConstructionOperatorProcessor; + +/** + * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), + * using the current point and (x2, y2) as the Bezier control points (see 8.5.2.2, "Cubic Bezier Curves"). The new + * current point shall be (x3, y3). + * + * @see "PDF 1.7 specification, Section 8.5.2 'Path Construction Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class CurveToReplicateInitialPoint extends PathConstructionOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public CurveToReplicateInitialPoint(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			COSNumber x2 = (COSNumber) operands.get(0); +			COSNumber y2 = (COSNumber) operands.get(1); +			COSNumber x3 = (COSNumber) operands.get(2); +			COSNumber y3 = (COSNumber) operands.get(3); + +	        Point2D currentPoint = pdfPage.getCurrentPath().getCurrentPoint(); +			Point2D p2 = transform(x2.doubleValue(), y2.doubleValue()); +			Point2D p3 = transform(x3.doubleValue(), y3.doubleValue()); + +			pdfPage.getCurrentPath().curveTo( +					(float)currentPoint.getX(), (float)currentPoint.getY(), +					(float) p2.getX(), (float) p2.getY(), +					(float) p3.getX(), (float) p3.getY() +			); + +			if (log.isTraceEnabled()) { +				log.trace("Appending cubic Bezier curve with x2:" + p2.getX() + ",y2:" + p2.getY() + ", x3:" +						+ p3.getX() + ",y3:" + p3.getY() + ", using current point x:" + currentPoint.getX() + ",y:" +						+ currentPoint.getY()); +			} +		} catch (Exception e) { +			log.warn("Error processing operator 'v'.", e); +		} +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/LineTo.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/LineTo.java new file mode 100644 index 00000000..a3bee751 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/LineTo.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction; + +import java.awt.geom.Point2D; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathConstructionOperatorProcessor; + +/** + * Append a straight line segment from the current point to the point (x, y). The new current point shall be (x, y). + * + * @see "PDF 1.7 specification, Section 8.5.2 'Path Construction Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class LineTo extends PathConstructionOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public LineTo(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			COSNumber x = (COSNumber) operands.get(0); +			COSNumber y = (COSNumber) operands.get(1); +			Point2D p = transform(x.doubleValue(), y.doubleValue()); + +			pdfPage.getCurrentPath().lineTo((float) p.getX(), (float) p.getY()); + +			if (log.isTraceEnabled()) { +				log.trace("Adding line to x:" + p.getX() + ",y:" + p.getY()); +			} +		} catch (Exception e) { +			log.warn("Error processing operator 'l'.", e); +		} +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/MoveTo.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/MoveTo.java new file mode 100644 index 00000000..624405f8 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/construction/MoveTo.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.construction; + +import java.awt.geom.Point2D; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSNumber; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathConstructionOperatorProcessor; + +/** + * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment. If the + * previous path construction operator in the current path was also m, the new m overrides it; no vestige of the + * previous m operation remains in the path. + * + * @see "PDF 1.7 specification, Section 8.5.2 'Path Construction Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class MoveTo  extends PathConstructionOperatorProcessor{ + +	public MoveTo(PDFPage context) { +		super(context); +		// TODO Auto-generated constructor stub +	} + +	private Log log = LogFactory.getLog(getClass()); + +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			COSNumber x = (COSNumber) operands.get(0); +			COSNumber y = (COSNumber) operands.get(1); +			Point2D p = transform(x.doubleValue(), y.doubleValue()); + +			pdfPage.getCurrentPath().moveTo((float) p.getX(), (float) p.getY()); + +			if (log.isTraceEnabled()) { +				log.trace("Moving current path to x:" + p.getX() + ",y:" + p.getY()); +			} +		} catch (Exception e) { +			log.warn("Error processing operator 'm'.", e); +		} +	} + + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/CloseAndStrokePath.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/CloseAndStrokePath.java new file mode 100644 index 00000000..488819ac --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/CloseAndStrokePath.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting; + +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathPaintingOperatorProcessor; + +/** + * Close and stroke the path. This operator shall have the same effect as the sequence <code>h S</code>. + * + * @see "PDF 1.7 specification, Section 8.5.3 'Path-Painting Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class CloseAndStrokePath extends PathPaintingOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public CloseAndStrokePath(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		if (log.isTraceEnabled()) { +			log.trace("Closing and stroking path."); +		} +		context.processOperator("h", operands); +		context.processOperator("S", operands); +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/CloseFillEvenOddAndStrokePath.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/CloseFillEvenOddAndStrokePath.java new file mode 100644 index 00000000..41078e40 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/CloseFillEvenOddAndStrokePath.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting; + +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathPaintingOperatorProcessor; + +/** + * Close, fill, and then stroke the path, using the even-odd rule to determine the region to fill. This operator shall + * have the same effect as the sequence <code>h B*</code>. + * + * @see "PDF 1.7 specification, Section 8.5.3 'Path-Painting Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class CloseFillEvenOddAndStrokePath extends PathPaintingOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public CloseFillEvenOddAndStrokePath(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		if (log.isTraceEnabled()) { +			log.trace("Closing, filling (even odd rule) and stroking path."); +		} +		context.processOperator("h", operands); +		context.processOperator("B*", operands); +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/CloseFillNonZeroAndStrokePath.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/CloseFillNonZeroAndStrokePath.java new file mode 100644 index 00000000..ef297ca9 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/CloseFillNonZeroAndStrokePath.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting; + +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathPaintingOperatorProcessor; + +/** + * Close, fill, and then stroke the path, using the nonzero winding number rule to determine the region to fill. This + * operator shall have the same effect as the sequence <code>h B</code>. + * + * @see "PDF 1.7 specification, Section 8.5.3 'Path-Painting Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class CloseFillNonZeroAndStrokePath extends PathPaintingOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public CloseFillNonZeroAndStrokePath(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		if (log.isTraceEnabled()) { +			log.trace("Closing, filling (non zero rule) and stroking path."); +		} +		context.processOperator("h", operands); +		context.processOperator("B", operands); +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/EndPath.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/EndPath.java new file mode 100644 index 00000000..0001d7e5 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/EndPath.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting; + +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathPaintingOperatorProcessor; + +/** + * End the path object without filling or stroking it. This operator shall be a path-painting no-op, used primarily for + * the side effect of changing the current clipping path. + * + * @see "PDF 1.7 specification, Section 8.5.3 'Path-Painting Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class EndPath extends PathPaintingOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public EndPath(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			log.debug("Ending path " + pdfPage.getCurrentPath()); +			pdfPage.getCurrentPath().reset(); + +			if (log.isTraceEnabled()) { +				log.trace("End path without filling or stroking."); +			} + +		} catch (Exception e) { +			log.warn("Error processing operator 'n'.", e); +		} +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillEvenOddAndStrokePath.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillEvenOddAndStrokePath.java new file mode 100644 index 00000000..12a4b037 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillEvenOddAndStrokePath.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting; + +import java.awt.geom.GeneralPath; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathPaintingOperatorProcessor; + +/** + * Fill and then stroke the path, using the even-odd rule to determine the region to fill. This operator shall produce + * the same result as <code>B</code>, except that the path is filled as if with <code>f*</code> instead of + * <code>f</code>. + * + * @see "PDF 1.7 specification, Section 8.5.3 'Path-Painting Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class FillEvenOddAndStrokePath extends PathPaintingOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public FillEvenOddAndStrokePath(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			if (log.isTraceEnabled()) { +				log.trace("Filling (even odd rule) and stroking path."); +			} + +			GeneralPath currentPath = (GeneralPath) pdfPage.getCurrentPath().clone(); +			context.processOperator("f*", operands); +			pdfPage.setCurrentPath(currentPath); +			context.processOperator("S", operands); + +		} catch (Exception e) { +			log.warn("Error processing operator 'B*'.", e); +		} +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillNonZeroAndStrokePath.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillNonZeroAndStrokePath.java new file mode 100644 index 00000000..a6fca720 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillNonZeroAndStrokePath.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting; + +import java.awt.geom.GeneralPath; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathPaintingOperatorProcessor; + +/** + * Fill and then stroke the path, using the nonzero winding number rule to determine the region to fill. This operator + * shall produce the same result as constructing two identical path objects, painting the first with <code>f</code> and + * the second with <code>S</code>. + * + * @see "PDF 1.7 specification, Section 8.5.3 'Path-Painting Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class FillNonZeroAndStrokePath extends PathPaintingOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public FillNonZeroAndStrokePath(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			if (log.isTraceEnabled()) { +				log.trace("Filling (non zero rule) and stroking path."); +			} + +			GeneralPath currentPath = (GeneralPath) pdfPage.getCurrentPath().clone(); +			context.processOperator("f", operands); +			pdfPage.setCurrentPath(currentPath); +			context.processOperator("S", operands); + +		} catch (Exception e) { +			log.warn("Error processing operator 'B'.", e); +		} +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillPathEvenOddRule.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillPathEvenOddRule.java new file mode 100644 index 00000000..10eeae03 --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillPathEvenOddRule.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting; + +import java.awt.Rectangle; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathPaintingOperatorProcessor; + +/** + * Fill the path, using the even-odd rule to determine the region to fill. + * + * @see "PDF 1.7 specification, Section 8.5.3 'Path-Painting Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class FillPathEvenOddRule extends PathPaintingOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public FillPathEvenOddRule(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			pdfPage.getCurrentPath().setWindingRule(java.awt.geom.GeneralPath.WIND_EVEN_ODD); +			Rectangle bounds = pdfPage.getCurrentPath().getBounds(); +			pdfPage.getCurrentPath().reset(); + +			if (log.isTraceEnabled()) { +				log.trace("Filling path, using even-odd rule."); +			} + +			pdfPage.registerPathBounds(bounds); + +		} catch (Exception e) { +			log.warn("Error processing operator 'f*'.", e); +		} +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillPathNonZeroWindingNumberRule.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillPathNonZeroWindingNumberRule.java new file mode 100644 index 00000000..ca8922fc --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/FillPathNonZeroWindingNumberRule.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting; + +import java.awt.Rectangle; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathPaintingOperatorProcessor; + +/** + * Fill the path, using the nonzero winding number rule to determine the region to fill. Any subpaths that are open + * shall be implicitly closed before being filled. + * + * @see "PDF 1.7 specification, Section 8.5.3 'Path-Painting Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class FillPathNonZeroWindingNumberRule extends PathPaintingOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public FillPathNonZeroWindingNumberRule(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			pdfPage.getCurrentPath().setWindingRule(java.awt.geom.GeneralPath.WIND_NON_ZERO); +			Rectangle bounds = pdfPage.getCurrentPath().getBounds(); +			pdfPage.getCurrentPath().reset(); + +			if (log.isTraceEnabled()) { +				log.trace("Filling path, using nonzero winding number rule."); +			} + +			pdfPage.registerPathBounds(bounds); + +		} catch (Exception e) { +			log.warn("Error processing operator 'f/F'.", e); +		} +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/StrokePath.java b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/StrokePath.java new file mode 100644 index 00000000..3dc0965e --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/java/at/knowcenter/wag/egov/egiz/pdfbox2/pdf/operator/path/painting/StrokePath.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + *  + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + *  + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + *  + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a + * joint initiative of the Federal Chancellery Austria and Graz University of + * Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.painting; + +import java.awt.Rectangle; +import java.io.IOException; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.contentstream.operator.Operator; +import org.apache.pdfbox.cos.COSBase; + +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFPage; +import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.operator.path.PathPaintingOperatorProcessor; + +/** + * Strokes the path. + * + * @see "PDF 1.7 specification, Section 8.5.3 'Path-Painting Operators'" + * @author PdfBox, modified by Datentechnik Innovation GmbH + */ +public class StrokePath extends PathPaintingOperatorProcessor { + +	private Log log = LogFactory.getLog(getClass()); + +	public StrokePath(PDFPage context) { +		super(context); +	} + +	@Override +	public void process(Operator operator, List<COSBase> operands) throws IOException { +		try { +			PDFPage pdfPage = (PDFPage) context; + +			Rectangle bounds = pdfPage.getCurrentPath().getBounds(); +			pdfPage.getCurrentPath().reset(); + +			if (log.isTraceEnabled()) { +				log.trace("Stroking path."); +			} + +			pdfPage.registerPathBounds(bounds); + +		} catch (Exception e) { +			log.warn("Error processing operator 'S'.", e); +		} +	} + +	@Override +	public String getName() { +		// TODO Auto-generated method stub +		return null; +	} + +} diff --git a/pdf-as-pdfbox-2/src/main/resources/META-INF/services/at.gv.egiz.pdfas.lib.backend.PDFASBackend b/pdf-as-pdfbox-2/src/main/resources/META-INF/services/at.gv.egiz.pdfas.lib.backend.PDFASBackend new file mode 100644 index 00000000..130d219c --- /dev/null +++ b/pdf-as-pdfbox-2/src/main/resources/META-INF/services/at.gv.egiz.pdfas.lib.backend.PDFASBackend @@ -0,0 +1 @@ +at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXBackend
\ No newline at end of file diff --git a/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/TTFFontTest.java b/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/TTFFontTest.java new file mode 100644 index 00000000..ca45354b --- /dev/null +++ b/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/TTFFontTest.java @@ -0,0 +1,45 @@ +package at.gv.egiz.pdfas.lib.testpdfbox; + +import java.io.File; +import java.util.Iterator; +import java.util.List; + +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.pdmodel.PDDocument; + +public class TTFFontTest { + +	public static void main(String[] args) { +		try { +			PDDocument doc = PDDocument.load(new File("/home/afitzek/Downloads/pdf_groesse/willenserklaerung_signedByUser.pdf")); +		 +			List<COSObject> cosObjects = doc.getDocument().getObjectsByType(COSName.FONT); +		 +			Iterator<COSObject> cosObjectIt = cosObjects.iterator(); +			 +			while(cosObjectIt.hasNext()) { +				COSObject cosObject = cosObjectIt.next(); +				COSBase subType = cosObject.getItem(COSName.SUBTYPE); +				COSBase baseFont = cosObject.getItem(COSName.BASE_FONT); +				COSBase aTest = cosObject.getItem(COSName.A); +				 +				System.out.println(aTest); +				 +				if(subType.equals(COSName.TRUE_TYPE)) { +					System.out.println("Object Number: " + cosObject.getObjectNumber() +  +							subType.toString()); +					System.out.println("    BaseFont: " + baseFont.toString()); +				} +				 +				 +			} +			 +			 +		} catch(Throwable e) { +			e.printStackTrace(); +		} +	} + +} | 
