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