From 6bf2c6651e2bbca2cb7b5680fa7eb2fea356abf7 Mon Sep 17 00:00:00 2001 From: Andreas Fitzek 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') 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 fontStyleMap = new HashMap(); - + + private static Map fontInfoCache = new HashMap(); + 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 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 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 cosObjects = doc.getDocument().getObjectsByType( + COSName.FONT); + + //COSName cosFontName = COSName.getPDFName(fontInfo.fontName); + //COSName cosFontFamily = COSName.getPDFName(fontInfo.fontFamily); + + Iterator 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