blob: 776d410f93f02506dc235fad22fef64e5bc99979 [file] [log] [blame]
/*
* 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.IOException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Collection;
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.Type1FontUtil;
/**
* This class represents a formatter for a given Type1 font.
* author Villu Ruusmann
* @version $Revision: 1.0 $
*/
public final class Type1FontFormatter {
private Map<Integer, Integer> gids;
public Type1FontFormatter(Map<Integer, Integer> gids) {
this.gids = gids;
}
/**
* Read and convert a given CFFFont.
* @param font the given CFFFont
* @param i
* @return the Type1 font
* @throws IOException if an error occurs during reading the given font
*/
public byte[] format(CFFFont font, String i) throws IOException {
DataOutput output = new DataOutput();
printFont(font, output, i);
return output.getBytes();
}
private void printFont(CFFFont font, DataOutput output, String iStr)
throws IOException {
output.println("%!FontType1-1.0 " + font.getName() + iStr + " "
+ font.getTopDict().get("version"));
printFontDictionary(font, output, iStr);
for (int i = 0; i < 8; i++) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < 64; j++) {
sb.append("0");
}
output.println(sb.toString());
}
output.println("cleartomark");
}
private void printFontDictionary(CFFFont font, DataOutput output, String iStr)
throws IOException {
output.println("10 dict begin");
output.println("/FontInfo 10 dict dup begin");
output.println("/version (" + font.getTopDict().get("version")
+ ") readonly def");
output.println("/Notice (" + font.getTopDict().get("Notice")
+ ") readonly def");
output.println("/FullName (" + font.getTopDict().get("FullName")
+ ") readonly def");
output.println("/FamilyName (" + font.getTopDict().get("FamilyName")
+ ") readonly def");
output.println("/Weight (" + font.getTopDict().get("Weight")
+ ") readonly def");
output.println("/ItalicAngle " + font.getTopDict().get("ItalicAngle")
+ " def");
output.println("/isFixedPitch " + font.getTopDict().get("isFixedPitch")
+ " def");
output.println("/UnderlinePosition "
+ font.getTopDict().get("UnderlinePosition") + " def");
output.println("/UnderlineThickness "
+ font.getTopDict().get("UnderlineThickness") + " def");
output.println("end readonly def");
output.println("/FontName /" + font.getName() + iStr + " def");
output.println("/PaintType " + font.getTopDict().get("PaintType") + " def");
output.println("/FontType 1 def");
NumberFormat matrixFormat = new DecimalFormat("0.########", new DecimalFormatSymbols(Locale.US));
output.println("/FontMatrix "
+ formatArray(font.getTopDict().get("FontMatrix"), matrixFormat, false)
+ " readonly def");
output.println("/FontBBox "
+ formatArray(font.getTopDict().get("FontBBox"), false)
+ " readonly def");
output.println("/StrokeWidth " + font.getTopDict().get("StrokeWidth")
+ " def");
int max = 0;
StringBuilder sb = new StringBuilder();
for (Map.Entry<Integer, Integer> gid : gids.entrySet()) {
String name = "gid_" + gid.getKey();
if (gid.getKey() == 0) {
name = ".notdef";
}
if (font instanceof CFFType1Font) {
name = font.getCharset().getNameForGID(gid.getKey());
}
sb.append(String.format("dup %d /%s put", gid.getValue(), name)).append('\n');
max = Math.max(max, gid.getValue());
}
output.println("/Encoding " + (max + 1) + " array");
output.println("0 1 " + max + " {1 index exch /.notdef put} for");
output.print(sb.toString());
output.println("readonly def");
output.println("currentdict end");
DataOutput eexecOutput = new DataOutput();
printEexecFontDictionary(font, eexecOutput);
output.println("currentfile eexec");
byte[] eexecBytes = Type1FontUtil.eexecEncrypt(eexecOutput.getBytes());
output.write(eexecBytes);
}
private void printEexecFontDictionary(CFFFont font, DataOutput output)
throws IOException {
output.println("dup /Private 15 dict dup begin");
output.println("/RD {string currentfile exch readstring pop} executeonly def");
output.println("/ND {noaccess def} executeonly def");
output.println("/NP {noaccess put} executeonly def");
Map<String, Object> privDict;
if (font instanceof CFFCIDFont) {
privDict = ((CFFCIDFont)font).getPrivDicts().get(0);
} else {
privDict = ((CFFType1Font)font).getPrivateDict();
}
output.println("/BlueValues "
+ formatArray(privDict.get("BlueValues"), true) + " ND");
output.println("/OtherBlues "
+ formatArray(privDict.get("OtherBlues"), true) + " ND");
output.println("/BlueScale " + privDict.get("BlueScale") + " def");
output.println("/BlueShift " + privDict.get("BlueShift") + " def");
output.println("/BlueFuzz " + privDict.get("BlueFuzz") + " def");
output.println("/StdHW " + formatArray(privDict.get("StdHW"), true)
+ " ND");
output.println("/StdVW " + formatArray(privDict.get("StdVW"), true)
+ " ND");
output.println("/ForceBold " + privDict.get("ForceBold") + " def");
output.println("/MinFeature {16 16} def");
output.println("/password 5839 def");
output.println("2 index /CharStrings " + gids.size() + " dict dup begin");
Type1CharStringFormatter formatter = new Type1CharStringFormatter();
for (int gid : gids.keySet()) {
String mapping = "gid_" + gid;
if (gid == 0) {
mapping = ".notdef";
}
byte[] type1Bytes;
if (font instanceof CFFCIDFont) {
int cid = font.getCharset().getCIDForGID(gid);
type1Bytes = formatter.format(((CFFCIDFont)font).getType2CharString(cid).getType1Sequence());
} else {
mapping = font.getCharset().getNameForGID(gid);
type1Bytes = formatter.format(((CFFType1Font)font).getType1CharString(mapping).getType1Sequence());
}
byte[] charstringBytes = Type1FontUtil.charstringEncrypt(type1Bytes, 4);
output.print("/" + mapping + " " + charstringBytes.length + " RD ");
output.write(charstringBytes);
output.print(" ND");
output.println();
}
output.println("end");
output.println("end");
output.println("readonly put");
output.println("noaccess put");
output.println("dup /FontName get exch definefont pop");
output.println("mark currentfile closefile");
}
private static String formatArray(Object object, boolean executable) {
return formatArray(object, null, executable);
}
private static String formatArray(Object object, NumberFormat format, boolean executable) {
StringBuffer sb = new StringBuffer();
sb.append(executable ? "{" : "[");
if (object instanceof Collection) {
String sep = "";
Collection<?> elements = (Collection<?>) object;
for (Object element : elements) {
sb.append(sep).append(formatElement(element, format));
sep = " ";
}
} else if (object instanceof Number) {
sb.append(formatElement(object, format));
}
sb.append(executable ? "}" : "]");
return sb.toString();
}
private static String formatElement(Object object, NumberFormat format) {
if (format != null) {
if (object instanceof Double || object instanceof Float) {
Number number = (Number)object;
return format.format(number.doubleValue());
} else if (object instanceof Long || object instanceof Integer) {
Number number = (Number)object;
return format.format(number.longValue());
}
}
return String.valueOf(object);
}
}