Line 0
Link Here
|
|
|
1 |
/* |
2 |
* $Id: PDFToUnicodeCMap.java,v 1.3.2.1 2005/12/01 12:00:00 ono Exp $ |
3 |
* ============================================================================ |
4 |
* The Apache Software License, Version 1.1 |
5 |
* ============================================================================ |
6 |
* |
7 |
* Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved. |
8 |
* |
9 |
* Redistribution and use in source and binary forms, with or without modifica- |
10 |
* tion, are permitted provided that the following conditions are met: |
11 |
* |
12 |
* 1. Redistributions of source code must retain the above copyright notice, |
13 |
* this list of conditions and the following disclaimer. |
14 |
* |
15 |
* 2. Redistributions in binary form must reproduce the above copyright notice, |
16 |
* this list of conditions and the following disclaimer in the documentation |
17 |
* and/or other materials provided with the distribution. |
18 |
* |
19 |
* 3. The end-user documentation included with the redistribution, if any, must |
20 |
* include the following acknowledgment: "This product includes software |
21 |
* developed by the Apache Software Foundation (http://www.apache.org/)." |
22 |
* Alternately, this acknowledgment may appear in the software itself, if |
23 |
* and wherever such third-party acknowledgments normally appear. |
24 |
* |
25 |
* 4. The names "FOP" and "Apache Software Foundation" must not be used to |
26 |
* endorse or promote products derived from this software without prior |
27 |
* written permission. For written permission, please contact |
28 |
* apache@apache.org. |
29 |
* |
30 |
* 5. Products derived from this software may not be called "Apache", nor may |
31 |
* "Apache" appear in their name, without prior written permission of the |
32 |
* Apache Software Foundation. |
33 |
* |
34 |
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, |
35 |
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
36 |
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
37 |
* APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
38 |
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- |
39 |
* DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
40 |
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
41 |
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
42 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
43 |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
44 |
* ============================================================================ |
45 |
* |
46 |
* This software consists of voluntary contributions made by many individuals |
47 |
* on behalf of the Apache Software Foundation and was originally created by |
48 |
* James Tauber <jtauber@jtauber.com>. For more information on the Apache |
49 |
* Software Foundation, please see <http://www.apache.org/>. |
50 |
*/ |
51 |
package org.apache.fop.pdf; |
52 |
|
53 |
/** |
54 |
* Class representing ToUnicode CMaps. |
55 |
* Here are some documentation resources: |
56 |
* <ul> |
57 |
* <li>PDF Reference, Second Edition, Section 5.6.4, for general information |
58 |
* about CMaps in PDF Files.</li> |
59 |
* <li>PDF Reference, Second Edition, Section 5.9, for specific information |
60 |
* about ToUnicodeCMaps in PDF Files.</li> |
61 |
* <li> |
62 |
* <a href="http://partners.adobe.com/asn/developer/pdfs/tn/5411.ToUnicode.pdf"> |
63 |
* Adobe Technical Note #5411, "ToUnicode Mapping File Tutorial"</a>. |
64 |
* </ul> |
65 |
*/ |
66 |
import org.apache.fop.fonts.CIDFont; |
67 |
|
68 |
public class PDFToUnicodeCMap extends PDFCMap { |
69 |
|
70 |
/** |
71 |
* handle to read font |
72 |
*/ |
73 |
protected CIDFont cidFont; |
74 |
|
75 |
/** |
76 |
* Constructor. |
77 |
* |
78 |
* @param name One of the registered names found in Table 5.14 in PDF |
79 |
* Reference, Second Edition. |
80 |
* @param sysInfo The attributes of the character collection of the CIDFont. |
81 |
*/ |
82 |
public PDFToUnicodeCMap(CIDFont cidMetrics, String name, PDFCIDSystemInfo sysInfo) { |
83 |
super(name, sysInfo); |
84 |
cidFont = cidMetrics; |
85 |
} |
86 |
|
87 |
public void fillInPDF(StringBuffer p) { |
88 |
writeCIDInit(p); |
89 |
writeCIDSystemInfo(p); |
90 |
writeVersionTypeName(p); |
91 |
writeCodeSpaceRange(p); |
92 |
writeBFEntries(p); |
93 |
writeWrapUp(p); |
94 |
add(p.toString()); |
95 |
} |
96 |
|
97 |
protected void writeCIDSystemInfo(StringBuffer p) { |
98 |
p.append("/CIDSystemInfo\n"); |
99 |
p.append("<< /Registry (Adobe)\n"); |
100 |
p.append("/Ordering (UCS)\n"); |
101 |
p.append("/Supplement 0\n"); |
102 |
p.append(">> def\n"); |
103 |
} |
104 |
|
105 |
protected void writeVersionTypeName(StringBuffer p) { |
106 |
p.append("/CMapName /Adobe-Identity-UCS def\n"); |
107 |
p.append("/CMapType 2 def\n"); |
108 |
} |
109 |
|
110 |
/** |
111 |
* Writes the character mappings for this font. |
112 |
*/ |
113 |
protected void writeBFEntries(StringBuffer p) { |
114 |
if(cidFont == null) return; |
115 |
|
116 |
char[] charArray = cidFont.getCharsUsed(); |
117 |
|
118 |
if(charArray != null) { |
119 |
writeBFCharEntries(p, charArray); |
120 |
writeBFRangeEntries(p, charArray); |
121 |
} |
122 |
} |
123 |
|
124 |
protected void writeBFCharEntries(StringBuffer p, char[] charArray) { |
125 |
int completedEntries = 0; |
126 |
int totalEntries = 0; |
127 |
for (int i = 0; i < charArray.length; i++) { |
128 |
if (! partOfRange(charArray, i)) { |
129 |
totalEntries ++; |
130 |
} |
131 |
} |
132 |
if (totalEntries < 1) { |
133 |
return; |
134 |
} |
135 |
int remainingEntries = totalEntries; |
136 |
/* Limited to 100 entries in each section */ |
137 |
int entriesThisSection = Math.min(remainingEntries, 100); |
138 |
int remainingEntriesThisSection = entriesThisSection; |
139 |
p.append(entriesThisSection + " beginbfchar\n"); |
140 |
for (int i = 0; i < charArray.length; i++) { |
141 |
if (partOfRange(charArray, i)) { |
142 |
continue; |
143 |
} |
144 |
p.append("<" + padHexString(Integer.toHexString(i), 4) |
145 |
+ "> "); |
146 |
p.append("<" + padHexString(Integer.toHexString(charArray[i]), 4) |
147 |
+ ">\n"); |
148 |
/* Compute the statistics. */ |
149 |
completedEntries ++; |
150 |
remainingEntries = totalEntries - completedEntries; |
151 |
remainingEntriesThisSection --; |
152 |
if (remainingEntriesThisSection < 1) { |
153 |
if (remainingEntries > 0) { |
154 |
p.append("endbfchar\n"); |
155 |
entriesThisSection = Math.min(remainingEntries, 100); |
156 |
remainingEntriesThisSection = entriesThisSection; |
157 |
p.append(entriesThisSection + " beginbfchar\n"); |
158 |
} |
159 |
} |
160 |
} |
161 |
p.append("endbfchar\n"); |
162 |
} |
163 |
|
164 |
protected void writeBFRangeEntries(StringBuffer p, char[] charArray) { |
165 |
int completedEntries = 0; |
166 |
int totalEntries = 0; |
167 |
for (int i = 0; i < charArray.length; i++) { |
168 |
if (startOfRange(charArray, i)) { |
169 |
totalEntries ++; |
170 |
} |
171 |
} |
172 |
if (totalEntries < 1) { |
173 |
return; |
174 |
} |
175 |
int remainingEntries = totalEntries; |
176 |
int entriesThisSection = Math.min(remainingEntries, 100); |
177 |
int remainingEntriesThisSection = entriesThisSection; |
178 |
p.append(entriesThisSection + " beginbfrange\n"); |
179 |
for (int i = 0; i < charArray.length; i++) { |
180 |
if (! startOfRange(charArray, i)) { |
181 |
continue; |
182 |
} |
183 |
p.append("<" |
184 |
+ padHexString(Integer.toHexString(i), 4) |
185 |
+ "> "); |
186 |
p.append("<" |
187 |
+ padHexString(Integer.toHexString |
188 |
(endOfRange(charArray, i)), 4) |
189 |
+ "> "); |
190 |
p.append("<" |
191 |
+ padHexString(Integer.toHexString(charArray[i]), 4) |
192 |
+ ">\n"); |
193 |
/* Compute the statistics. */ |
194 |
completedEntries ++; |
195 |
remainingEntries = totalEntries - completedEntries; |
196 |
if (remainingEntriesThisSection < 1) { |
197 |
if (remainingEntries > 0) { |
198 |
p.append("endbfrange\n"); |
199 |
entriesThisSection = Math.min(remainingEntries, 100); |
200 |
remainingEntriesThisSection = entriesThisSection; |
201 |
p.append(entriesThisSection + " beginbfrange\n"); |
202 |
} |
203 |
} |
204 |
} |
205 |
p.append("endbfrange\n"); |
206 |
} |
207 |
|
208 |
/** |
209 |
* Find the end of the current range. |
210 |
* @param charArray The array which is being tested. |
211 |
* @param startOfRange The index to the array element that is the start of |
212 |
* the range. |
213 |
* @return The index to the element that is the end of the range. |
214 |
*/ |
215 |
private int endOfRange(char[] charArray, int startOfRange) { |
216 |
int endOfRange = -1; |
217 |
for (int i = startOfRange; i < charArray.length - 1 && endOfRange < 0; |
218 |
i++) { |
219 |
if (! sameRangeEntryAsNext(charArray, i)) { |
220 |
endOfRange = i; |
221 |
} |
222 |
} |
223 |
return endOfRange; |
224 |
} |
225 |
|
226 |
/** |
227 |
* Determine whether this array element should be part of a bfchar entry or |
228 |
* a bfrange entry. |
229 |
* @param charArray The array to be tested. |
230 |
* @param arrayIndex The index to the array element to be tested. |
231 |
* @return True if this array element should be included in a range. |
232 |
*/ |
233 |
private boolean partOfRange(char[] charArray, int arrayIndex) { |
234 |
if (charArray.length < 2) { |
235 |
return false; |
236 |
} |
237 |
if (arrayIndex == 0) { |
238 |
return sameRangeEntryAsNext(charArray, 0); |
239 |
} |
240 |
if (arrayIndex == charArray.length - 1) { |
241 |
return sameRangeEntryAsNext(charArray, arrayIndex - 1); |
242 |
} |
243 |
if (sameRangeEntryAsNext(charArray, arrayIndex - 1)) { |
244 |
return true; |
245 |
} |
246 |
if (sameRangeEntryAsNext(charArray, arrayIndex)) { |
247 |
return true; |
248 |
} |
249 |
return false; |
250 |
} |
251 |
|
252 |
/** |
253 |
* Determine whether two bytes can be written in the same bfrange entry. |
254 |
* @param charArray The array to be tested. |
255 |
* @param firstItem The first of the two items in the array to be tested. |
256 |
* The second item is firstItem + 1. |
257 |
* @return True if both 1) the next item in the array is sequential with |
258 |
* this one, and 2) the first byte of the character in the first position |
259 |
* is equal to the first byte of the character in the second position. |
260 |
*/ |
261 |
private boolean sameRangeEntryAsNext(char[] charArray, int firstItem) { |
262 |
if (charArray[firstItem] + 1 != charArray[firstItem + 1]) { |
263 |
return false; |
264 |
} |
265 |
if (firstItem / 256 != (firstItem + 1) / 256) { |
266 |
return false; |
267 |
} |
268 |
return true; |
269 |
} |
270 |
|
271 |
/** |
272 |
* Determine whether this array element should be the start of a bfrange |
273 |
* entry. |
274 |
* @param charArray The array to be tested. |
275 |
* @param arrayIndex The index to the array element to be tested. |
276 |
* @return True if this array element is the beginning of a range. |
277 |
*/ |
278 |
private boolean startOfRange(char[] charArray, int arrayIndex) { |
279 |
// Can't be the start of a range if not part of a range. |
280 |
if (! partOfRange(charArray, arrayIndex)) { |
281 |
return false; |
282 |
} |
283 |
// If first element in the array, must be start of a range |
284 |
if (arrayIndex == 0) { |
285 |
return true; |
286 |
} |
287 |
// If last element in the array, cannot be start of a range |
288 |
if (arrayIndex == charArray.length - 1) { |
289 |
return false; |
290 |
} |
291 |
/* |
292 |
* If part of same range as the previous element is, cannot be start |
293 |
* of range. |
294 |
*/ |
295 |
if (sameRangeEntryAsNext(charArray, arrayIndex - 1)) { |
296 |
return false; |
297 |
} |
298 |
// Otherwise, this is start of a range. |
299 |
return true; |
300 |
} |
301 |
|
302 |
/** |
303 |
* Prepends the input string with a sufficient number of "0" characters to |
304 |
* get the returned string to be numChars length. |
305 |
* @param input The input string. |
306 |
* @param numChars The minimum characters in the output string. |
307 |
* @return The padded string. |
308 |
*/ |
309 |
public static String padHexString(String input, int numChars) { |
310 |
int length = input.length(); |
311 |
if (length >= numChars) { |
312 |
return input; |
313 |
} |
314 |
StringBuffer returnString = new StringBuffer(); |
315 |
for (int i = 1; i <= numChars - length; i++) { |
316 |
returnString.append("0"); |
317 |
} |
318 |
returnString.append(input); |
319 |
return returnString.toString(); |
320 |
} |
321 |
|
322 |
} |