<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>fontbox</artifactId>
- <version>2.0.27</version>
+ <version>3.0.3</version>
</dependency>
<dependency>
<groupId>javax.media</groupId>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
- <version>2.0.27</version>
+ <version>3.0.3</version>
<scope>test</scope>
</dependency>
</dependencies>
import org.apache.commons.io.IOUtils;
import org.apache.fontbox.cff.CFFFont;
import org.apache.fontbox.cff.CFFParser;
+import org.apache.pdfbox.io.RandomAccessReadBuffer;
import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.fonts.type1.PFBData;
}
private List<InputStream> convertOTFToType1(InputStream in) throws IOException {
- CFFFont f = new CFFParser().parse(IOUtils.toByteArray(in)).get(0);
+ CFFFont f = new CFFParser().parse(new RandomAccessReadBuffer(IOUtils.toByteArray(in))).get(0);
List<InputStream> fonts = new ArrayList<InputStream>();
Map<Integer, Integer> glyphs = cidSet.getGlyphs();
int i = 0;
import java.util.List;
import java.util.Map;
-import org.apache.fontbox.cff.CFFDataInput;
import org.apache.fontbox.cff.CFFOperator;
+import org.apache.fontbox.cff.DataInputByteArray;
import org.apache.fop.fonts.truetype.FontFileReader;
import org.apache.fop.fonts.truetype.OTFFile;
* A class to read the CFF data from an OTF CFF font file.
*/
public class CFFDataReader {
- private CFFDataInput cffData;
+ private DataInputByteArray cffData;
private byte[] header;
private CFFIndexData nameIndex;
operator[0] = dictData[i];
}
String operatorName = "";
- CFFOperator tempOp = null;
if (operator.length > 1) {
- tempOp = CFFOperator.getOperator(new CFFOperator.Key(operator[0], operator[1]));
+ operatorName = CFFOperator.getOperator(operator[0], operator[1]);
} else {
- tempOp = CFFOperator.getOperator(new CFFOperator.Key(operator[0]));
- }
- if (tempOp != null) {
- operatorName = tempOp.getName();
+ operatorName = CFFOperator.getOperator(operator[0]);
}
DICTEntry newEntry = new DICTEntry();
newEntry.setOperator(operator);
* @return Returns an object representing the index
* @throws IOException Throws an IO Exception if an error occurs
*/
- public CFFIndexData readIndex(CFFDataInput input) throws IOException {
+ public CFFIndexData readIndex(DataInputByteArray input) throws IOException {
CFFIndexData nameIndex = new CFFIndexData();
if (input != null) {
int origPos = input.getPosition();
return 0;
}
cffData.setPosition(charsetOffset);
- int charsetFormat = cffData.readCard8();
+ int charsetFormat = cffData.readUnsignedByte();
switch (charsetFormat) {
case 0: //Adjust for .notdef character
cffData.setPosition(cffData.getPosition() + (--gid * 2));
- return cffData.readSID();
+ return cffData.readUnsignedShort();
case 1: return getSIDFromGIDFormat(gid, 1);
case 2: return getSIDFromGIDFormat(gid, 2);
default: return 0;
int glyphCount = 0;
while (true) {
int oldGlyphCount = glyphCount;
- int start = cffData.readSID();
- glyphCount += ((format == 1) ? cffData.readCard8() : cffData.readCard16()) + 1;
+ int start = cffData.readUnsignedShort();
+ glyphCount += ((format == 1) ? cffData.readUnsignedByte() : cffData.readUnsignedShort()) + 1;
if (gid <= glyphCount) {
return start + (gid - oldGlyphCount) - 1;
}
return charStringIndex;
}
- public CFFDataInput getCFFData() {
+ public DataInputByteArray getCFFData() {
return cffData;
}
return fdFonts;
}
- public CFFDataInput getLocalSubrsForGlyph(int glyph) throws IOException {
+ public DataInputByteArray getLocalSubrsForGlyph(int glyph) throws IOException {
//Subsets are currently written using a Format0 FDSelect
FDSelect fontDictionary = getFDSelect();
if (fontDictionary instanceof Format0FDSelect) {
FontDict font = getFDFonts().get(found);
byte[] localSubrData = font.getLocalSubrData().getByteData();
if (localSubrData != null) {
- return new CFFDataInput(localSubrData);
+ return new DataInputByteArray(localSubrData);
} else {
return null;
}
FontDict font = getFDFonts().get(index);
byte[] localSubrsData = font.getLocalSubrData().getByteData();
if (localSubrsData != null) {
- return new CFFDataInput(localSubrsData);
+ return new DataInputByteArray(localSubrsData);
} else {
return null;
}
int offset = topDict.get("Encoding").getOperands().get(0).intValue();
if (offset != 0 && offset != 1) {
//No need to set the offset as we are reading the data sequentially.
- int format = cffData.readCard8();
- int numEntries = cffData.readCard8();
+ int format = cffData.readUnsignedByte();
+ int numEntries = cffData.readUnsignedByte();
switch (format) {
case 0:
foundEncoding = readFormat0Encoding(format, numEntries);
newEncoding.setNumEntries(numEntries);
int[] codes = new int[numEntries];
for (int i = 0; i < numEntries; i++) {
- codes[i] = cffData.readCard8();
+ codes[i] = cffData.readUnsignedByte();
}
newEncoding.setCodes(codes);
return newEncoding;
newEncoding.setNumEntries(numEntries);
Map<Integer, Integer> ranges = new LinkedHashMap<Integer, Integer>();
for (int i = 0; i < numEntries; i++) {
- int first = cffData.readCard8();
- int left = cffData.readCard8();
+ int first = cffData.readUnsignedByte();
+ int left = cffData.readUnsignedByte();
ranges.put(first, left);
}
newEncoding.setRanges(ranges);
if (fdSelectEntry != null) {
int fdOffset = fdSelectEntry.getOperands().get(0).intValue();
cffData.setPosition(fdOffset);
- int format = cffData.readCard8();
+ int format = cffData.readUnsignedByte();
switch (format) {
case 0:
fdSelect = readFormat0FDSelect();
int glyphCount = charStringIndex.getNumObjects();
int[] fds = new int[glyphCount];
for (int i = 0; i < glyphCount; i++) {
- fds[i] = cffData.readCard8();
+ fds[i] = cffData.readUnsignedByte();
}
newFDs.setFDIndexes(fds);
return newFDs;
private Format3FDSelect readFormat3FDSelect() throws IOException {
Format3FDSelect newFDs = new Format3FDSelect();
newFDs.setFormat(3);
- int rangeCount = cffData.readCard16();
+ int rangeCount = cffData.readUnsignedShort();
newFDs.setRangeCount(rangeCount);
Map<Integer, Integer> ranges = new LinkedHashMap<Integer, Integer>();
for (int i = 0; i < rangeCount; i++) {
- int first = cffData.readCard16();
- int fd = cffData.readCard8();
+ int first = cffData.readUnsignedShort();
+ int fd = cffData.readUnsignedByte();
ranges.put(first, fd);
}
newFDs.setRanges(ranges);
- newFDs.setSentinelGID(cffData.readCard16());
+ newFDs.setSentinelGID(cffData.readUnsignedShort());
return newFDs;
}
* @param cffData A byte array containing the CFF data
* @throws IOException Throws an IO Exception if an error occurs
*/
- public void parseIndexHeader(CFFDataInput cffData) throws IOException {
- setNumObjects(cffData.readCard16());
- setOffSize(cffData.readOffSize());
+ public void parseIndexHeader(DataInputByteArray cffData) throws IOException {
+ setNumObjects(cffData.readUnsignedShort());
+ setOffSize(cffData.readUnsignedByte());
int[] offsets = new int[getNumObjects() + 1];
byte[] bytes;
//Fills the offsets array
for (int i = 0; i <= getNumObjects(); i++) {
switch (getOffSize()) {
case 1:
- offsets[i] = cffData.readCard8();
+ offsets[i] = cffData.readUnsignedByte();
break;
case 2:
- offsets[i] = cffData.readCard16();
+ offsets[i] = cffData.readUnsignedShort();
break;
case 3:
bytes = cffData.readBytes(3);
import java.io.IOException;
-import org.apache.fontbox.cff.CFFDataInput;
+import org.apache.fontbox.cff.DataInputByteArray;
+
+public class FOPCFFDataInput extends DataInputByteArray {
+ private final byte[] inputBuffer;
+ private int bufferPosition;
-public class FOPCFFDataInput extends CFFDataInput {
public FOPCFFDataInput(byte[] buffer) {
super(buffer);
+ this.inputBuffer = buffer;
+ }
+
+ public boolean hasRemaining() throws IOException {
+ return this.bufferPosition < this.inputBuffer.length;
+ }
+
+ public int getPosition() {
+ return this.bufferPosition;
+ }
+
+ public void setPosition(int position) throws IOException {
+ if (position < 0) {
+ throw new IOException("position is negative");
+// } else if (position >= this.inputBuffer.length) {
+// throw new IOException("New position is out of range " + position + " >= " + this.inputBuffer.length);
+ } else {
+ this.bufferPosition = position;
+ }
+ }
+
+ public byte readByte() throws IOException {
+ if (!this.hasRemaining()) {
+ throw new IOException("End off buffer reached");
+ } else {
+ return this.inputBuffer[this.bufferPosition++];
+ }
+ }
+
+ public int readUnsignedByte() throws IOException {
+ if (!this.hasRemaining()) {
+ throw new IOException("End off buffer reached");
+ } else {
+ return this.inputBuffer[this.bufferPosition++] & 255;
+ }
+ }
+
+ public int peekUnsignedByte(int offset) throws IOException {
+ if (offset < 0) {
+ throw new IOException("offset is negative");
+ } else if (this.bufferPosition + offset >= this.inputBuffer.length) {
+ throw new IOException("Offset position is out of range " + (this.bufferPosition + offset)
+ + " >= " + this.inputBuffer.length);
+ } else {
+ return this.inputBuffer[this.bufferPosition + offset] & 255;
+ }
+ }
+
+ public byte[] readBytes(int length) throws IOException {
+ if (length < 0) {
+ throw new IOException("length is negative");
+ } else if (this.inputBuffer.length - this.bufferPosition < length) {
+ throw new IOException("Premature end of buffer reached");
+ } else {
+ byte[] bytes = new byte[length];
+ System.arraycopy(this.inputBuffer, this.bufferPosition, bytes, 0, length);
+ this.bufferPosition += length;
+ return bytes;
+ }
}
- public int readOffSize() throws IOException {
- return readUnsignedByte();
+ public int length() throws IOException {
+ return this.inputBuffer.length;
}
}
import java.io.IOException;
import java.util.List;
-import org.apache.fontbox.cff.CFFDataInput;
import org.apache.fontbox.cff.CFFFont;
import org.apache.fontbox.cff.CFFParser;
import org.apache.fontbox.cff.CFFType1Font;
+import org.apache.fontbox.cff.DataInputByteArray;
+import org.apache.pdfbox.io.RandomAccessReadBuffer;
public class OTFFile extends OpenFont {
fontFile = in;
fontFile.seekSet(0);
CFFParser parser = new CFFParser();
- fileFont = parser.parse(in.getAllBytes()).get(0);
+ fileFont = parser.parse(new RandomAccessReadBuffer(in.getAllBytes())).get(0);
embedFontName = fileFont.getName();
}
*/
public static byte[] getCFFData(FontFileReader fontFile) throws IOException {
byte[] cff = fontFile.getAllBytes();
- CFFDataInput input = new CFFDataInput(fontFile.getAllBytes());
+ DataInputByteArray input = new DataInputByteArray(fontFile.getAllBytes());
input.readBytes(4); //OTTO
short numTables = input.readShort();
input.readShort(); //searchRange
return cff;
}
- private static long readLong(CFFDataInput input) throws IOException {
- return (input.readCard16() << 16) | input.readCard16();
+ private static long readLong(DataInputByteArray input) throws IOException {
+ return (input.readUnsignedShort() << 16) | input.readUnsignedShort();
}
public boolean isType1() {
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import org.apache.fontbox.cff.CFFStandardString;
import org.apache.fontbox.cff.CFFType1Font;
import org.apache.fontbox.cff.CharStringCommand;
+import org.apache.fontbox.cff.Type1CharString;
import org.apache.fontbox.cff.Type2CharString;
import org.apache.fop.fonts.MultiByteFont;
/** The operator used to identify a global subroutine reference */
private static final int GLOBAL_SUBROUTINE = 29;
- private static final String ACCENT_CMD = "seac";
+ private static final String ACCENT_CMD = "SEAC|";
/** The parser used to parse type2 charstring */
private Type2Parser type2Parser;
for (int gid : subsetGlyphs.keySet()) {
Type2CharString type2CharString = cffType1Font.getType2CharString(gid);
List<Number> stack = new ArrayList<Number>();
- for (Object obj : type2CharString.getType1Sequence()) {
+ List type1Sequence = getType1Sequence(type2CharString);
+ for (Object obj : type1Sequence) {
if (obj instanceof CharStringCommand) {
- String name = CharStringCommand.TYPE1_VOCABULARY.get(((CharStringCommand) obj).getKey());
- if (ACCENT_CMD.equals(name)) {
+ if (ACCENT_CMD.equals(obj.toString())) {
int first = stack.get(3).intValue();
int second = stack.get(4).intValue();
mbFont.mapChar(AdobeStandardEncoding.getUnicodeFromCodePoint(first));
}
}
+ private List<Object> getType1Sequence(Type1CharString type1CharString) {
+ try {
+ Field f = Type1CharString.class.getDeclaredField("type1Sequence");
+ f.setAccessible(true);
+ return (List<Object>) f.get(type1CharString);
+ } catch (IllegalAccessException | NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private Map<Integer, Integer> sortByValue(Map<Integer, Integer> map) {
List<Entry<Integer, Integer>> list = new ArrayList<Entry<Integer, Integer>>(map.entrySet());
Collections.sort(list, new Comparator<Entry<Integer, Integer>>() {
--- /dev/null
+/*
+ * 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.fop.render.ps;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class DataOutput {
+
+ private ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
+
+ private String outputEncoding;
+
+ /**
+ * Constructor.
+ */
+ public DataOutput() {
+ this("ISO-8859-1");
+ }
+
+ /**
+ * Constructor with a given encoding.
+ * @param encoding the encoding to be used for writing
+ */
+ public DataOutput(String encoding) {
+ this.outputEncoding = encoding;
+ }
+
+ /**
+ * Returns the written data buffer as byte array.
+ * @return the data buffer as byte array
+ */
+ public byte[] getBytes() {
+ return outputBuffer.toByteArray();
+ }
+
+ /**
+ * Write an int value to the buffer.
+ * @param value the given value
+ */
+ public void write(int value) {
+ outputBuffer.write(value);
+ }
+
+ /**
+ * Write a byte array to the buffer.
+ * @param buffer the given byte array
+ */
+ public void write(byte[] buffer) {
+ outputBuffer.write(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Write a part of a byte array to the buffer.
+ * @param buffer the given byte buffer
+ * @param offset the offset where to start
+ * @param length the amount of bytes to be written from the array
+ */
+ public void write(byte[] buffer, int offset, int length) {
+ outputBuffer.write(buffer, offset, length);
+ }
+
+ /**
+ * Write the given string to the buffer using the given encoding.
+ * @param string the given string
+ * @throws IOException If an error occurs during writing the data to the buffer
+ */
+ public void print(String string) throws IOException {
+ write(string.getBytes(outputEncoding));
+ }
+
+ /**
+ * Write the given string to the buffer using the given encoding.
+ * A newline is added after the given string
+ * @param string the given string
+ * @throws IOException If an error occurs during writing the data to the buffer
+ */
+ public void println(String string) throws IOException {
+ write(string.getBytes(outputEncoding));
+ write('\n');
+ }
+
+ /**
+ * Add a newline to the given string.
+ */
+ public void println() {
+ write('\n');
+ }
+}
package org.apache.fop.render.ps;
import java.io.ByteArrayOutputStream;
+import java.lang.reflect.Field;
import java.util.List;
import org.apache.fontbox.cff.CharStringCommand;
}
private void writeCommand(CharStringCommand command) {
- int[] value = command.getKey().getValue();
+ int[] value = getValue(command);
for (int aValue : value) {
output.write(aValue);
}
}
+ private int[] getValue(CharStringCommand command) {
+ CharStringCommand.Type1KeyWord keyWord = command.getType1KeyWord();
+ if (keyWord == null) {
+ return new int[0];
+ }
+ CharStringCommand.Key key = CharStringCommand.Key.valueOf(keyWord.name());
+ try {
+ Field f = key.getClass().getDeclaredField("hashValue");
+ f.setAccessible(true);
+ int value = (int) f.get(key);
+ return new int[] {value};
+ } catch (IllegalAccessException | NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private void writeNumber(Integer number) {
int value = number;
if (value >= -107 && value <= 107) {
package org.apache.fop.render.ps;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Collection;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.fontbox.cff.CFFCIDFont;
import org.apache.fontbox.cff.CFFFont;
import org.apache.fontbox.cff.CFFType1Font;
-import org.apache.fontbox.cff.DataOutput;
+import org.apache.fontbox.cff.Type1CharString;
import org.apache.fontbox.cff.Type1FontUtil;
/**
if (gid == 0) {
mapping = ".notdef";
}
- byte[] type1Bytes;
+ byte[] type1Bytes = null;
if (font instanceof CFFCIDFont) {
int cid = font.getCharset().getCIDForGID(gid);
- type1Bytes = formatter.format(((CFFCIDFont)font).getType2CharString(cid).getType1Sequence());
+ type1Bytes = formatter.format(getType1Sequence(((CFFCIDFont)font).getType2CharString(cid)));
} else {
mapping = font.getCharset().getNameForGID(gid);
- type1Bytes = formatter.format(((CFFType1Font)font).getType1CharString(mapping).getType1Sequence());
+ type1Bytes = formatter.format(getType1Sequence(((CFFType1Font)font).getType1CharString(mapping)));
}
byte[] charstringBytes = Type1FontUtil.charstringEncrypt(type1Bytes, 4);
output.print("/" + mapping + " " + charstringBytes.length + " RD ");
output.println("mark currentfile closefile");
}
+ private List<Object> getType1Sequence(Type1CharString type1CharString) {
+ try {
+ Field f = Type1CharString.class.getDeclaredField("type1Sequence");
+ f.setAccessible(true);
+ return (List<Object>) f.get(type1CharString);
+ } catch (IllegalAccessException | NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private static String formatArray(Object object, boolean executable) {
return formatArray(object, null, executable);
}
import static org.junit.Assert.assertEquals;
-import org.apache.fontbox.cff.CFFDataInput;
-
import org.apache.fop.fonts.cff.CFFDataReader.CFFIndexData;
import org.apache.fop.fonts.cff.CFFDataReader.DICTEntry;
import org.apache.fop.fonts.truetype.OTFSubSetFile;
data[i] = (byte)randGen.nextInt(255);
}
testIndex = OTFSubSetFile.concatArray(testIndex, data);
- CFFIndexData indexData = cffReader.readIndex(new CFFDataInput(testIndex));
+ CFFIndexData indexData = cffReader.readIndex(new FOPCFFDataInput(testIndex));
assertEquals(indexData.getNumObjects(), 5);
assertEquals(indexData.getOffSize(), 1);
assertEquals(indexData.getOffsets().length, 6);
import org.apache.fontbox.cff.CFFType1Font;
import org.apache.fontbox.cff.CharStringCommand;
import org.apache.fontbox.cff.Type2CharString;
+import org.apache.pdfbox.io.RandomAccessReadBuffer;
import org.apache.fop.fonts.MultiByteFont;
import org.apache.fop.fonts.cff.CFFDataReader;
}
OTFSubSetFile otfSubSetFile = new OTFSubSetFile();
otfSubSetFile.readFont(sourceSansReader, sb.toString(), null, glyphs);
- new CFFParser().parse(otfSubSetFile.getFontSubset());
+ new CFFParser().parse(new RandomAccessReadBuffer(otfSubSetFile.getFontSubset()));
}
@Test
OTFSubSetFile sourceSansSubset = new OTFSubSetFile() {
protected void initializeFont(FontFileReader in) {
fileFont = new CFFType1Font() {
- List<Object> sequence = Arrays.asList(0, 0, 0, (int)'a', (int)'b', new CharStringCommand(12, 6));
+ List<Object> sequence = Arrays.asList(0, 0, 0, (int)'a', (int)'b',
+ CharStringCommand.getInstance(12, 6));
public Type2CharString getType2CharString(int gid) {
return new Type2CharString(null, null, null, 0, sequence, 0, 0);
}
package org.apache.fop.render.pdf;
-
-
import java.io.File;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
private static String extractTextFromPDF(byte[] pdfContent) throws IOException {
PDFTextStripper pdfStripper = new PDFTextStripper();
- PDDocument pdDoc = PDDocument.load(pdfContent);
+ PDDocument pdDoc = Loader.loadPDF(pdfContent);
return pdfStripper.getText(pdDoc);
}
}