From 6bf2c6651e2bbca2cb7b5680fa7eb2fea356abf7 Mon Sep 17 00:00:00 2001
From: Andreas Fitzek <andreas.fitzek@iaik.tugraz.at>
Date: Tue, 24 Feb 2015 11:23:31 +0100
Subject: pdfbox TTF font fixed re embedding inside same signature

---
 .../lib/impl/stamping/pdfbox/FontInfoCache.java    |   7 +
 .../pdfas/lib/impl/stamping/pdfbox/PDFBoxFont.java | 227 ++++++++++++++++-----
 .../lib/impl/stamping/pdfbox/PDFBoxTable.java      |  27 ++-
 .../lib/impl/stamping/pdfbox/PdfBoxStamper.java    |   4 +-
 .../impl/stamping/pdfbox/PdfBoxVisualObject.java   |   9 +-
 5 files changed, 207 insertions(+), 67 deletions(-)
 create mode 100644 pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/FontInfoCache.java

(limited to 'pdf-as-pdfbox/src/main/java')

diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/FontInfoCache.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/FontInfoCache.java
new file mode 100644
index 00000000..10a5c9f8
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/FontInfoCache.java
@@ -0,0 +1,7 @@
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox;
+
+public class FontInfoCache {
+	String filename;
+	String fontName;
+	String fontFamily;
+}
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxFont.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxFont.java
index 8fcca9b7..fdbf40e2 100644
--- a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxFont.java
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxFont.java
@@ -24,11 +24,22 @@
 package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox;
 
 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.PDDocument;
 import org.apache.pdfbox.pdmodel.font.PDFont;
 import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont;
@@ -39,10 +50,10 @@ import org.slf4j.LoggerFactory;
 import at.gv.egiz.pdfas.common.settings.ISettings;
 
 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";
@@ -50,32 +61,34 @@ public class PDFBoxFont {
 	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);
+		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()) {
+		while (it.hasNext()) {
 			logger.info(it.next());
 		}
 	}
-	
+
 	PDFont font;
 	PDFont cachedfont = null;
 	float fontSize;
