You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2015/05/31 12:36:07 UTC
svn commit: r1682711 - in
/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf:
KerningSubtable.java KerningTable.java TTFParser.java TrueTypeFont.java
Author: lehmi
Date: Sun May 31 10:36:06 2015
New Revision: 1682711
URL: http://svn.apache.org/r1682711
Log:
PDFBOX-2806: added support for kern table as proposed by Glenn Adams
Added:
pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningSubtable.java (with props)
pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningTable.java (with props)
Modified:
pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java
pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java
Added: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningSubtable.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningSubtable.java?rev=1682711&view=auto
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningSubtable.java (added)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningSubtable.java Sun May 31 10:36:06 2015
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.fontbox.ttf;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * A 'kern' table in a true type font.
+ *
+ * @author Glenn Adams
+ */
+public class KerningSubtable extends TTFTable
+{
+ // coverage field bit masks and values
+ private static final int COVERAGE_HORIZONTAL = 0x0001;
+ private static final int COVERAGE_MINIMUMS = 0x0002;
+ private static final int COVERAGE_CROSS_STREAM = 0x0004;
+ private static final int COVERAGE_FORMAT = 0xFF00;
+
+ private static final int COVERAGE_HORIZONTAL_SHIFT = 0;
+ private static final int COVERAGE_MINIMUMS_SHIFT = 1;
+ private static final int COVERAGE_CROSS_STREAM_SHIFT = 2;
+ private static final int COVERAGE_FORMAT_SHIFT = 8;
+
+ // true if horizontal kerning
+ private boolean horizontal;
+ // true if mimimum adjustment values (versus kerning values)
+ private boolean minimums;
+ // true if cross-stream (block progression) kerning
+ private boolean crossStream;
+ // format specific pair data
+ private PairData pairs;
+
+ /**
+ * Constructor.
+ */
+ public KerningSubtable()
+ {
+ }
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @param version The version of the table to be read
+ * @throws IOException If there is an error reading the data.
+ */
+ public void read(TrueTypeFont ttf, TTFDataStream data, int version) throws IOException
+ {
+ if (version == 0)
+ {
+ readSubtable0(ttf, data);
+ }
+ else if (version == 1)
+ {
+
+ readSubtable1(ttf, data);
+ }
+ else
+ {
+ throw new IllegalStateException();
+ }
+ }
+
+ public boolean isHorizontalKerning()
+ {
+ return isHorizontalKerning(false);
+ }
+
+ public boolean isHorizontalKerning(boolean cross)
+ {
+ if (!horizontal)
+ {
+ return false;
+ }
+ else if (minimums)
+ {
+ return false;
+ }
+ else if (cross)
+ {
+ return crossStream;
+ }
+ else
+ {
+ return !crossStream;
+ }
+ }
+
+ public int[] getKerning(int[] glyphs)
+ {
+ int ng = glyphs.length;
+ int[] kerning = new int[ng];
+ for (int i = 0; i < ng; ++i)
+ {
+ int l = glyphs[i];
+ int r = -1;
+ for (int k = i + 1; k < ng; ++k)
+ {
+ int g = glyphs[k];
+ if (g >= 0)
+ {
+ r = g;
+ break;
+ }
+ }
+ kerning[i] = getKerning(l, r);
+ }
+ return kerning;
+ }
+
+ public int getKerning(int l, int r)
+ {
+ return pairs.getKerning(l, r);
+ }
+
+ private void readSubtable0(TrueTypeFont ttf, TTFDataStream data) throws IOException
+ {
+ int version = data.readUnsignedShort();
+ if (version != 0)
+ {
+ throw new UnsupportedOperationException("Unsupported kerning sub-table version: "
+ + version);
+ }
+ int length = data.readUnsignedShort();
+ if (length < 6)
+ {
+ throw new IOException("Kerning sub-table too short, got " + length
+ + " bytes, expect 6 or more.");
+ }
+ int coverage = data.readUnsignedShort();
+ if (isBitsSet(coverage, COVERAGE_HORIZONTAL, COVERAGE_HORIZONTAL_SHIFT))
+ {
+ this.horizontal = true;
+ }
+ if (isBitsSet(coverage, COVERAGE_MINIMUMS, COVERAGE_MINIMUMS_SHIFT))
+ {
+ this.minimums = true;
+ }
+ if (isBitsSet(coverage, COVERAGE_CROSS_STREAM, COVERAGE_CROSS_STREAM_SHIFT))
+ {
+ this.crossStream = true;
+ }
+ int format = getBits(coverage, COVERAGE_FORMAT, COVERAGE_FORMAT_SHIFT);
+ if ((format != 0) && (format != 2))
+ {
+ throw new UnsupportedOperationException("Unsupported kerning sub-table format: "
+ + format);
+ }
+ if (format == 0)
+ {
+ readSubtable0Format0(ttf, data);
+ }
+ else if (format == 2)
+ {
+ readSubtable0Format2(ttf, data);
+ }
+ }
+
+ private void readSubtable0Format0(TrueTypeFont ttf, TTFDataStream data) throws IOException
+ {
+ pairs = new PairData0Format0();
+ pairs.read(data);
+ }
+
+ private void readSubtable0Format2(TrueTypeFont ttf, TTFDataStream data) throws IOException
+ {
+ throw new UnsupportedOperationException(
+ "Kerning table version 0 format 2 not yet supported.");
+ }
+
+ private void readSubtable1(TrueTypeFont ttf, TTFDataStream data) throws IOException
+ {
+ throw new UnsupportedOperationException(
+ "Kerning table version 1 formats not yet supported.");
+ }
+
+ private static boolean isBitsSet(int bits, int mask, int shift)
+ {
+ return getBits(bits, mask, shift) != 0;
+ }
+
+ private static int getBits(int bits, int mask, int shift)
+ {
+ return (bits & mask) >> shift;
+ }
+
+ abstract static class PairData
+ {
+ public abstract void read(TTFDataStream data) throws IOException;
+
+ public abstract int getKerning(int l, int r);
+ }
+
+ static class PairData0Format0 extends PairData implements Comparator<int[]>
+ {
+ private int searchRange;
+ private int[][] pairs;
+
+ @Override
+ public void read(TTFDataStream data) throws IOException
+ {
+ int numPairs = data.readUnsignedShort();
+ searchRange = data.readUnsignedShort()/6;
+ int entrySelector = data.readUnsignedShort();
+ int rangeShift = data.readUnsignedShort();
+ pairs = new int[numPairs][3];
+ for (int i = 0; i < numPairs; ++i)
+ {
+ int left = data.readUnsignedShort();
+ int right = data.readUnsignedShort();
+ int value = data.readSignedShort();
+ pairs[i][0] = left;
+ pairs[i][1] = right;
+ pairs[i][2] = value;
+ }
+ }
+
+ @Override
+ public int getKerning(int l, int r)
+ {
+ int[] key = new int[] { l, r, 0 };
+ int index;
+ index = Arrays.binarySearch(pairs, 0, searchRange, key, this);
+ if (index >= 0)
+ {
+ return pairs[index][2];
+ }
+ index = Arrays.binarySearch(pairs, searchRange, pairs.length, key, this);
+ if (index >= 0)
+ {
+ return pairs[searchRange + index][2];
+ }
+ return 0;
+ }
+
+ @Override
+ public int compare(int[] p1, int[] p2)
+ {
+ assert p1 != null;
+ assert p1.length >= 2;
+ assert p2 != null;
+ assert p2.length >= 2;
+ int l1 = p1[0];
+ int l2 = p2[0];
+ if (l1 < l2)
+ {
+ return -1;
+ }
+ else if (l1 > l2)
+ {
+ return 1;
+ }
+ else
+ {
+ int r1 = p1[1];
+ int r2 = p2[1];
+ if (r1 < r2)
+ {
+ return -1;
+ }
+ else if (r1 > r2)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+ }
+}
Propchange: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningSubtable.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningTable.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningTable.java?rev=1682711&view=auto
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningTable.java (added)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningTable.java Sun May 31 10:36:06 2015
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.fontbox.ttf;
+
+import java.io.IOException;
+
+/**
+ * A 'kern' table in a true type font.
+ *
+ * @author Glenn Adams
+ */
+public class KerningTable extends TTFTable
+{
+ /**
+ * Tag to identify this table.
+ */
+ public static final String TAG = "kern";
+
+ private KerningSubtable[] subtables;
+
+ /**
+ * This will read the required data from the stream.
+ *
+ * @param ttf The font that is being read.
+ * @param data The stream to read the data from.
+ * @throws IOException If there is an error reading the data.
+ */
+ @Override
+ public void read(TrueTypeFont ttf, TTFDataStream data) throws IOException
+ {
+ int version = data.readUnsignedShort();
+ if (version != 0)
+ {
+ version = (version << 16) | data.readUnsignedShort();
+ }
+ if (version > 1)
+ {
+ throw new UnsupportedOperationException("Unsupported kerning table version: " + version);
+ }
+ int numSubtables;
+ if (version == 0)
+ {
+ numSubtables = data.readUnsignedShort();
+ }
+ else
+ {
+ numSubtables = (int) data.readUnsignedInt();
+ }
+ subtables = new KerningSubtable[numSubtables];
+ for (int i = 0; i < numSubtables; ++i)
+ {
+ KerningSubtable subtable = new KerningSubtable();
+ subtable.read(ttf, data, version);
+ subtables[i] = subtable;
+ }
+ initialized = true;
+ }
+
+ /**
+ * Obtain first subtable that supports non-cross-stream horizontal kerning.
+ *
+ * @return first matching subtable or null if none found
+ */
+ public KerningSubtable getHorizontalKerningSubtable()
+ {
+ return getHorizontalKerningSubtable(false);
+ }
+
+ /**
+ * Obtain first subtable that supports horizontal kerning with specificed cross stream.
+ *
+ * @param cross true if requesting cross stream horizontal kerning
+ * @return first matching subtable or null if none found
+ */
+ public KerningSubtable getHorizontalKerningSubtable(boolean cross)
+ {
+ for (KerningSubtable s : subtables)
+ {
+ if (s.isHorizontalKerning(cross))
+ {
+ return s;
+ }
+ }
+ return null;
+ }
+}
Propchange: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/KerningTable.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java?rev=1682711&r1=1682710&r2=1682711&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TTFParser.java Sun May 31 10:36:06 2015
@@ -259,6 +259,10 @@ public class TTFParser
{
table = new DigitalSignatureTable();
}
+ else if (tag.equals(KerningTable.TAG))
+ {
+ table = new KerningTable();
+ }
else if (tag.equals(VerticalHeaderTable.TAG))
{
table = new VerticalHeaderTable();
Modified: pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java?rev=1682711&r1=1682710&r2=1682711&view=diff
==============================================================================
--- pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java (original)
+++ pdfbox/trunk/fontbox/src/main/java/org/apache/fontbox/ttf/TrueTypeFont.java Sun May 31 10:36:06 2015
@@ -318,6 +318,21 @@ public class TrueTypeFont implements Typ
}
/**
+ * Get the "kern" table for this TTF.
+ *
+ * @return The "kern" table.
+ */
+ public synchronized KerningTable getKerning() throws IOException
+ {
+ KerningTable kerning = (KerningTable)tables.get( KerningTable.TAG );
+ if (kerning != null && !kerning.getInitialized())
+ {
+ readTable(kerning);
+ }
+ return kerning;
+ }
+
+ /**
* This permit to get the data of the True Type Font
* program representing the stream used to build this
* object (normally from the TTFParser object).