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.pdfbox.pdmodel.font; 18 19 import java.io.BufferedInputStream; 20 import java.io.FileInputStream; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.util.ArrayList; 24 import java.util.Iterator; 25 import java.util.List; 26 27 import org.apache.fontbox.afm.AFMParser; 28 import org.apache.fontbox.afm.CharMetric; 29 import org.apache.fontbox.afm.FontMetric; 30 31 import org.apache.fontbox.pfb.PfbParser; 32 33 import org.apache.pdfbox.encoding.AFMEncoding; 34 import org.apache.pdfbox.encoding.DictionaryEncoding; 35 import org.apache.pdfbox.encoding.Encoding; 36 import org.apache.pdfbox.pdmodel.PDDocument; 37 import org.apache.pdfbox.pdmodel.common.PDRectangle; 38 import org.apache.pdfbox.pdmodel.common.PDStream; 39 import org.apache.pdfbox.cos.COSArray; 40 import org.apache.pdfbox.cos.COSDictionary; 41 import org.apache.pdfbox.cos.COSInteger; 42 import org.apache.pdfbox.cos.COSName; 43 44 /** 45 * This is implementation of the Type1 Font 46 * with a afm and a pfb file. 47 * 48 * @author <a href="mailto:m.g.n@gmx.de">Michael Niedermair</a> 49 * @version $Revision: 1.5 $ 50 */ 51 public class PDType1AfmPfbFont extends PDType1Font 52 { 53 /** 54 * the buffersize. 55 */ 56 private static final int BUFFERSIZE = 0xffff; 57 58 /** 59 * The font descriptor. 60 */ 61 private PDFontDescriptorDictionary fd; 62 63 /** 64 * The font metric. 65 */ 66 private FontMetric metric; 67 68 /** 69 * Create a new object. 70 * @param doc The PDF document that will hold the embedded font. 71 * @param afmname The font filename. 72 * @throws IOException If there is an error loading the data. 73 */ 74 public PDType1AfmPfbFont(final PDDocument doc, final String afmname) 75 throws IOException 76 { 77 78 super(); 79 InputStream afmin = new BufferedInputStream(new FileInputStream(afmname), BUFFERSIZE); 80 String pfbname = afmname.replaceAll(".AFM", "").replaceAll(".afm", "") + ".pfb"; 81 InputStream pfbin = new BufferedInputStream(new FileInputStream(pfbname), BUFFERSIZE); 82 load(doc, afmin, pfbin); 83 } 84 85 /** 86 * Create a new object. 87 * @param doc The PDF document that will hold the embedded font. 88 * @param afm The afm input. 89 * @param pfb The pfb input. 90 * @throws IOException If there is an error loading the data. 91 */ 92 public PDType1AfmPfbFont(final PDDocument doc, final InputStream afm, final InputStream pfb) 93 throws IOException 94 { 95 super(); 96 load(doc, afm, pfb); 97 } 98 99 /** 100 * This will load a afm and pfb to be embedding into a document. 101 * 102 * @param doc The PDF document that will hold the embedded font. 103 * @param afm The afm input. 104 * @param pfb The pfb input. 105 * @throws IOException If there is an error loading the data. 106 */ 107 private void load(final PDDocument doc, final InputStream afm, 108 final InputStream pfb) throws IOException 109 { 110 111 fd = new PDFontDescriptorDictionary(); 112 setFontDescriptor(fd); 113 114 // read the pfb 115 PfbParser pfbparser = new PfbParser(pfb); 116 pfb.close(); 117 118 PDStream fontStream = new PDStream(doc, pfbparser.getInputStream(), 119 false); 120 fontStream.getStream().setInt("Length", pfbparser.size()); 121 for (int i = 0; i < pfbparser.getLengths().length; i++) 122 { 123 fontStream.getStream().setInt("Length" + (i + 1), 124 pfbparser.getLengths()[i]); 125 } 126 fontStream.addCompression(); 127 fd.setFontFile(fontStream); 128 129 // read the afm 130 AFMParser parser = new AFMParser(afm); 131 parser.parse(); 132 metric = parser.getResult(); 133 setEncoding(afmToDictionary(new AFMEncoding(metric))); 134 135 // set the values 136 setBaseFont(metric.getFontName()); 137 fd.setFontName(metric.getFontName()); 138 fd.setFontFamily(metric.getFamilyName()); 139 fd.setNonSymbolic(true); 140 fd.setFontBoundingBox(new PDRectangle(metric.getFontBBox())); 141 fd.setItalicAngle(metric.getItalicAngle()); 142 fd.setAscent(metric.getAscender()); 143 fd.setDescent(metric.getDescender()); 144 fd.setCapHeight(metric.getCapHeight()); 145 fd.setXHeight(metric.getXHeight()); 146 fd.setAverageWidth(metric.getAverageCharacterWidth()); 147 fd.setCharacterSet(metric.getCharacterSet()); 148 149 // get firstchar, lastchar 150 int firstchar = 255; 151 int lastchar = 0; 152 153 // widths 154 List listmetric = metric.getCharMetrics(); 155 Encoding encoding = getEncoding(); 156 int maxWidths = 256; 157 List widths = new ArrayList(maxWidths); 158 Integer zero = new Integer(250); 159 Iterator iter = listmetric.iterator(); 160 for( int i=0; i<maxWidths; i++ ) 161 { 162 widths.add(zero); 163 } 164 while (iter.hasNext()) 165 { 166 CharMetric m = (CharMetric) iter.next(); 167 int n = m.getCharacterCode(); 168 if (n > 0) 169 { 170 firstchar = Math.min(firstchar, n); 171 lastchar = Math.max(lastchar, n); 172 if (m.getWx() > 0) 173 { 174 float width = m.getWx(); 175 widths.set(n,new Float(width)); 176 // germandbls has 2 character codes !! Don't ask me why ..... 177 // StandardEncoding = 0373 = 251 178 // WinANSIEncoding = 0337 = 223 179 if (m.getName().equals("germandbls") && n != 223) 180 { 181 widths.set(0337,new Float(width)); 182 } 183 } 184 } 185 else 186 { 187 // my AFMPFB-Fonts has no character-codes for german umlauts 188 // so that I've to add them here by hand 189 if (m.getName().equals("adieresis")) 190 { 191 widths.set(0344,(Float)widths.get(encoding.getCode(COSName.getPDFName( "a" )))); 192 } 193 else if (m.getName().equals("odieresis")) 194 { 195 widths.set(0366,(Float)widths.get(encoding.getCode(COSName.getPDFName( "o" )))); 196 } 197 else if (m.getName().equals("udieresis")) 198 { 199 widths.set(0374,(Float)widths.get(encoding.getCode(COSName.getPDFName( "u" )))); 200 } 201 else if (m.getName().equals("Adieresis")) 202 { 203 widths.set(0304,(Float)widths.get(encoding.getCode(COSName.getPDFName( "A" )))); 204 } 205 else if (m.getName().equals("Odieresis")) 206 { 207 widths.set(0326,(Float)widths.get(encoding.getCode(COSName.getPDFName( "O" )))); 208 } 209 else if (m.getName().equals("Udieresis")) 210 { 211 widths.set(0334,(Float)widths.get(encoding.getCode(COSName.getPDFName( "U" )))); 212 } 213 } 214 } 215 setFirstChar(0); 216 setLastChar(255); 217 setWidths(widths); 218 } 219 220 /* 221 * This will generate a Encoding from the AFM-Encoding, because the AFM-Enconding isn't exported to the pdf 222 * and consequently the StandardEncoding is used so that any special character is missing 223 * I've copied the code from the pdfbox-forum posted by V0JT4 and made some additions concerning german umlauts 224 * see also https://sourceforge.net/forum/message.php?msg_id=4705274 225 */ 226 private DictionaryEncoding afmToDictionary(AFMEncoding encoding) throws java.io.IOException 227 { 228 COSArray array = new COSArray(); 229 array.add(COSInteger.ZERO); 230 for (int i = 0; i < 256; i++) 231 { 232 array.add(encoding.getName(i)); 233 } 234 // my AFMPFB-Fonts has no character-codes for german umlauts 235 // so that I've to add them here by hand 236 array.set( 0337+1, COSName.getPDFName("germandbls")); 237 array.set( 0344+1, COSName.getPDFName("adieresis")); 238 array.set( 0366+1, COSName.getPDFName("odieresis")); 239 array.set( 0374+1, COSName.getPDFName("udieresis")); 240 array.set( 0304+1, COSName.getPDFName("Adieresis")); 241 array.set( 0326+1, COSName.getPDFName("Odieresis")); 242 array.set( 0334+1, COSName.getPDFName("Udieresis")); 243 244 COSDictionary dictionary = new COSDictionary(); 245 dictionary.setItem(COSName.NAME, COSName.ENCODING); 246 dictionary.setItem(COSName.DIFFERENCES, array); 247 dictionary.setItem(COSName.BASE_ENCODING, COSName.STANDARD_ENCODING); 248 return new DictionaryEncoding(dictionary); 249 } 250 251 252 253 /** 254 * {@inheritDoc} 255 */ 256 public PDFontDescriptor getFontDescriptor() throws IOException 257 { 258 return fd; 259 } 260 261 }