@@ -83,11 +96,97 @@ public class PDFBoxFont {
 	String ttfFontDesc;
 	PDDocument doc;
 	ISettings settings;
-	
-	private PDFont generateTTF(String fonttype, PDDocument doc) throws IOException {
+
+	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.parseTTF(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;
+					fontInfoCache.put(pathName, fontInfo);
+					return fontInfo;
+				} catch (Throwable e) {
+					logger.warn("Failed to generate FontInfo from file: {}", pathName);
+				}
+				return null;
+			}
+		}
+	}
+
+	private PDFont findCachedFont(PDDocument doc, FontInfoCache fontInfo) {
+		try {
+			List<COSObject> cosObjects = doc.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, PDDocument doc)
+			throws IOException {
 		boolean cacheNow = false;
-		if(doc == null) {
-			if(this.doc == null) {
+		if (doc == null) {
+			if (this.doc == null) {
 				this.doc = new PDDocument();
 			}
 			doc = this.doc;
@@ -96,73 +195,97 @@ public class PDFBoxFont {
 		}
 		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(doc, fontInfo);
+
+			if (font != null) {
+				return font;
+			}
+		} 
 		
 		logger.debug("Instantiating font.");
-        String fontPath = this.settings.getWorkingDirectory()  + File.separator + "fonts" + File.separator + fontName;
-        logger.debug("Instantiating \"" + fontPath + "\".");
-
-        if(cacheNow) {
-        	cachedfont = PDTrueTypeFont.loadTTF(doc, fontPath);
-        	return cachedfont;
-        } else {
-        	return PDTrueTypeFont.loadTTF(doc, fontPath);
-        }
+		
+		//if (cacheNow) {
+			cachedfont = PDTrueTypeFont.loadTTF(doc, fontPath);
+			fontStyleMap.put(fontPath, cachedfont);
+			return cachedfont;
+		//} else {
+		//	return PDTrueTypeFont.loadTTF(doc, fontPath);
+		//}
+
 	}
-	
-	private PDFont generateFont(String fonttype, String fontder) throws IOException {
-		if(fonttype.startsWith("TTF:")) {
+
+	private PDFont generateFont(String fonttype, String fontder,
+			PDDocument originalDoc) throws IOException {
+		if (fonttype.startsWith("TTF:")) {
 			// Load TTF Font
-			return generateTTF(fonttype, null);
+			return generateTTF(fonttype, originalDoc);
 		} else {
-			if(fontder == null) {
+			if (fontder == null) {
 				fontder = NORMAL;
 			}
-			
+
 			String fontDesc = fonttype + SEP + fontder;
 			PDFont font = fontStyleMap.get(fontDesc);
-			if(font == null) {
+			if (font == null) {
 				showBuildinFonts();
 				throw new IOException("Invalid font descriptor");
 			}
 			return font;
 		}
 	}
-	
-	private void setFont(String desc) throws IOException {
+
+	private void setFont(String desc, PDDocument originalDoc)
+			throws IOException {
 		String[] fontArr = desc.split(",");
-		
-		if(fontArr.length == 3) {
-			font = generateFont(fontArr[0], fontArr[2]);
+
+		if (fontArr.length == 3) {
+			font = generateFont(fontArr[0], fontArr[2], originalDoc);
 			fontSize = Float.parseFloat(fontArr[1]);
-		} else if(fontArr.length == 2 && fontArr[0].startsWith("TTF:")) {
-			font = generateFont(fontArr[0], null);
+		} else if (fontArr.length == 2 && fontArr[0].startsWith("TTF:")) {
+			font = generateFont(fontArr[0], null, originalDoc);
 			fontSize = Float.parseFloat(fontArr[1]);
 		} else {
-			logger.warn("Using default font because: {} is not a valid font descriptor.", desc);
+			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) throws IOException {
+	public PDFBoxFont(String fontDesc, ISettings settings,
+			PDDocument originalDoc) throws IOException {
 		this.settings = settings;
 		this.fontDesc = fontDesc;
 		logger.debug("Creating Font: " + fontDesc);
-		this.setFont(fontDesc);
+		this.setFont(fontDesc, originalDoc);
 	}
-	
+
 	public PDFont getFont(PDDocument doc) throws IOException {
-		if(cachedfont != null) {
+		if (cachedfont != null) {
 			return cachedfont;
 		}
-		if(font instanceof PDTrueTypeFont && doc != null) {
+		if (font instanceof PDTrueTypeFont && doc != null) {
 			return generateTTF(ttfFontDesc, doc);
 		} else {
 			return font;
 		}
 	}
-	
+
 	public float getFontSize() {
 		return fontSize;
 	}
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java
index 2fec7083..22947643 100644
--- a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java
@@ -32,6 +32,7 @@ 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;
@@ -67,6 +68,8 @@ public class PDFBoxTable {
 	float[] rowHeights;
 	float[] colWidths;
 
+	PDDocument originalDoc;
+	
 	private void normalizeContent(Table abstractTable) throws PdfAsException {
 		try {
 			int rows = abstractTable.getRows().size();
@@ -90,7 +93,7 @@ public class PDFBoxTable {
 		}
 	}
 
-	private void initializeStyle(Table abstractTable, PDFBoxTable parent)
+	private void initializeStyle(Table abstractTable, PDFBoxTable parent, PDDocument originalDoc)
 			throws IOException {
 		this.table = abstractTable;
 		try {
@@ -127,7 +130,7 @@ public class PDFBoxTable {
 								+ abstractTable.getName());
 			}
 
-			font = new PDFBoxFont(fontString, settings);
+			font = new PDFBoxFont(fontString, settings, originalDoc);
 
 			if (vfontString == null && parent != null && parent.style != null) {
 				vfontString = parent.style.getValueFont();
@@ -137,7 +140,7 @@ public class PDFBoxTable {
 								+ abstractTable.getName());
 			}
 
-			valueFont = new PDFBoxFont(vfontString, settings);
+			valueFont = new PDFBoxFont(vfontString, settings, originalDoc);
 		}
 		padding = style.getPadding();
 
@@ -145,9 +148,10 @@ public class PDFBoxTable {
 	}
 
 	public PDFBoxTable(Table abstractTable, PDFBoxTable parent, float fixSize,
-			ISettings settings) throws IOException, PdfAsException {
+			ISettings settings, PDDocument originalDoc) throws IOException, PdfAsException {
 		this.settings = settings;
-		initializeStyle(abstractTable, parent);
+		this.originalDoc = originalDoc;
+		initializeStyle(abstractTable, parent, originalDoc);
 		float[] relativSizes = abstractTable.getColsRelativeWith();
 		if (relativSizes != null) {
 			colWidths = new float[relativSizes.length];
@@ -178,9 +182,10 @@ public class PDFBoxTable {
 	}
 
 	public PDFBoxTable(Table abstractTable, PDFBoxTable parent,
-			ISettings settings) throws IOException, PdfAsException {
+			ISettings settings, PDDocument originalDoc) throws IOException, PdfAsException {
 		this.settings = settings;
-		initializeStyle(abstractTable, parent);
+		this.originalDoc = originalDoc;
+		initializeStyle(abstractTable, parent, originalDoc);
 		this.calculateWidthHeight();
 	}
 
@@ -342,7 +347,7 @@ public class PDFBoxTable {
 			PDFBoxTable pdfBoxTable = null;
 			if (cell.getValue() instanceof Table) {
 				pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this,
-						this.settings);
+						this.settings, originalDoc);
 				cell.setValue(pdfBoxTable);
 			} else if (cell.getValue() instanceof PDFBoxTable) {
 				pdfBoxTable = (PDFBoxTable) cell.getValue();
@@ -550,13 +555,13 @@ public class PDFBoxTable {
 			PDFBoxTable pdfBoxTable = null;
 			if (cell.getValue() instanceof Table) {
 				pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this,
-						width, this.settings);
+						width, this.settings, this.originalDoc);
 				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.settings, this.originalDoc);
 				cell.setValue(pdfBoxTable);
 			} else {
 				throw new IOException("Failed to build PDFBox Table");
@@ -614,7 +619,7 @@ public class PDFBoxTable {
 			PDFBoxTable pdfBoxTable = null;
 			if (cell.getValue() instanceof Table) {
 				pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this,
-						this.settings);
+						this.settings, originalDoc);
 				cell.setValue(pdfBoxTable);
 			} else if (cell.getValue() instanceof PDFBoxTable) {
 				pdfBoxTable = (PDFBoxTable) cell.getValue();
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxStamper.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxStamper.java
index 5c190883..3dd1f0a4 100644
--- a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxStamper.java
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxStamper.java
@@ -28,6 +28,7 @@ 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.pdfbox.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;
@@ -46,7 +47,8 @@ public class PdfBoxStamper implements IPDFStamper {
 	
 	public IPDFVisualObject createVisualPDFObject(PDFObject pdf, Table table) throws IOException {
 		try {
-			return new PdfBoxVisualObject(table, pdf.getStatus().getSettings());
+			PDFBOXObject pdfboxObject = (PDFBOXObject)pdf;
+			return new PdfBoxVisualObject(table, pdf.getStatus().getSettings(), pdfboxObject.getDocument());
 		} catch (PdfAsException e) {
 			throw new PdfAsWrappedIOException(e);
 		}
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxVisualObject.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxVisualObject.java
index ca363a8e..9fd8ed84 100644
--- a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxVisualObject.java
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxVisualObject.java
@@ -25,6 +25,7 @@ package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox;
 
 import java.io.IOException;
 
+import org.apache.pdfbox.pdmodel.PDDocument;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,11 +46,13 @@ public class PdfBoxVisualObject implements IPDFVisualObject {
 	private float y;
 	private int page;
 	private ISettings settings;
+	private PDDocument originalDoc;
 
-	public PdfBoxVisualObject(Table table, ISettings settings)
+	public PdfBoxVisualObject(Table table, ISettings settings, PDDocument originalDoc)
 			throws IOException, PdfAsException {
 		this.abstractTable = table;
-		this.table = new PDFBoxTable(table, null, settings);
+		this.originalDoc = originalDoc;
+		this.table = new PDFBoxTable(table, null, settings, originalDoc);
 		this.settings = settings;
 	}
 
@@ -59,7 +62,7 @@ public class PdfBoxVisualObject implements IPDFVisualObject {
 
 	public void fixWidth() {
 		try {
-			table = new PDFBoxTable(abstractTable, null, this.width,  settings);
+			table = new PDFBoxTable(abstractTable, null, this.width,  settings, this.originalDoc);
 		} catch (IOException e) {
 			logger.warn("Failed to fix width of Table!", e);
 		} catch (PdfAsException e) {
-- 
cgit v1.2.3