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 18 /* $Id: DoubleByteFont.java 1679676 2015-05-16 02:10:42Z adelmelle $ */ 19 20 package org.apache.fop.afp.fonts; 21 22 import java.awt.Rectangle; 23 import java.lang.Character.UnicodeBlock; 24 import java.util.HashSet; 25 import java.util.Set; 26 27 import org.apache.commons.logging.Log; 28 import org.apache.commons.logging.LogFactory; 29 30 import org.apache.fop.afp.AFPEventProducer; 31 32 /** 33 * Implementation of {@link AbstractOutlineFont} that supports double-byte fonts (CID Keyed font (Type 0)). 34 * The width of characters that are not prescribed a width metrics in the font resource use 35 * a fallback width. The default width is 1 em. A character can be supplied and queried for the 36 * fallback width of all non-ideograph characters. 37 */ 38 public class DoubleByteFont extends AbstractOutlineFont { 39 40 private static final Log log = LogFactory.getLog(DoubleByteFont.class); 41 42 private final Set<Integer> charsProcessed; 43 44 //See also http://unicode.org/reports/tr11/ which we've not closely looked at, yet 45 //TODO the Unicode block listed here is probably not complete (ex. Hiragana, Katakana etc.) 46 private static final Set<UnicodeBlock> IDEOGRAPHIC = new HashSet<UnicodeBlock>(); 47 static { 48 IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS); 49 //IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT);//Java 1.5 50 IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS); 51 IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A); 52 //IDEOGRAPHIC.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B); //Java 1.1 53 } 54 55 /** 56 * Constructor for an double-byte outline font. 57 * @param name the name of the font 58 * @param embeddable whether or not this font is embeddable 59 * @param charSet the character set 60 * @param eventProducer Handles any AFP related events 61 */ DoubleByteFont(String name, boolean embeddable, CharacterSet charSet, AFPEventProducer eventProducer)62 public DoubleByteFont(String name, boolean embeddable, CharacterSet charSet, 63 AFPEventProducer eventProducer) { 64 super(name, embeddable, charSet, eventProducer); 65 charsProcessed = new HashSet<Integer>(); 66 } 67 68 /** {@inheritDoc} */ getWidth(int character, int size)69 public int getWidth(int character, int size) { 70 int charWidth; 71 try { 72 charWidth = charSet.getWidth(toUnicodeCodepoint(character), size); 73 } catch (IllegalArgumentException e) { 74 if (!charsProcessed.contains(character)) { 75 charsProcessed.add(character); 76 getAFPEventProducer().charactersetMissingMetrics(this, (char)character, 77 charSet.getName().trim()); 78 } 79 // We shall try and handle characters that have no mapped width metric in font resource 80 charWidth = -1; 81 } 82 83 if (charWidth == -1) { 84 charWidth = getDefaultCharacterWidth(character) * size; 85 } 86 return charWidth; 87 } 88 getDefaultCharacterWidth(int character)89 private int getDefaultCharacterWidth(int character) { 90 int nominalCharIncrement = charSet.getNominalCharIncrement(); 91 if (nominalCharIncrement > 0) { 92 return nominalCharIncrement; 93 } else { 94 return inferCharWidth(character); 95 } 96 } 97 98 @Override getBoundingBox(int character, int size)99 public Rectangle getBoundingBox(int character, int size) { 100 Rectangle characterBox = getBoundingBoxOrNull(character, size); 101 if (characterBox == null) { 102 characterBox = getDefaultCharacterBox(character, size); 103 } 104 return characterBox; 105 } 106 getBoundingBoxOrNull(int character, int size)107 private Rectangle getBoundingBoxOrNull(int character, int size) { 108 Rectangle characterBox = null; 109 try { 110 characterBox = charSet.getCharacterBox(toUnicodeCodepoint(character), size); 111 } catch (IllegalArgumentException e) { 112 if (!charsProcessed.contains(character)) { 113 charsProcessed.add(character); 114 getAFPEventProducer().charactersetMissingMetrics(this, (char) character, 115 charSet.getName().trim()); 116 } 117 } 118 return characterBox; 119 } 120 getDefaultCharacterBox(int character, int size)121 private Rectangle getDefaultCharacterBox(int character, int size) { 122 return getBoundingBoxOrNull('-', size); 123 } 124 inferCharWidth(int character)125 private int inferCharWidth(int character) { 126 127 //Is this character an ideograph? 128 boolean isIdeographic = false; 129 Character.UnicodeBlock charBlock = Character.UnicodeBlock.of((char)character); 130 if (charBlock == null) { 131 isIdeographic = false; 132 } else if (IDEOGRAPHIC.contains(charBlock)) { 133 isIdeographic = true; 134 } else { //default 135 isIdeographic = false; 136 } 137 138 if (isIdeographic) { 139 return charSet.getEmSpaceIncrement(); 140 } else { 141 return charSet.getSpaceIncrement(); 142 } 143 } 144 145 } 146