--- /dev/null
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import org.axsl.fontR.FontConsumer;
+import org.axsl.fontR.FontUse;
+
+/**
+ * Class that stores a mapping of each FontUse to a corresponding internal name
+ * (F1, F2...), used to refer to a font in the PDF file.
+ */
+public class FontMap {
+
+ /**
+ * The Map iself. Keys are FontUse instances, values are String (internal
+ * name).
+ */
+ private Map fontMap;
+
+ /**
+ * Font consumer associated to this map.
+ */
+ private FontConsumer fontConsumer;
+
+
+ /**
+ * Builds a font mapping of the FontUses available from the font server
+ * associated to the given font consumer.
+ *
+ * @param fontConsumer font consumer from which to get the FontUses
+ */
+ public FontMap(FontConsumer fontConsumer) {
+ this.fontConsumer = fontConsumer;
+ this.fontMap = new Hashtable();
+ }
+
+ /**
+ * Builds a font mapping of (possibly all) the FontUses available from the
+ * font server associated to the given font consumer.
+ *
+ * @param fontConsumer
+ * font consumer from which to get the FontUses
+ * @param registerAllFonts
+ * if <code>true</code>, all of the FontUses available from
+ * the font server are registered (useful for poscript output).
+ * Otherwise font uses are only registered when needed. This is
+ * the default.
+ */
+ public FontMap(FontConsumer fontConsumer, boolean registerAllFonts) {
+ this.fontConsumer = fontConsumer;
+ if (!registerAllFonts) {
+ this.fontMap = new Hashtable();
+ } else {
+ // TODO vh: re-enable
+// FontUse[] fontUses = fontConsumer.getFontServer().getAllFontUses(true, false);
+// fontMap = new Hashtable(fontUses.length);
+// for (int i = 0; i < fontUses.length; i++) {
+// fontMap.put(fontUses[i], "F" + new Integer(i));
+// }
+ }
+ }
+
+ /**
+ * Returns the font consumer associated to this font map.
+ * @return the font consumer
+ */
+ public FontConsumer getFontConsumer() {
+ return fontConsumer;
+ }
+
+ /**
+ * Returns the internal name associated to the given FontUse instance.
+ * @param fontUse a FontUse
+ * @return the corresponding internal name
+ */
+ public String getInternalName(FontUse fontUse) {
+ String internalName;
+ if (!fontMap.containsKey(fontUse)) {
+ internalName = "F" + (fontMap.size() + 1);
+ fontMap.put(fontUse, internalName);
+ } else {
+ internalName = (String) fontMap.get(fontUse);
+ }
+ return internalName;
+ }
+
+ /**
+ * Returns the number of registered font uses.
+ * @return number of mappings
+ */
+ public int getSize() {
+ return fontMap.size();
+ }
+
+ /**
+ * Returns the mappings contained in this font map. Each element in the
+ * returned set is a Map.Entry, in which the key is a FontUse and the value
+ * its corresponding internal name.
+ *
+ * @return a set of the mappings contained in this font map.
+ */
+ public Set getMappings() {
+ return fontMap.entrySet();
+ }
+}
--- /dev/null
+/*
+ * Copyright 2004 The FOray Project.
+ * http://www.foray.org
+ *
+ * Licensed 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.
+ *
+ * This work is in part derived from the following work(s), used with the
+ * permission of the licensor:
+ * Apache FOP, licensed by the Apache Software Foundation
+ *
+ */
+
+/* $Id$ */
+
+package org.apache.fop.pdf;
+
+import org.axsl.fontR.FontConsumer;
+import org.axsl.fontR.FontUse;
+import org.axsl.fontR.output.FontPDF;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+public class PDFFontFileStream extends PDFStream {
+ private FontUse font;
+ private FontPDF fontOutput;
+
+ public PDFFontFileStream(FontUse font, FontConsumer fontConsumer) {
+ super();
+ if (!font.getFont().isEmbeddable()) {
+ // TODO vh: better error handling
+ log.error("Can't create PDFFontFileStream for a Font "
+ + "that is not embeddable.");
+ return;
+ }
+ this.font = font;
+ this.fontOutput = (FontPDF)font.getFontOutput("application/pdf");
+ byte[] fontFileStream = fontOutput.getContents();
+ // TODO vh
+// addFilter("flate");
+// addFilter("ascii-85");
+ try {
+ setData(fontFileStream);
+ } catch (IOException ioe) {
+ log.error("Failed to embed font "
+ + font.getPostscriptName() + ": " + ioe.getMessage());
+ }
+ }
+
+ protected int output(OutputStream stream) throws IOException {
+ int length = 0;
+ // TODO vh
+// String filterEntry = applyFilters();
+ String preData
+ = getObjectID() + "\n<< /Length "
+ + getDataLength()
+ + " "
+// + filterEntry
+ + fontOutput.getPDFFontFileStreamAdditional()
+ + " >>\n";
+
+ byte[] p;
+ try {
+ p = preData.getBytes(PDFDocument.ENCODING);
+ } catch (UnsupportedEncodingException ue) {
+ p = preData.getBytes();
+ }
+
+ stream.write(p);
+ length += p.length;
+
+ length += outputStreamData(data, stream);
+ try {
+ p = ("\nendobj\n").getBytes(PDFDocument.ENCODING);
+ } catch (UnsupportedEncodingException ue) {
+ p = ("\nendobj\n").getBytes();
+ }
+ stream.write(p);
+ length += p.length;
+ return length;
+ }
+
+}
--- /dev/null
+package org.apache.fop.pdf;
+
+import org.apache.fop.fonts.CIDFont;
+
+public class PDFToUnicodeCMap extends PDFCMap {
+
+ /**
+ * handle to read font
+ */
+ protected CIDFont cidFont;
+
+ /**
+ * Constructor.
+ *
+ * @param name One of the registered names found in Table 5.14 in PDF
+ * Reference, Second Edition.
+ * @param sysInfo The attributes of the character collection of the CIDFont.
+ */
+ public PDFToUnicodeCMap(CIDFont cidMetrics, String name, PDFCIDSystemInfo sysInfo) {
+ super(name, sysInfo);
+ cidFont = cidMetrics;
+ }
+
+ public void fillInPDF(StringBuffer p) {
+ writeCIDInit(p);
+ writeCIDSystemInfo(p);
+ writeVersionTypeName(p);
+ writeCodeSpaceRange(p);
+ writeBFEntries(p);
+ writeWrapUp(p);
+ add(p.toString());
+ }
+
+ protected void writeCIDSystemInfo(StringBuffer p) {
+ p.append("/CIDSystemInfo\n");
+ p.append("<< /Registry (Adobe)\n");
+ p.append("/Ordering (UCS)\n");
+ p.append("/Supplement 0\n");
+ p.append(">> def\n");
+ }
+
+ protected void writeVersionTypeName(StringBuffer p) {
+ p.append("/CMapName /Adobe-Identity-UCS def\n");
+ p.append("/CMapType 2 def\n");
+ }
+
+ /**
+ * Writes the character mappings for this font.
+ */
+ protected void writeBFEntries(StringBuffer p) {
+ if(cidFont == null) return;
+
+ char[] charArray = cidFont.getCharsUsed();
+
+ if(charArray != null) {
+ writeBFCharEntries(p, charArray);
+ writeBFRangeEntries(p, charArray);
+ }
+ }
+
+ protected void writeBFCharEntries(StringBuffer p, char[] charArray) {
+ int completedEntries = 0;
+ int totalEntries = 0;
+ for (int i = 0; i < charArray.length; i++) {
+ if (! partOfRange(charArray, i)) {
+ totalEntries ++;
+ }
+ }
+ if (totalEntries < 1) {
+ return;
+ }
+ int remainingEntries = totalEntries;
+ /* Limited to 100 entries in each section */
+ int entriesThisSection = Math.min(remainingEntries, 100);
+ int remainingEntriesThisSection = entriesThisSection;
+ p.append(entriesThisSection + " beginbfchar\n");
+ for (int i = 0; i < charArray.length; i++) {
+ if (partOfRange(charArray, i)) {
+ continue;
+ }
+ p.append("<" + padHexString(Integer.toHexString(i), 4)
+ + "> ");
+ p.append("<" + padHexString(Integer.toHexString(charArray[i]), 4)
+ + ">\n");
+ /* Compute the statistics. */
+ completedEntries ++;
+ remainingEntries = totalEntries - completedEntries;
+ remainingEntriesThisSection --;
+ if (remainingEntriesThisSection < 1) {
+ if (remainingEntries > 0) {
+ p.append("endbfchar\n");
+ entriesThisSection = Math.min(remainingEntries, 100);
+ remainingEntriesThisSection = entriesThisSection;
+ p.append(entriesThisSection + " beginbfchar\n");
+ }
+ }
+ }
+ p.append("endbfchar\n");
+ }
+
+ protected void writeBFRangeEntries(StringBuffer p, char[] charArray) {
+ int completedEntries = 0;
+ int totalEntries = 0;
+ for (int i = 0; i < charArray.length; i++) {
+ if (startOfRange(charArray, i)) {
+ totalEntries ++;
+ }
+ }
+ if (totalEntries < 1) {
+ return;
+ }
+ int remainingEntries = totalEntries;
+ int entriesThisSection = Math.min(remainingEntries, 100);
+ int remainingEntriesThisSection = entriesThisSection;
+ p.append(entriesThisSection + " beginbfrange\n");
+ for (int i = 0; i < charArray.length; i++) {
+ if (! startOfRange(charArray, i)) {
+ continue;
+ }
+ p.append("<"
+ + padHexString(Integer.toHexString(i), 4)
+ + "> ");
+ p.append("<"
+ + padHexString(Integer.toHexString
+ (endOfRange(charArray, i)), 4)
+ + "> ");
+ p.append("<"
+ + padHexString(Integer.toHexString(charArray[i]), 4)
+ + ">\n");
+ /* Compute the statistics. */
+ completedEntries ++;
+ remainingEntries = totalEntries - completedEntries;
+ if (remainingEntriesThisSection < 1) {
+ if (remainingEntries > 0) {
+ p.append("endbfrange\n");
+ entriesThisSection = Math.min(remainingEntries, 100);
+ remainingEntriesThisSection = entriesThisSection;
+ p.append(entriesThisSection + " beginbfrange\n");
+ }
+ }
+ }
+ p.append("endbfrange\n");
+ }
+
+ /**
+ * Find the end of the current range.
+ * @param charArray The array which is being tested.
+ * @param startOfRange The index to the array element that is the start of
+ * the range.
+ * @return The index to the element that is the end of the range.
+ */
+ private int endOfRange(char[] charArray, int startOfRange) {
+ int endOfRange = -1;
+ for (int i = startOfRange; i < charArray.length - 1 && endOfRange < 0;
+ i++) {
+ if (! sameRangeEntryAsNext(charArray, i)) {
+ endOfRange = i;
+ }
+ }
+ return endOfRange;
+ }
+
+ /**
+ * Determine whether this array element should be part of a bfchar entry or
+ * a bfrange entry.
+ * @param charArray The array to be tested.
+ * @param arrayIndex The index to the array element to be tested.
+ * @return True if this array element should be included in a range.
+ */
+ private boolean partOfRange(char[] charArray, int arrayIndex) {
+ if (charArray.length < 2) {
+ return false;
+ }
+ if (arrayIndex == 0) {
+ return sameRangeEntryAsNext(charArray, 0);
+ }
+ if (arrayIndex == charArray.length - 1) {
+ return sameRangeEntryAsNext(charArray, arrayIndex - 1);
+ }
+ if (sameRangeEntryAsNext(charArray, arrayIndex - 1)) {
+ return true;
+ }
+ if (sameRangeEntryAsNext(charArray, arrayIndex)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether two bytes can be written in the same bfrange entry.
+ * @param charArray The array to be tested.
+ * @param firstItem The first of the two items in the array to be tested.
+ * The second item is firstItem + 1.
+ * @return True if both 1) the next item in the array is sequential with
+ * this one, and 2) the first byte of the character in the first position
+ * is equal to the first byte of the character in the second position.
+ */
+ private boolean sameRangeEntryAsNext(char[] charArray, int firstItem) {
+ if (charArray[firstItem] + 1 != charArray[firstItem + 1]) {
+ return false;
+ }
+ if (firstItem / 256 != (firstItem + 1) / 256) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Determine whether this array element should be the start of a bfrange
+ * entry.
+ * @param charArray The array to be tested.
+ * @param arrayIndex The index to the array element to be tested.
+ * @return True if this array element is the beginning of a range.
+ */
+ private boolean startOfRange(char[] charArray, int arrayIndex) {
+ // Can't be the start of a range if not part of a range.
+ if (! partOfRange(charArray, arrayIndex)) {
+ return false;
+ }
+ // If first element in the array, must be start of a range
+ if (arrayIndex == 0) {
+ return true;
+ }
+ // If last element in the array, cannot be start of a range
+ if (arrayIndex == charArray.length - 1) {
+ return false;
+ }
+ /*
+ * If part of same range as the previous element is, cannot be start
+ * of range.
+ */
+ if (sameRangeEntryAsNext(charArray, arrayIndex - 1)) {
+ return false;
+ }
+ // Otherwise, this is start of a range.
+ return true;
+ }
+
+ /**
+ * Prepends the input string with a sufficient number of "0" characters to
+ * get the returned string to be numChars length.
+ * @param input The input string.
+ * @param numChars The minimum characters in the output string.
+ * @return The padded string.
+ */
+ public static String padHexString(String input, int numChars) {
+ int length = input.length();
+ if (length >= numChars) {
+ return input;
+ }
+ StringBuffer returnString = new StringBuffer();
+ for (int i = 1; i <= numChars - length; i++) {
+ returnString.append("0");
+ }
+ returnString.append(input);
+ return returnString.toString();
+ }
+
+}
\ No newline at end of file