1 50 51 package com.lowagie.text.pdf; 52 53 import java.io.IOException ; 54 import java.io.InputStream ; 55 import java.util.Enumeration ; 56 import java.util.HashMap ; 57 import java.util.Hashtable ; 58 import java.util.Properties ; 59 import java.util.StringTokenizer ; 60 61 import com.lowagie.text.DocumentException; 62 63 68 69 class CJKFont extends BaseFont { 70 72 static final String CJK_ENCODING = "UnicodeBigUnmarked"; 73 private static final int FIRST = 0; 74 private static final int BRACKET = 1; 75 private static final int SERIAL = 2; 76 private static final int V1Y = 880; 77 78 static Properties cjkFonts = new Properties (); 79 static Properties cjkEncodings = new Properties (); 80 static Hashtable allCMaps = new Hashtable (); 81 static Hashtable allFonts = new Hashtable (); 82 private static boolean propertiesLoaded = false; 83 84 85 private String fontName; 86 87 private String style = ""; 88 89 private String CMap; 90 91 private boolean cidDirect = false; 92 93 private char[] translationMap; 94 private IntHashtable vMetrics; 95 private IntHashtable hMetrics; 96 private HashMap fontDesc; 97 private boolean vertical = false; 98 99 private static void loadProperties() { 100 if (propertiesLoaded) 101 return; 102 synchronized (allFonts) { 103 if (propertiesLoaded) 104 return; 105 try { 106 InputStream is = getResourceStream(RESOURCE_PATH + "cjkfonts.properties"); 107 cjkFonts.load(is); 108 is.close(); 109 is = getResourceStream(RESOURCE_PATH + "cjkencodings.properties"); 110 cjkEncodings.load(is); 111 is.close(); 112 } 113 catch (Exception e) { 114 cjkFonts = new Properties (); 115 cjkEncodings = new Properties (); 116 } 117 propertiesLoaded = true; 118 } 119 } 120 121 127 CJKFont(String fontName, String enc, boolean emb) throws DocumentException { 128 loadProperties(); 129 fontType = FONT_TYPE_CJK; 130 String nameBase = getBaseName(fontName); 131 if (!isCJKFont(nameBase, enc)) 132 throw new DocumentException("Font '" + fontName + "' with '" + enc + "' encoding is not a CJK font."); 133 if (nameBase.length() < fontName.length()) { 134 style = fontName.substring(nameBase.length()); 135 fontName = nameBase; 136 } 137 this.fontName = fontName; 138 encoding = CJK_ENCODING; 139 vertical = enc.endsWith("V"); 140 CMap = enc; 141 if (enc.startsWith("Identity-")) { 142 cidDirect = true; 143 String s = cjkFonts.getProperty(fontName); 144 s = s.substring(0, s.indexOf('_')); 145 char c[] = (char[])allCMaps.get(s); 146 if (c == null) { 147 c = readCMap(s); 148 if (c == null) 149 throw new DocumentException("The cmap " + s + " does not exist as a resource."); 150 c[CID_NEWLINE] = '\n'; 151 allCMaps.put(s, c); 152 } 153 translationMap = c; 154 } 155 else { 156 char c[] = (char[])allCMaps.get(enc); 157 if (c == null) { 158 String s = cjkEncodings.getProperty(enc); 159 if (s == null) 160 throw new DocumentException("The resource cjkencodings.properties does not contain the encoding " + enc); 161 StringTokenizer tk = new StringTokenizer (s); 162 String nt = tk.nextToken(); 163 c = (char[])allCMaps.get(nt); 164 if (c == null) { 165 c = readCMap(nt); 166 allCMaps.put(nt, c); 167 } 168 if (tk.hasMoreTokens()) { 169 String nt2 = tk.nextToken(); 170 char m2[] = readCMap(nt2); 171 for (int k = 0; k < 0x10000; ++k) { 172 if (m2[k] == 0) 173 m2[k] = c[k]; 174 } 175 allCMaps.put(enc, m2); 176 c = m2; 177 } 178 } 179 translationMap = c; 180 } 181 fontDesc = (HashMap )allFonts.get(fontName); 182 if (fontDesc == null) { 183 fontDesc = readFontProperties(fontName); 184 allFonts.put(fontName, fontDesc); 185 } 186 hMetrics = (IntHashtable)fontDesc.get("W"); 187 vMetrics = (IntHashtable)fontDesc.get("W2"); 188 } 189 190 195 public static boolean isCJKFont(String fontName, String enc) { 196 loadProperties(); 197 String encodings = cjkFonts.getProperty(fontName); 198 return (encodings != null && (enc.equals("Identity-H") || enc.equals("Identity-V") || encodings.indexOf("_" + enc + "_") >= 0)); 199 } 200 201 public int getWidth(String text) { 202 int total = 0; 203 for (int k = 0; k < text.length(); ++k) { 204 int c = text.charAt(k); 205 if (!cidDirect) 206 c = translationMap[c]; 207 int v; 208 if (vertical) 209 v = vMetrics.get(c); 210 else 211 v = hMetrics.get(c); 212 if (v > 0) 213 total += v; 214 else 215 total += 1000; 216 } 217 return total; 218 } 219 220 int getRawWidth(int c, String name) { 221 return 0; 222 } 223 224 public int getKerning(char char1, char char2) { 225 return 0; 226 } 227 228 private PdfDictionary getFontDescriptor() { 229 PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR); 230 dic.put(PdfName.ASCENT, new PdfLiteral((String )fontDesc.get("Ascent"))); 231 dic.put(PdfName.CAPHEIGHT, new PdfLiteral((String )fontDesc.get("CapHeight"))); 232 dic.put(PdfName.DESCENT, new PdfLiteral((String )fontDesc.get("Descent"))); 233 dic.put(PdfName.FLAGS, new PdfLiteral((String )fontDesc.get("Flags"))); 234 dic.put(PdfName.FONTBBOX, new PdfLiteral((String )fontDesc.get("FontBBox"))); 235 dic.put(PdfName.FONTNAME, new PdfName(fontName + style)); 236 dic.put(PdfName.ITALICANGLE, new PdfLiteral((String )fontDesc.get("ItalicAngle"))); 237 dic.put(PdfName.STEMV, new PdfLiteral((String )fontDesc.get("StemV"))); 238 PdfDictionary pdic = new PdfDictionary(); 239 pdic.put(PdfName.PANOSE, new PdfString((String )fontDesc.get("Panose"), null)); 240 dic.put(PdfName.STYLE, pdic); 241 return dic; 242 } 243 244 private PdfDictionary getCIDFont(PdfIndirectReference fontDescriptor, IntHashtable cjkTag) { 245 PdfDictionary dic = new PdfDictionary(PdfName.FONT); 246 dic.put(PdfName.SUBTYPE, PdfName.CIDFONTTYPE0); 247 dic.put(PdfName.BASEFONT, new PdfName(fontName + style)); 248 dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor); 249 int keys[] = cjkTag.toOrderedKeys(); 250 String w = convertToHCIDMetrics(keys, hMetrics); 251 if (w != null) 252 dic.put(PdfName.W, new PdfLiteral(w)); 253 if (vertical) { 254 w = convertToVCIDMetrics(keys, vMetrics, hMetrics); 255 if (w != null) 256 dic.put(PdfName.W2, new PdfLiteral(w)); 257 } 258 else 259 dic.put(PdfName.DW, new PdfNumber(1000)); 260 PdfDictionary cdic = new PdfDictionary(); 261 cdic.put(PdfName.REGISTRY, new PdfString((String )fontDesc.get("Registry"), null)); 262 cdic.put(PdfName.ORDERING, new PdfString((String )fontDesc.get("Ordering"), null)); 263 cdic.put(PdfName.SUPPLEMENT, new PdfLiteral((String )fontDesc.get("Supplement"))); 264 dic.put(PdfName.CIDSYSTEMINFO, cdic); 265 return dic; 266 } 267 268 private PdfDictionary getFontBaseType(PdfIndirectReference CIDFont) { 269 PdfDictionary dic = new PdfDictionary(PdfName.FONT); 270 dic.put(PdfName.SUBTYPE, PdfName.TYPE0); 271 String name = fontName; 272 if (style.length() > 0) 273 name += "-" + style.substring(1); 274 name += "-" + CMap; 275 dic.put(PdfName.BASEFONT, new PdfName(name)); 276 dic.put(PdfName.ENCODING, new PdfName(CMap)); 277 dic.put(PdfName.DESCENDANTFONTS, new PdfArray(CIDFont)); 278 return dic; 279 } 280 281 void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException { 282 IntHashtable cjkTag = (IntHashtable)params[0]; 283 PdfIndirectReference ind_font = null; 284 PdfObject pobj = null; 285 PdfIndirectObject obj = null; 286 pobj = getFontDescriptor(); 287 if (pobj != null){ 288 obj = writer.addToBody(pobj); 289 ind_font = obj.getIndirectReference(); 290 } 291 pobj = getCIDFont(ind_font, cjkTag); 292 if (pobj != null){ 293 obj = writer.addToBody(pobj); 294 ind_font = obj.getIndirectReference(); 295 } 296 pobj = getFontBaseType(ind_font); 297 writer.addToBody(pobj, ref); 298 } 299 300 private float getDescNumber(String name) { 301 return Integer.parseInt((String )fontDesc.get(name)); 302 } 303 304 private float getBBox(int idx) { 305 String s = (String )fontDesc.get("FontBBox"); 306 StringTokenizer tk = new StringTokenizer (s, " []\r\n\t\f"); 307 String ret = tk.nextToken(); 308 for (int k = 0; k < idx; ++k) 309 ret = tk.nextToken(); 310 return Integer.parseInt(ret); 311 } 312 313 320 public float getFontDescriptor(int key, float fontSize) { 321 switch (key) { 322 case AWT_ASCENT: 323 case ASCENT: 324 return getDescNumber("Ascent") * fontSize / 1000; 325 case CAPHEIGHT: 326 return getDescNumber("CapHeight") * fontSize / 1000; 327 case AWT_DESCENT: 328 case DESCENT: 329 return getDescNumber("Descent") * fontSize / 1000; 330 case ITALICANGLE: 331 return getDescNumber("ItalicAngle"); 332 case BBOXLLX: 333 return fontSize * getBBox(0) / 1000; 334 case BBOXLLY: 335 return fontSize * getBBox(1) / 1000; 336 case BBOXURX: 337 return fontSize * getBBox(2) / 1000; 338 case BBOXURY: 339 return fontSize * getBBox(3) / 1000; 340 case AWT_LEADING: 341 return 0; 342 case AWT_MAXADVANCE: 343 return fontSize * (getBBox(2) - getBBox(0)) / 1000; 344 } 345 return 0; 346 } 347 348 public String getPostscriptFontName() { 349 return fontName; 350 } 351 352 360 public String [][] getFullFontName() { 361 return new String [][]{{"", "", "", fontName}}; 362 } 363 364 372 public String [][] getFamilyFontName() { 373 return getFullFontName(); 374 } 375 376 static char[] readCMap(String name) { 377 try { 378 name = name + ".cmap"; 379 InputStream is = getResourceStream(RESOURCE_PATH + name); 380 char c[] = new char[0x10000]; 381 for (int k = 0; k < 0x10000; ++k) 382 c[k] = (char)((is.read() << 8) + is.read()); 383 return c; 384 } 385 catch (Exception e) { 386 } 388 return null; 389 } 390 391 static IntHashtable createMetric(String s) { 392 IntHashtable h = new IntHashtable(); 393 StringTokenizer tk = new StringTokenizer (s); 394 while (tk.hasMoreTokens()) { 395 int n1 = Integer.parseInt(tk.nextToken()); 396 h.put(n1, Integer.parseInt(tk.nextToken())); 397 } 398 return h; 399 } 400 401 static String convertToHCIDMetrics(int keys[], IntHashtable h) { 402 if (keys.length == 0) 403 return null; 404 int lastCid = 0; 405 int lastValue = 0; 406 int start; 407 for (start = 0; start < keys.length; ++start) { 408 lastCid = keys[start]; 409 lastValue = h.get(lastCid); 410 if (lastValue != 0) { 411 ++start; 412 break; 413 } 414 } 415 if (lastValue == 0) 416 return null; 417 StringBuffer buf = new StringBuffer (); 418 buf.append('['); 419 buf.append(lastCid); 420 int state = FIRST; 421 for (int k = start; k < keys.length; ++k) { 422 int cid = keys[k]; 423 int value = h.get(cid); 424 if (value == 0) 425 continue; 426 switch (state) { 427 case FIRST: { 428 if (cid == lastCid + 1 && value == lastValue) { 429 state = SERIAL; 430 } 431 else if (cid == lastCid + 1) { 432 state = BRACKET; 433 buf.append('[').append(lastValue); 434 } 435 else { 436 buf.append('[').append(lastValue).append(']').append(cid); 437 } 438 break; 439 } 440 case BRACKET: { 441 if (cid == lastCid + 1 && value == lastValue) { 442 state = SERIAL; 443 buf.append(']').append(lastCid); 444 } 445 else if (cid == lastCid + 1) { 446 buf.append(' ').append(lastValue); 447 } 448 else { 449 state = FIRST; 450 buf.append(' ').append(lastValue).append(']').append(cid); 451 } 452 break; 453 } 454 case SERIAL: { 455 if (cid != lastCid + 1 || value != lastValue) { 456 buf.append(' ').append(lastCid).append(' ').append(lastValue).append(' ').append(cid); 457 state = FIRST; 458 } 459 break; 460 } 461 } 462 lastValue = value; 463 lastCid = cid; 464 } 465 switch (state) { 466 case FIRST: { 467 buf.append('[').append(lastValue).append("]]"); 468 break; 469 } 470 case BRACKET: { 471 buf.append(' ').append(lastValue).append("]]"); 472 break; 473 } 474 case SERIAL: { 475 buf.append(' ').append(lastCid).append(' ').append(lastValue).append(']'); 476 break; 477 } 478 } 479 return buf.toString(); 480 } 481 482 static String convertToVCIDMetrics(int keys[], IntHashtable v, IntHashtable h) { 483 if (keys.length == 0) 484 return null; 485 int lastCid = 0; 486 int lastValue = 0; 487 int lastHValue = 0; 488 int start; 489 for (start = 0; start < keys.length; ++start) { 490 lastCid = keys[start]; 491 lastValue = v.get(lastCid); 492 if (lastValue != 0) { 493 ++start; 494 break; 495 } 496 else 497 lastHValue = h.get(lastCid); 498 } 499 if (lastValue == 0) 500 return null; 501 if (lastHValue == 0) 502 lastHValue = 1000; 503 StringBuffer buf = new StringBuffer (); 504 buf.append('['); 505 buf.append(lastCid); 506 int state = FIRST; 507 for (int k = start; k < keys.length; ++k) { 508 int cid = keys[k]; 509 int value = v.get(cid); 510 if (value == 0) 511 continue; 512 int hValue = h.get(lastCid); 513 if (hValue == 0) 514 hValue = 1000; 515 switch (state) { 516 case FIRST: { 517 if (cid == lastCid + 1 && value == lastValue && hValue == lastHValue) { 518 state = SERIAL; 519 } 520 else { 521 buf.append(' ').append(lastCid).append(' ').append(-lastValue).append(' ').append(lastHValue / 2).append(' ').append(V1Y).append(' ').append(cid); 522 } 523 break; 524 } 525 case SERIAL: { 526 if (cid != lastCid + 1 || value != lastValue || hValue != lastHValue) { 527 buf.append(' ').append(lastCid).append(' ').append(-lastValue).append(' ').append(lastHValue / 2).append(' ').append(V1Y).append(' ').append(cid); 528 state = FIRST; 529 } 530 break; 531 } 532 } 533 lastValue = value; 534 lastCid = cid; 535 lastHValue = hValue; 536 } 537 buf.append(' ').append(lastCid).append(' ').append(-lastValue).append(' ').append(lastHValue / 2).append(' ').append(V1Y).append(" ]"); 538 return buf.toString(); 539 } 540 541 static HashMap readFontProperties(String name) { 542 try { 543 name += ".properties"; 544 InputStream is = getResourceStream(RESOURCE_PATH + name); 545 Properties p = new Properties (); 546 p.load(is); 547 is.close(); 548 IntHashtable W = createMetric(p.getProperty("W")); 549 p.remove("W"); 550 IntHashtable W2 = createMetric(p.getProperty("W2")); 551 p.remove("W2"); 552 HashMap map = new HashMap (); 553 for (Enumeration e = p.keys(); e.hasMoreElements();) { 554 Object obj = e.nextElement(); 555 map.put(obj, p.getProperty((String )obj)); 556 } 557 map.put("W", W); 558 map.put("W2", W2); 559 return map; 560 } 561 catch (Exception e) { 562 } 564 return null; 565 } 566 567 public char getUnicodeEquivalent(char c) { 568 if (cidDirect) 569 return translationMap[c]; 570 return c; 571 } 572 573 public char getCidCode(char c) { 574 if (cidDirect) 575 return c; 576 return translationMap[c]; 577 } 578 579 582 public boolean hasKernPairs() { 583 return false; 584 } 585 586 592 public boolean charExists(char c) { 593 return translationMap[c] != 0; 594 } 595 596 603 public boolean setCharAdvance(char c, int advance) { 604 return false; 605 } 606 607 612 public void setPostscriptFontName(String name) { 613 fontName = name; 614 } 615 616 public boolean setKerning(char char1, char char2, int kern) { 617 return false; 618 } 619 620 public int[] getCharBBox(char c) { 621 return null; 622 } 623 624 protected int[] getRawCharBBox(int c, String name) { 625 return null; 626 } 627 } | Popular Tags |