1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.fop.render.ps; 18 19 import java.io.IOException; 20 import java.text.DecimalFormat; 21 import java.text.DecimalFormatSymbols; 22 import java.text.NumberFormat; 23 import java.util.Collection; 24 import java.util.Locale; 25 import java.util.Map; 26 27 import org.apache.fontbox.cff.CFFCIDFont; 28 import org.apache.fontbox.cff.CFFFont; 29 import org.apache.fontbox.cff.CFFType1Font; 30 import org.apache.fontbox.cff.DataOutput; 31 import org.apache.fontbox.cff.Type1FontUtil; 32 33 /** 34 * This class represents a formatter for a given Type1 font. 35 * author Villu Ruusmann 36 * @version $Revision: 1.0 $ 37 */ 38 public final class Type1FontFormatter { 39 private Map<Integer, Integer> gids; 40 Type1FontFormatter(Map<Integer, Integer> gids)41 public Type1FontFormatter(Map<Integer, Integer> gids) { 42 this.gids = gids; 43 } 44 45 /** 46 * Read and convert a given CFFFont. 47 * @param font the given CFFFont 48 * @param i 49 * @return the Type1 font 50 * @throws IOException if an error occurs during reading the given font 51 */ format(CFFFont font, String i)52 public byte[] format(CFFFont font, String i) throws IOException { 53 DataOutput output = new DataOutput(); 54 printFont(font, output, i); 55 return output.getBytes(); 56 } 57 printFont(CFFFont font, DataOutput output, String iStr)58 private void printFont(CFFFont font, DataOutput output, String iStr) 59 throws IOException { 60 output.println("%!FontType1-1.0 " + font.getName() + iStr + " " 61 + font.getTopDict().get("version")); 62 63 printFontDictionary(font, output, iStr); 64 65 for (int i = 0; i < 8; i++) { 66 StringBuilder sb = new StringBuilder(); 67 68 for (int j = 0; j < 64; j++) { 69 sb.append("0"); 70 } 71 72 output.println(sb.toString()); 73 } 74 75 output.println("cleartomark"); 76 } 77 printFontDictionary(CFFFont font, DataOutput output, String iStr)78 private void printFontDictionary(CFFFont font, DataOutput output, String iStr) 79 throws IOException { 80 output.println("10 dict begin"); 81 output.println("/FontInfo 10 dict dup begin"); 82 output.println("/version (" + font.getTopDict().get("version") 83 + ") readonly def"); 84 output.println("/Notice (" + font.getTopDict().get("Notice") 85 + ") readonly def"); 86 output.println("/FullName (" + font.getTopDict().get("FullName") 87 + ") readonly def"); 88 output.println("/FamilyName (" + font.getTopDict().get("FamilyName") 89 + ") readonly def"); 90 output.println("/Weight (" + font.getTopDict().get("Weight") 91 + ") readonly def"); 92 output.println("/ItalicAngle " + font.getTopDict().get("ItalicAngle") 93 + " def"); 94 output.println("/isFixedPitch " + font.getTopDict().get("isFixedPitch") 95 + " def"); 96 output.println("/UnderlinePosition " 97 + font.getTopDict().get("UnderlinePosition") + " def"); 98 output.println("/UnderlineThickness " 99 + font.getTopDict().get("UnderlineThickness") + " def"); 100 output.println("end readonly def"); 101 output.println("/FontName /" + font.getName() + iStr + " def"); 102 output.println("/PaintType " + font.getTopDict().get("PaintType") + " def"); 103 output.println("/FontType 1 def"); 104 NumberFormat matrixFormat = new DecimalFormat("0.########", new DecimalFormatSymbols(Locale.US)); 105 output.println("/FontMatrix " 106 + formatArray(font.getTopDict().get("FontMatrix"), matrixFormat, false) 107 + " readonly def"); 108 output.println("/FontBBox " 109 + formatArray(font.getTopDict().get("FontBBox"), false) 110 + " readonly def"); 111 output.println("/StrokeWidth " + font.getTopDict().get("StrokeWidth") 112 + " def"); 113 114 int max = 0; 115 StringBuilder sb = new StringBuilder(); 116 for (Map.Entry<Integer, Integer> gid : gids.entrySet()) { 117 String name = "gid_" + gid.getKey(); 118 if (gid.getKey() == 0) { 119 name = ".notdef"; 120 } 121 if (font instanceof CFFType1Font) { 122 name = font.getCharset().getNameForGID(gid.getKey()); 123 } 124 sb.append(String.format("dup %d /%s put", gid.getValue(), name)).append('\n'); 125 max = Math.max(max, gid.getValue()); 126 } 127 output.println("/Encoding " + (max + 1) + " array"); 128 output.println("0 1 " + max + " {1 index exch /.notdef put} for"); 129 output.print(sb.toString()); 130 output.println("readonly def"); 131 132 output.println("currentdict end"); 133 134 DataOutput eexecOutput = new DataOutput(); 135 136 printEexecFontDictionary(font, eexecOutput); 137 138 output.println("currentfile eexec"); 139 140 byte[] eexecBytes = Type1FontUtil.eexecEncrypt(eexecOutput.getBytes()); 141 output.write(eexecBytes); 142 } 143 printEexecFontDictionary(CFFFont font, DataOutput output)144 private void printEexecFontDictionary(CFFFont font, DataOutput output) 145 throws IOException { 146 output.println("dup /Private 15 dict dup begin"); 147 output.println("/RD {string currentfile exch readstring pop} executeonly def"); 148 output.println("/ND {noaccess def} executeonly def"); 149 output.println("/NP {noaccess put} executeonly def"); 150 Map<String, Object> privDict; 151 if (font instanceof CFFCIDFont) { 152 privDict = ((CFFCIDFont)font).getPrivDicts().get(0); 153 } else { 154 privDict = ((CFFType1Font)font).getPrivateDict(); 155 } 156 output.println("/BlueValues " 157 + formatArray(privDict.get("BlueValues"), true) + " ND"); 158 output.println("/OtherBlues " 159 + formatArray(privDict.get("OtherBlues"), true) + " ND"); 160 output.println("/BlueScale " + privDict.get("BlueScale") + " def"); 161 output.println("/BlueShift " + privDict.get("BlueShift") + " def"); 162 output.println("/BlueFuzz " + privDict.get("BlueFuzz") + " def"); 163 output.println("/StdHW " + formatArray(privDict.get("StdHW"), true) 164 + " ND"); 165 output.println("/StdVW " + formatArray(privDict.get("StdVW"), true) 166 + " ND"); 167 output.println("/ForceBold " + privDict.get("ForceBold") + " def"); 168 output.println("/MinFeature {16 16} def"); 169 output.println("/password 5839 def"); 170 171 output.println("2 index /CharStrings " + gids.size() + " dict dup begin"); 172 Type1CharStringFormatter formatter = new Type1CharStringFormatter(); 173 for (int gid : gids.keySet()) { 174 String mapping = "gid_" + gid; 175 if (gid == 0) { 176 mapping = ".notdef"; 177 } 178 byte[] type1Bytes; 179 if (font instanceof CFFCIDFont) { 180 int cid = font.getCharset().getCIDForGID(gid); 181 type1Bytes = formatter.format(((CFFCIDFont)font).getType2CharString(cid).getType1Sequence()); 182 } else { 183 mapping = font.getCharset().getNameForGID(gid); 184 type1Bytes = formatter.format(((CFFType1Font)font).getType1CharString(mapping).getType1Sequence()); 185 } 186 byte[] charstringBytes = Type1FontUtil.charstringEncrypt(type1Bytes, 4); 187 output.print("/" + mapping + " " + charstringBytes.length + " RD "); 188 output.write(charstringBytes); 189 output.print(" ND"); 190 output.println(); 191 } 192 193 output.println("end"); 194 output.println("end"); 195 196 output.println("readonly put"); 197 output.println("noaccess put"); 198 output.println("dup /FontName get exch definefont pop"); 199 output.println("mark currentfile closefile"); 200 } 201 formatArray(Object object, boolean executable)202 private static String formatArray(Object object, boolean executable) { 203 return formatArray(object, null, executable); 204 } 205 formatArray(Object object, NumberFormat format, boolean executable)206 private static String formatArray(Object object, NumberFormat format, boolean executable) { 207 StringBuffer sb = new StringBuffer(); 208 209 sb.append(executable ? "{" : "["); 210 211 if (object instanceof Collection) { 212 String sep = ""; 213 214 Collection<?> elements = (Collection<?>) object; 215 for (Object element : elements) { 216 sb.append(sep).append(formatElement(element, format)); 217 218 sep = " "; 219 } 220 } else if (object instanceof Number) { 221 sb.append(formatElement(object, format)); 222 } 223 224 sb.append(executable ? "}" : "]"); 225 226 return sb.toString(); 227 } 228 formatElement(Object object, NumberFormat format)229 private static String formatElement(Object object, NumberFormat format) { 230 if (format != null) { 231 if (object instanceof Double || object instanceof Float) { 232 Number number = (Number)object; 233 return format.format(number.doubleValue()); 234 } else if (object instanceof Long || object instanceof Integer) { 235 Number number = (Number)object; 236 return format.format(number.longValue()); 237 } 238 } 239 return String.valueOf(object); 240 } 241 } 242