You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ti...@apache.org on 2018/02/17 19:32:26 UTC
svn commit: r1824629 - in /pdfbox/branches/2.0/pdfbox/src:
main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Embedder.java
test/java/org/apache/pdfbox/pdmodel/font/TestFontEmbedding.java
Author: tilman
Date: Sat Feb 17 19:32:26 2018
New Revision: 1824629
URL: http://svn.apache.org/viewvc?rev=1824629&view=rev
Log:
PDFBOX-4106: Correct vmtx embedding for proportional fonts and add test for it, by Aaron Madlon-Kay
Modified:
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Embedder.java
pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/TestFontEmbedding.java
Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Embedder.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Embedder.java?rev=1824629&r1=1824628&r2=1824629&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Embedder.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDCIDFontType2Embedder.java Sat Feb 17 19:32:26 2018
@@ -30,8 +30,12 @@ import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.fontbox.ttf.GlyphData;
+import org.apache.fontbox.ttf.GlyphTable;
+import org.apache.fontbox.ttf.HorizontalMetricsTable;
import org.apache.fontbox.ttf.TrueTypeFont;
import org.apache.fontbox.ttf.VerticalHeaderTable;
+import org.apache.fontbox.ttf.VerticalMetricsTable;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSInteger;
@@ -349,7 +353,13 @@ final class PDCIDFontType2Embedder exten
float scaling = 1000f / ttf.getHeader().getUnitsPerEm();
- long w1 = Math.round(-ttf.getVerticalHeader().getAdvanceHeightMax() * scaling);
+ VerticalHeaderTable vhea = ttf.getVerticalHeader();
+ VerticalMetricsTable vmtx = ttf.getVerticalMetrics();
+ GlyphTable glyf = ttf.getGlyph();
+ HorizontalMetricsTable hmtx = ttf.getHorizontalMetrics();
+
+ long v_y = Math.round(vhea.getAscender() * scaling);
+ long w1 = Math.round(-vhea.getAdvanceHeightMax() * scaling);
COSArray heights = new COSArray();
COSArray w2 = new COSArray();
@@ -360,10 +370,16 @@ final class PDCIDFontType2Embedder exten
{
// Unlike buildWidths, we look up with cid (not gid) here because this is
// the original TTF, not the rebuilt one.
- long height = Math.round(-ttf.getVerticalMetrics().getAdvanceHeight(cid) * scaling);
- if (height == w1)
+ GlyphData glyph = glyf.getGlyph(cid);
+ if (glyph == null)
+ {
+ continue;
+ }
+ long height = Math.round((glyph.getYMaximum() + vmtx.getTopSideBearing(cid)) * scaling);
+ long advance = Math.round(-vmtx.getAdvanceHeight(cid) * scaling);
+ if (height == v_y && advance == w1)
{
- // skip default height
+ // skip default metrics
continue;
}
// c [w1_1y v_1x v_1y w1_2y v_2x v_2y ... w1_ny v_nx v_ny]
@@ -373,10 +389,10 @@ final class PDCIDFontType2Embedder exten
heights.add(COSInteger.get(cid)); // c
heights.add(w2);
}
- w2.add(COSInteger.get(height)); // w1_iy
- long width = Math.round(ttf.getHorizontalMetrics().getAdvanceWidth(cid) * scaling);
+ w2.add(COSInteger.get(advance)); // w1_iy
+ long width = Math.round(hmtx.getAdvanceWidth(cid) * scaling);
w2.add(COSInteger.get(width / 2)); // v_ix
- w2.add(COSInteger.get(w1)); // v_iy
+ w2.add(COSInteger.get(height)); // v_iy
prev = cid;
}
cidFont.setItem(COSName.W2, heights);
@@ -510,12 +526,18 @@ final class PDCIDFontType2Embedder exten
}
int cidMax = ttf.getNumberOfGlyphs();
- int[] gidMetrics = new int[cidMax * 3];
+ int[] gidMetrics = new int[cidMax * 4];
for (int cid = 0; cid < cidMax; cid++)
{
- gidMetrics[cid * 3] = cid;
- gidMetrics[cid * 3 + 1] = ttf.getVerticalMetrics().getAdvanceHeight(cid);
- gidMetrics[cid * 3 + 2] = ttf.getHorizontalMetrics().getAdvanceWidth(cid);
+ GlyphData glyph = ttf.getGlyph().getGlyph(cid);
+ if (glyph == null) {
+ gidMetrics[cid * 4] = Integer.MIN_VALUE;
+ } else {
+ gidMetrics[cid * 4] = cid;
+ gidMetrics[cid * 4 + 1] = ttf.getVerticalMetrics().getAdvanceHeight(cid);
+ gidMetrics[cid * 4 + 2] = ttf.getHorizontalMetrics().getAdvanceWidth(cid);
+ gidMetrics[cid * 4 + 3] = glyph.getYMaximum() + ttf.getVerticalMetrics().getTopSideBearing(cid);
+ }
}
cidFont.setItem(COSName.W2, getVerticalMetrics(gidMetrics));
@@ -530,11 +552,10 @@ final class PDCIDFontType2Embedder exten
float scaling = 1000f / ttf.getHeader().getUnitsPerEm();
- long v_y = Math.round(ttf.getVerticalHeader().getAscender() * scaling);
-
long lastCid = values[0];
long lastW1Value = Math.round(-values[1] * scaling);
- long lastVxValue = Math.round(values[2] * scaling / 2);
+ long lastVxValue = Math.round(values[2] * scaling / 2f);
+ long lastVyValue = Math.round(values[3] * scaling);
COSArray inner = new COSArray();
COSArray outer = new COSArray();
@@ -542,16 +563,22 @@ final class PDCIDFontType2Embedder exten
State state = State.FIRST;
- for (int i = 3; i < values.length; i += 3)
+ for (int i = 4; i < values.length; i += 4)
{
long cid = values[i];
+ if (cid == Integer.MIN_VALUE)
+ {
+ // no glyph for this cid
+ continue;
+ }
long w1Value = Math.round(-values[i + 1] * scaling);
long vxValue = Math.round(values[i + 2] * scaling / 2);
+ long vyValue = Math.round(values[i + 3] * scaling);
switch (state)
{
case FIRST:
- if (cid == lastCid + 1 && w1Value == lastW1Value && vxValue == lastVxValue)
+ if (cid == lastCid + 1 && w1Value == lastW1Value && vxValue == lastVxValue && vyValue == lastVyValue)
{
state = State.SERIAL;
}
@@ -561,20 +588,20 @@ final class PDCIDFontType2Embedder exten
inner = new COSArray();
inner.add(COSInteger.get(lastW1Value));
inner.add(COSInteger.get(lastVxValue));
- inner.add(COSInteger.get(v_y));
+ inner.add(COSInteger.get(lastVyValue));
}
else
{
inner = new COSArray();
inner.add(COSInteger.get(lastW1Value));
inner.add(COSInteger.get(lastVxValue));
- inner.add(COSInteger.get(v_y));
+ inner.add(COSInteger.get(lastVyValue));
outer.add(inner);
outer.add(COSInteger.get(cid));
}
break;
case BRACKET:
- if (cid == lastCid + 1 && w1Value == lastW1Value && vxValue == lastVxValue)
+ if (cid == lastCid + 1 && w1Value == lastW1Value && vxValue == lastVxValue && vyValue == lastVyValue)
{
state = State.SERIAL;
outer.add(inner);
@@ -584,25 +611,25 @@ final class PDCIDFontType2Embedder exten
{
inner.add(COSInteger.get(lastW1Value));
inner.add(COSInteger.get(lastVxValue));
- inner.add(COSInteger.get(v_y));
+ inner.add(COSInteger.get(lastVyValue));
}
else
{
state = State.FIRST;
inner.add(COSInteger.get(lastW1Value));
inner.add(COSInteger.get(lastVxValue));
- inner.add(COSInteger.get(v_y));
+ inner.add(COSInteger.get(lastVyValue));
outer.add(inner);
outer.add(COSInteger.get(cid));
}
break;
case SERIAL:
- if (cid != lastCid + 1 || w1Value != lastW1Value || vxValue != lastVxValue)
+ if (cid != lastCid + 1 || w1Value != lastW1Value || vxValue != lastVxValue || vyValue != lastVyValue)
{
outer.add(COSInteger.get(lastCid));
outer.add(COSInteger.get(lastW1Value));
outer.add(COSInteger.get(lastVxValue));
- outer.add(COSInteger.get(v_y));
+ outer.add(COSInteger.get(lastVyValue));
outer.add(COSInteger.get(cid));
state = State.FIRST;
}
@@ -610,6 +637,7 @@ final class PDCIDFontType2Embedder exten
}
lastW1Value = w1Value;
lastVxValue = vxValue;
+ lastVyValue = vyValue;
lastCid = cid;
}
@@ -619,20 +647,20 @@ final class PDCIDFontType2Embedder exten
inner = new COSArray();
inner.add(COSInteger.get(lastW1Value));
inner.add(COSInteger.get(lastVxValue));
- inner.add(COSInteger.get(v_y));
+ inner.add(COSInteger.get(lastVyValue));
outer.add(inner);
break;
case BRACKET:
inner.add(COSInteger.get(lastW1Value));
inner.add(COSInteger.get(lastVxValue));
- inner.add(COSInteger.get(v_y));
+ inner.add(COSInteger.get(lastVyValue));
outer.add(inner);
break;
case SERIAL:
outer.add(COSInteger.get(lastCid));
outer.add(COSInteger.get(lastW1Value));
outer.add(COSInteger.get(lastVxValue));
- outer.add(COSInteger.get(v_y));
+ outer.add(COSInteger.get(lastVyValue));
break;
}
return outer;
Modified: pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/TestFontEmbedding.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/TestFontEmbedding.java?rev=1824629&r1=1824628&r2=1824629&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/TestFontEmbedding.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/test/java/org/apache/pdfbox/pdmodel/font/TestFontEmbedding.java Sat Feb 17 19:32:26 2018
@@ -113,6 +113,63 @@ public class TestFontEmbedding extends T
assertEquals(expectedExtractedtext, extracted.replaceAll("\r", "").trim());
}
+ /**
+ * Embed a proportional TTF as vertical CIDFontType2 with subsetting.
+ *
+ * @throws IOException
+ */
+ public void testCIDFontType2VerticalSubsetProportional() throws IOException
+ {
+ String text = "ãABCã";
+ String expectedExtractedtext = "ã\nA\nB\nC\nã";
+ File pdf = new File(OUT_DIR, "CIDFontType2VP.pdf");
+
+ PDDocument document = new PDDocument();
+
+ PDPage page = new PDPage(PDRectangle.A4);
+ document.addPage(page);
+ File ipafont = new File("target/fonts/ipagp00303", "ipagp.ttf");
+ PDType0Font vfont = PDType0Font.loadVertical(document, ipafont);
+ PDPageContentStream contentStream = new PDPageContentStream(document, page);
+
+ contentStream.beginText();
+ contentStream.setFont(vfont, 20);
+ contentStream.newLineAtOffset(50, 700);
+ contentStream.showText(text);
+ contentStream.endText();
+ contentStream.close();
+
+ // Check the font substitution
+ byte[] encode = vfont.encode(text);
+ int cid = ((encode[0] & 0xFF) << 8) + (encode[1] & 0xFF);
+ assertEquals(12607, cid); // it's 12461 without substitution
+ // Check the dictionaries
+ COSDictionary fontDict = vfont.getCOSObject();
+ assertEquals(COSName.IDENTITY_V, fontDict.getDictionaryObject(COSName.ENCODING));
+
+ document.save(pdf);
+
+ // Vertical metrics are fixed during subsetting, so do this after calling save()
+ COSDictionary descFontDict = vfont.getDescendantFont().getCOSObject();
+ COSArray dw2 = (COSArray) descFontDict.getDictionaryObject(COSName.DW2);
+ assertNull(dw2); // This font uses default values for DW2
+ // c [ w1_1y v_1x v_1y ... w1_ny v_nx v_ny ]
+ COSArray w2 = (COSArray) descFontDict.getDictionaryObject(COSName.W2);
+ assertEquals(2, w2.size());
+ assertEquals(12607, w2.getInt(0)); // Start CID
+ COSArray metrics = (COSArray) w2.getObject(1);
+ int i = 0;
+ for (int n : new int[] {-570, 500, 450, -570, 500, 880})
+ {
+ assertEquals(n, metrics.getInt(i++));
+ }
+ document.close();
+
+ // Check text extraction
+ String extracted = getUnicodeText(pdf);
+ assertEquals(expectedExtractedtext, extracted.replaceAll("\r", "").trim());
+ }
+
private void validateCIDFontType2(boolean useSubset) throws Exception
{
PDDocument document = new PDDocument();