You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

OTFSubSetFile.java 45KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129
  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. /* $Id$ */
  18. package org.apache.fop.fonts.truetype;
  19. import java.io.IOException;
  20. import java.util.ArrayList;
  21. import java.util.Arrays;
  22. import java.util.Collections;
  23. import java.util.Comparator;
  24. import java.util.HashMap;
  25. import java.util.LinkedHashMap;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.Map.Entry;
  29. import java.util.Set;
  30. import org.apache.fontbox.cff.CFFStandardString;
  31. import org.apache.fontbox.cff.encoding.CFFEncoding;
  32. import org.apache.fop.fonts.MultiByteFont;
  33. import org.apache.fop.fonts.cff.CFFDataReader;
  34. import org.apache.fop.fonts.cff.CFFDataReader.CFFIndexData;
  35. import org.apache.fop.fonts.cff.CFFDataReader.DICTEntry;
  36. import org.apache.fop.fonts.cff.CFFDataReader.FDSelect;
  37. import org.apache.fop.fonts.cff.CFFDataReader.FontDict;
  38. import org.apache.fop.fonts.cff.CFFDataReader.Format0FDSelect;
  39. import org.apache.fop.fonts.cff.CFFDataReader.Format3FDSelect;
  40. /**
  41. * Reads an OpenType CFF file and generates a subset
  42. * The OpenType specification can be found at the Microsoft
  43. * Typography site: http://www.microsoft.com/typography/otspec/
  44. */
  45. public class OTFSubSetFile extends OTFFile {
  46. protected byte[] output;
  47. protected int currentPos;
  48. private int realSize;
  49. /** A map containing each glyph to be included in the subset
  50. * with their existing and new GID's **/
  51. protected LinkedHashMap<Integer, Integer> subsetGlyphs = new LinkedHashMap<Integer, Integer>();
  52. /** A map of the new GID to SID used to construct the charset table **/
  53. protected LinkedHashMap<Integer, Integer> gidToSID;
  54. protected CFFIndexData localIndexSubr;
  55. protected CFFIndexData globalIndexSubr;
  56. /** List of subroutines to write to the local / global indexes in the subset font **/
  57. protected List<byte[]> subsetLocalIndexSubr;
  58. protected List<byte[]> subsetGlobalIndexSubr;
  59. /** For fonts which have an FDSelect or ROS flag in Top Dict, this is used to store the
  60. * local subroutine indexes for each group as opposed to the above subsetLocalIndexSubr */
  61. private ArrayList<List<byte[]>> fdSubrs;
  62. /** The subset FD Select table used to store the mappings between glyphs and their
  63. * associated FDFont object which point to a private dict and local subroutines. */
  64. private LinkedHashMap<Integer, FDIndexReference> subsetFDSelect;
  65. /** A list of unique subroutines from the global / local subroutine indexes */
  66. protected List<Integer> localUniques;
  67. protected List<Integer> globalUniques;
  68. /** A store of the number of subroutines each global / local subroutine will store **/
  69. protected int subsetLocalSubrCount;
  70. protected int subsetGlobalSubrCount;
  71. /** A list of char string data for each glyph to be stored in the subset font **/
  72. protected List<byte[]> subsetCharStringsIndex;
  73. /** The embedded name to change in the name table **/
  74. protected String embeddedName;
  75. /** An array used to hold the string index data for the subset font **/
  76. protected List<byte[]> stringIndexData = new ArrayList<byte[]>();
  77. /** The CFF reader object used to read data and offsets from the original font file */
  78. protected CFFDataReader cffReader;
  79. /** The class used to represent this font **/
  80. private MultiByteFont mbFont;
  81. /** The number of standard strings in CFF **/
  82. public static final int NUM_STANDARD_STRINGS = 391;
  83. /** The operator used to identify a local subroutine reference */
  84. private static final int LOCAL_SUBROUTINE = 10;
  85. /** The operator used to identify a global subroutine reference */
  86. private static final int GLOBAL_SUBROUTINE = 29;
  87. public OTFSubSetFile() throws IOException {
  88. super();
  89. }
  90. public void readFont(FontFileReader in, String embeddedName, String header,
  91. MultiByteFont mbFont) throws IOException {
  92. this.mbFont = mbFont;
  93. readFont(in, embeddedName, header, mbFont.getUsedGlyphs());
  94. }
  95. /**
  96. * Reads and creates a subset of the font.
  97. *
  98. * @param in FontFileReader to read from
  99. * @param name Name to be checked for in the font file
  100. * @param header The header of the font file
  101. * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
  102. * new index as (Integer) value)
  103. * @throws IOException in case of an I/O problem
  104. */
  105. void readFont(FontFileReader in, String embeddedName, String header,
  106. Map<Integer, Integer> usedGlyphs) throws IOException {
  107. fontFile = in;
  108. currentPos = 0;
  109. realSize = 0;
  110. this.embeddedName = embeddedName;
  111. //Sort by the new GID and store in a LinkedHashMap
  112. subsetGlyphs = sortByValue(usedGlyphs);
  113. output = new byte[in.getFileSize()];
  114. initializeFont(in);
  115. cffReader = new CFFDataReader(fontFile);
  116. //Create the CIDFontType0C data
  117. createCFF();
  118. }
  119. private LinkedHashMap<Integer, Integer> sortByValue(Map<Integer, Integer> map) {
  120. List<Entry<Integer, Integer>> list = new ArrayList<Entry<Integer, Integer>>(map.entrySet());
  121. Collections.sort(list, new Comparator<Entry<Integer, Integer>>() {
  122. public int compare(Entry<Integer, Integer> o1, Entry<Integer, Integer> o2) {
  123. return ((Comparable<Integer>) o1.getValue()).compareTo(o2.getValue());
  124. }
  125. });
  126. LinkedHashMap<Integer, Integer> result = new LinkedHashMap<Integer, Integer>();
  127. for (Entry<Integer, Integer> entry : list) {
  128. result.put(entry.getKey(), entry.getValue());
  129. }
  130. return result;
  131. }
  132. protected void createCFF() throws IOException {
  133. //Header
  134. writeBytes(cffReader.getHeader());
  135. //Name Index
  136. writeIndex(Arrays.asList(embeddedName.getBytes()));
  137. //Keep offset of the topDICT so it can be updated once all data has been written
  138. int topDictOffset = currentPos;
  139. //Top DICT Index and Data
  140. byte[] topDictIndex = cffReader.getTopDictIndex().getByteData();
  141. int offSize = topDictIndex[2];
  142. writeBytes(topDictIndex, 0, 3 + (offSize * 2));
  143. int topDictDataOffset = currentPos;
  144. writeTopDICT();
  145. //Create the char string index data and related local / global subroutines
  146. if (cffReader.getFDSelect() == null) {
  147. createCharStringData();
  148. } else {
  149. createCharStringDataCID();
  150. }
  151. //If it is a CID-Keyed font, store each FD font and add each SID
  152. List<Integer> fontNameSIDs = null;
  153. List<Integer> subsetFDFonts = null;
  154. if (cffReader.getFDSelect() != null) {
  155. subsetFDFonts = getUsedFDFonts();
  156. fontNameSIDs = storeFDStrings(subsetFDFonts);
  157. }
  158. //String index
  159. writeStringIndex();
  160. //Global subroutine index
  161. writeIndex(subsetGlobalIndexSubr);
  162. //Encoding
  163. int encodingOffset = currentPos;
  164. writeEncoding(fileFont.getEncoding());
  165. //Charset table
  166. int charsetOffset = currentPos;
  167. writeCharsetTable(cffReader.getFDSelect() != null);
  168. //FDSelect table
  169. int fdSelectOffset = currentPos;
  170. if (cffReader.getFDSelect() != null) {
  171. writeFDSelect();
  172. }
  173. //Char Strings Index
  174. int charStringOffset = currentPos;
  175. writeIndex(subsetCharStringsIndex);
  176. if (cffReader.getFDSelect() == null) {
  177. //Keep offset to modify later with the local subroutine index offset
  178. int privateDictOffset = currentPos;
  179. writePrivateDict();
  180. //Local subroutine index
  181. int localIndexOffset = currentPos;
  182. writeIndex(subsetLocalIndexSubr);
  183. //Update the offsets
  184. updateOffsets(topDictOffset, charsetOffset, charStringOffset, privateDictOffset,
  185. localIndexOffset, encodingOffset);
  186. } else {
  187. List<Integer> privateDictOffsets = writeCIDDictsAndSubrs(subsetFDFonts);
  188. int fdArrayOffset = writeFDArray(subsetFDFonts, privateDictOffsets, fontNameSIDs);
  189. updateCIDOffsets(topDictDataOffset, fdArrayOffset, fdSelectOffset, charsetOffset,
  190. charStringOffset, encodingOffset);
  191. }
  192. }
  193. protected List<Integer> storeFDStrings(List<Integer> uniqueNewRefs) throws IOException {
  194. ArrayList<Integer> fontNameSIDs = new ArrayList<Integer>();
  195. List<FontDict> fdFonts = cffReader.getFDFonts();
  196. for (int i = 0; i < uniqueNewRefs.size(); i++) {
  197. FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i));
  198. byte[] fdFontByteData = fdFont.getByteData();
  199. Map<String, DICTEntry> fdFontDict = cffReader.parseDictData(fdFontByteData);
  200. fontNameSIDs.add(stringIndexData.size() + NUM_STANDARD_STRINGS);
  201. stringIndexData.add(cffReader.getStringIndex().getValue(fdFontDict.get("FontName")
  202. .getOperands().get(0).intValue() - NUM_STANDARD_STRINGS));
  203. }
  204. return fontNameSIDs;
  205. }
  206. protected void writeBytes(byte[] out) {
  207. for (int i = 0; i < out.length; i++) {
  208. writeByte(out[i]);
  209. }
  210. }
  211. protected void writeBytes(byte[] out, int offset, int length) {
  212. for (int i = offset; i < offset + length; i++) {
  213. output[currentPos++] = out[i];
  214. realSize++;
  215. }
  216. }
  217. private void writeEncoding(CFFEncoding encoding) throws IOException {
  218. LinkedHashMap<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
  219. DICTEntry encodingEntry = topDICT.get("Encoding");
  220. if (encodingEntry != null && encodingEntry.getOperands().get(0).intValue() != 0
  221. && encodingEntry.getOperands().get(0).intValue() != 1) {
  222. writeByte(0);
  223. writeByte(gidToSID.size());
  224. for (int gid : gidToSID.keySet()) {
  225. int code = encoding.getCode(gidToSID.get(gid));
  226. writeByte(code);
  227. }
  228. }
  229. }
  230. protected void writeTopDICT() throws IOException {
  231. LinkedHashMap<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
  232. List<String> topDictStringEntries = Arrays.asList("version", "Notice", "Copyright",
  233. "FullName", "FamilyName", "Weight", "PostScript");
  234. for (Map.Entry<String, DICTEntry> dictEntry : topDICT.entrySet()) {
  235. String dictKey = dictEntry.getKey();
  236. DICTEntry entry = dictEntry.getValue();
  237. //If the value is an SID, update the reference but keep the size the same
  238. if (dictKey.equals("ROS")) {
  239. writeROSEntry(entry);
  240. } else if (dictKey.equals("CIDCount")) {
  241. writeCIDCount(entry);
  242. } else if (topDictStringEntries.contains(dictKey)) {
  243. writeTopDictStringEntry(entry);
  244. } else {
  245. writeBytes(entry.getByteData());
  246. }
  247. }
  248. }
  249. private void writeROSEntry(DICTEntry dictEntry) throws IOException {
  250. int sidA = dictEntry.getOperands().get(0).intValue();
  251. if (sidA > 390) {
  252. stringIndexData.add(cffReader.getStringIndex().getValue(sidA - NUM_STANDARD_STRINGS));
  253. }
  254. int sidAStringIndex = stringIndexData.size() + 390;
  255. int sidB = dictEntry.getOperands().get(1).intValue();
  256. if (sidB > 390) {
  257. stringIndexData.add("Identity".getBytes());
  258. }
  259. int sidBStringIndex = stringIndexData.size() + 390;
  260. byte[] cidEntryByteData = dictEntry.getByteData();
  261. cidEntryByteData = updateOffset(cidEntryByteData, 0, dictEntry.getOperandLengths().get(0),
  262. sidAStringIndex);
  263. cidEntryByteData = updateOffset(cidEntryByteData, dictEntry.getOperandLengths().get(0),
  264. dictEntry.getOperandLengths().get(1), sidBStringIndex);
  265. cidEntryByteData = updateOffset(cidEntryByteData, dictEntry.getOperandLengths().get(0)
  266. + dictEntry.getOperandLengths().get(1), dictEntry.getOperandLengths().get(2), 139);
  267. writeBytes(cidEntryByteData);
  268. }
  269. protected void writeCIDCount(DICTEntry dictEntry) throws IOException {
  270. byte[] cidCountByteData = dictEntry.getByteData();
  271. cidCountByteData = updateOffset(cidCountByteData, 0, dictEntry.getOperandLengths().get(0),
  272. subsetGlyphs.size());
  273. writeBytes(cidCountByteData);
  274. }
  275. private void writeTopDictStringEntry(DICTEntry dictEntry) throws IOException {
  276. int sid = dictEntry.getOperands().get(0).intValue();
  277. if (sid > 391) {
  278. stringIndexData.add(cffReader.getStringIndex().getValue(sid - 391));
  279. }
  280. byte[] newDictEntry = createNewRef(stringIndexData.size() + 390, dictEntry.getOperator(),
  281. dictEntry.getOperandLength());
  282. writeBytes(newDictEntry);
  283. }
  284. private void writeStringIndex() throws IOException {
  285. Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
  286. int charsetOffset = topDICT.get("charset").getOperands().get(0).intValue();
  287. gidToSID = new LinkedHashMap<Integer, Integer>();
  288. for (int gid : subsetGlyphs.keySet()) {
  289. int sid = cffReader.getSIDFromGID(charsetOffset, gid);
  290. //Check whether the SID falls into the standard string set
  291. if (sid < NUM_STANDARD_STRINGS) {
  292. gidToSID.put(subsetGlyphs.get(gid), sid);
  293. if (mbFont != null) {
  294. mbFont.mapUsedGlyphName(subsetGlyphs.get(gid),
  295. CFFStandardString.getName(sid));
  296. }
  297. } else {
  298. int index = sid - NUM_STANDARD_STRINGS;
  299. if (index <= cffReader.getStringIndex().getNumObjects()) {
  300. if (mbFont != null) {
  301. mbFont.mapUsedGlyphName(subsetGlyphs.get(gid),
  302. new String(cffReader.getStringIndex().getValue(index)));
  303. }
  304. gidToSID.put(subsetGlyphs.get(gid), stringIndexData.size() + 391);
  305. stringIndexData.add(cffReader.getStringIndex().getValue(index));
  306. } else {
  307. if (mbFont != null) {
  308. mbFont.mapUsedGlyphName(subsetGlyphs.get(gid), ".notdef");
  309. }
  310. gidToSID.put(subsetGlyphs.get(gid), index);
  311. }
  312. }
  313. }
  314. //Write the String Index
  315. writeIndex(stringIndexData);
  316. }
  317. protected void createCharStringDataCID() throws IOException {
  318. CFFIndexData charStringsIndex = cffReader.getCharStringIndex();
  319. FDSelect fontDictionary = cffReader.getFDSelect();
  320. if (fontDictionary instanceof Format0FDSelect) {
  321. throw new UnsupportedOperationException("OTF CFF CID Format0 currently not implemented");
  322. } else if (fontDictionary instanceof Format3FDSelect) {
  323. Format3FDSelect fdSelect = (Format3FDSelect)fontDictionary;
  324. Map<Integer, Integer> subsetGroups = new HashMap<Integer, Integer>();
  325. List<Integer> uniqueGroups = new ArrayList<Integer>();
  326. for (int gid : subsetGlyphs.keySet()) {
  327. Set<Integer> rangeKeys = fdSelect.getRanges().keySet();
  328. Integer[] ranges = rangeKeys.toArray(new Integer[rangeKeys.size()]);
  329. for (int i = 0; i < ranges.length; i++) {
  330. int nextRange = -1;
  331. if (i < ranges.length - 1) {
  332. nextRange = ranges[i + 1];
  333. } else {
  334. nextRange = fdSelect.getSentinelGID();
  335. }
  336. if (gid >= ranges[i] && gid < nextRange) {
  337. subsetGroups.put(gid, fdSelect.getRanges().get(ranges[i]));
  338. if (!uniqueGroups.contains(fdSelect.getRanges().get(ranges[i]))) {
  339. uniqueGroups.add(fdSelect.getRanges().get(ranges[i]));
  340. }
  341. }
  342. }
  343. }
  344. //Prepare resources
  345. globalIndexSubr = cffReader.getGlobalIndexSubr();
  346. //Create the new char string index
  347. subsetCharStringsIndex = new ArrayList<byte[]>();
  348. globalUniques = new ArrayList<Integer>();
  349. subsetFDSelect = new LinkedHashMap<Integer, FDIndexReference>();
  350. List<List<Integer>> foundLocalUniques = new ArrayList<List<Integer>>();
  351. for (int i = 0; i < uniqueGroups.size(); i++) {
  352. foundLocalUniques.add(new ArrayList<Integer>());
  353. }
  354. for (int gid : subsetGlyphs.keySet()) {
  355. int group = subsetGroups.get(gid);
  356. localIndexSubr = cffReader.getFDFonts().get(group).getLocalSubrData();
  357. localUniques = foundLocalUniques.get(uniqueGroups.indexOf(subsetGroups.get(gid)));
  358. FDIndexReference newFDReference = new FDIndexReference(
  359. uniqueGroups.indexOf(subsetGroups.get(gid)), subsetGroups.get(gid));
  360. subsetFDSelect.put(subsetGlyphs.get(gid), newFDReference);
  361. byte[] data = charStringsIndex.getValue(gid);
  362. preScanForSubsetIndexSize(data);
  363. }
  364. //Create the two lists which are to store the local and global subroutines
  365. subsetGlobalIndexSubr = new ArrayList<byte[]>();
  366. fdSubrs = new ArrayList<List<byte[]>>();
  367. subsetGlobalSubrCount = globalUniques.size();
  368. globalUniques.clear();
  369. localUniques = null;
  370. for (int l = 0; l < foundLocalUniques.size(); l++) {
  371. fdSubrs.add(new ArrayList<byte[]>());
  372. }
  373. List<List<Integer>> foundLocalUniquesB = new ArrayList<List<Integer>>();
  374. for (int k = 0; k < uniqueGroups.size(); k++) {
  375. foundLocalUniquesB.add(new ArrayList<Integer>());
  376. }
  377. for (Integer gid : subsetGlyphs.keySet()) {
  378. int group = subsetGroups.get(gid);
  379. localIndexSubr = cffReader.getFDFonts().get(group).getLocalSubrData();
  380. localUniques = foundLocalUniquesB.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex());
  381. byte[] data = charStringsIndex.getValue(gid);
  382. subsetLocalIndexSubr = fdSubrs.get(subsetFDSelect.get(subsetGlyphs.get(gid)).getNewFDIndex());
  383. subsetLocalSubrCount = foundLocalUniques.get(subsetFDSelect.get(subsetGlyphs.get(gid))
  384. .getNewFDIndex()).size();
  385. data = readCharStringData(data, subsetLocalSubrCount);
  386. subsetCharStringsIndex.add(data);
  387. }
  388. }
  389. }
  390. protected void writeFDSelect() {
  391. writeByte(0); //Format
  392. for (Integer gid : subsetFDSelect.keySet()) {
  393. writeByte(subsetFDSelect.get(gid).getNewFDIndex());
  394. }
  395. }
  396. protected List<Integer> getUsedFDFonts() {
  397. List<Integer> uniqueNewRefs = new ArrayList<Integer>();
  398. for (int gid : subsetFDSelect.keySet()) {
  399. int fdIndex = subsetFDSelect.get(gid).getOldFDIndex();
  400. if (!uniqueNewRefs.contains(fdIndex)) {
  401. uniqueNewRefs.add(fdIndex);
  402. }
  403. }
  404. return uniqueNewRefs;
  405. }
  406. protected List<Integer> writeCIDDictsAndSubrs(List<Integer> uniqueNewRefs)
  407. throws IOException {
  408. List<Integer> privateDictOffsets = new ArrayList<Integer>();
  409. List<FontDict> fdFonts = cffReader.getFDFonts();
  410. for (int i = 0; i < uniqueNewRefs.size(); i++) {
  411. FontDict curFDFont = fdFonts.get(uniqueNewRefs.get(i));
  412. HashMap<String, DICTEntry> fdPrivateDict = cffReader.parseDictData(
  413. curFDFont.getPrivateDictData());
  414. int privateDictOffset = currentPos;
  415. privateDictOffsets.add(privateDictOffset);
  416. byte[] fdPrivateDictByteData = curFDFont.getPrivateDictData();
  417. if (fdPrivateDict.get("Subrs") != null) {
  418. int encodingValue = 0;
  419. if (fdPrivateDict.get("Subrs").getOperandLength() == 1) {
  420. encodingValue = 139;
  421. }
  422. fdPrivateDictByteData = updateOffset(fdPrivateDictByteData, fdPrivateDict.get("Subrs").getOffset(),
  423. fdPrivateDict.get("Subrs").getOperandLength(),
  424. fdPrivateDictByteData.length + encodingValue);
  425. }
  426. writeBytes(fdPrivateDictByteData);
  427. writeIndex(fdSubrs.get(i));
  428. }
  429. return privateDictOffsets;
  430. }
  431. protected int writeFDArray(List<Integer> uniqueNewRefs, List<Integer> privateDictOffsets,
  432. List<Integer> fontNameSIDs)
  433. throws IOException {
  434. int offset = currentPos;
  435. List<FontDict> fdFonts = cffReader.getFDFonts();
  436. writeCard16(uniqueNewRefs.size());
  437. writeByte(1); //Offset size
  438. writeByte(1); //First offset
  439. int count = 1;
  440. for (int i = 0; i < uniqueNewRefs.size(); i++) {
  441. FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i));
  442. count += fdFont.getByteData().length;
  443. writeByte(count);
  444. }
  445. for (int i = 0; i < uniqueNewRefs.size(); i++) {
  446. FontDict fdFont = fdFonts.get(uniqueNewRefs.get(i));
  447. byte[] fdFontByteData = fdFont.getByteData();
  448. Map<String, DICTEntry> fdFontDict = cffReader.parseDictData(fdFontByteData);
  449. //Update the SID to the FontName
  450. fdFontByteData = updateOffset(fdFontByteData, fdFontDict.get("FontName").getOffset() - 1,
  451. fdFontDict.get("FontName").getOperandLengths().get(0),
  452. fontNameSIDs.get(i));
  453. //Update the Private dict reference
  454. fdFontByteData = updateOffset(fdFontByteData, fdFontDict.get("Private").getOffset()
  455. + fdFontDict.get("Private").getOperandLengths().get(0),
  456. fdFontDict.get("Private").getOperandLengths().get(1),
  457. privateDictOffsets.get(i));
  458. writeBytes(fdFontByteData);
  459. }
  460. return offset;
  461. }
  462. private class FDIndexReference {
  463. private int newFDIndex;
  464. private int oldFDIndex;
  465. public FDIndexReference(int newFDIndex, int oldFDIndex) {
  466. this.newFDIndex = newFDIndex;
  467. this.oldFDIndex = oldFDIndex;
  468. }
  469. public int getNewFDIndex() {
  470. return newFDIndex;
  471. }
  472. public int getOldFDIndex() {
  473. return oldFDIndex;
  474. }
  475. }
  476. private void createCharStringData() throws IOException {
  477. Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
  478. CFFIndexData charStringsIndex = cffReader.getCharStringIndex();
  479. DICTEntry privateEntry = topDICT.get("Private");
  480. if (privateEntry != null) {
  481. int privateOffset = privateEntry.getOperands().get(1).intValue();
  482. Map<String, DICTEntry> privateDICT = cffReader.getPrivateDict(privateEntry);
  483. if (privateDICT.get("Subrs") != null) {
  484. int localSubrOffset = privateOffset + privateDICT.get("Subrs").getOperands().get(0).intValue();
  485. localIndexSubr = cffReader.readIndex(localSubrOffset);
  486. } else {
  487. localIndexSubr = cffReader.readIndex(null);
  488. }
  489. }
  490. globalIndexSubr = cffReader.getGlobalIndexSubr();
  491. //Create the two lists which are to store the local and global subroutines
  492. subsetLocalIndexSubr = new ArrayList<byte[]>();
  493. subsetGlobalIndexSubr = new ArrayList<byte[]>();
  494. //Create the new char string index
  495. subsetCharStringsIndex = new ArrayList<byte[]>();
  496. localUniques = new ArrayList<Integer>();
  497. globalUniques = new ArrayList<Integer>();
  498. for (int gid : subsetGlyphs.keySet()) {
  499. byte[] data = charStringsIndex.getValue(gid);
  500. preScanForSubsetIndexSize(data);
  501. }
  502. //Store the size of each subset index and clear the unique arrays
  503. subsetLocalSubrCount = localUniques.size();
  504. subsetGlobalSubrCount = globalUniques.size();
  505. localUniques.clear();
  506. globalUniques.clear();
  507. for (int gid : subsetGlyphs.keySet()) {
  508. byte[] data = charStringsIndex.getValue(gid);
  509. //Retrieve modified char string data and fill local / global subroutine arrays
  510. data = readCharStringData(data, subsetLocalSubrCount);
  511. subsetCharStringsIndex.add(data);
  512. }
  513. }
  514. private void preScanForSubsetIndexSize(byte[] data) throws IOException {
  515. boolean hasLocalSubroutines = localIndexSubr != null && localIndexSubr.getNumObjects() > 0;
  516. boolean hasGlobalSubroutines = globalIndexSubr != null && globalIndexSubr.getNumObjects() > 0;
  517. BytesNumber operand = new BytesNumber(-1, -1);
  518. for (int dataPos = 0; dataPos < data.length; dataPos++) {
  519. int b0 = data[dataPos] & 0xff;
  520. if (b0 == LOCAL_SUBROUTINE && hasLocalSubroutines) {
  521. int subrNumber = getSubrNumber(localIndexSubr.getNumObjects(), operand.getNumber());
  522. if (!localUniques.contains(subrNumber) && subrNumber < localIndexSubr.getNumObjects()) {
  523. localUniques.add(subrNumber);
  524. byte[] subr = localIndexSubr.getValue(subrNumber);
  525. preScanForSubsetIndexSize(subr);
  526. }
  527. operand.clearNumber();
  528. } else if (b0 == GLOBAL_SUBROUTINE && hasGlobalSubroutines) {
  529. int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), operand.getNumber());
  530. if (!globalUniques.contains(subrNumber) && subrNumber < globalIndexSubr.getNumObjects()) {
  531. globalUniques.add(subrNumber);
  532. byte[] subr = globalIndexSubr.getValue(subrNumber);
  533. preScanForSubsetIndexSize(subr);
  534. }
  535. operand.clearNumber();
  536. } else if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) {
  537. operand.clearNumber();
  538. if (b0 == 19 || b0 == 20) {
  539. dataPos += 1;
  540. }
  541. } else if (b0 == 28 || (b0 >= 32 && b0 <= 255)) {
  542. operand = readNumber(b0, data, dataPos);
  543. dataPos += operand.getNumBytes() - 1;
  544. }
  545. }
  546. }
  547. private int getSubrNumber(int numSubroutines, int operand) {
  548. int bias = getBias(numSubroutines);
  549. return bias + operand;
  550. }
  551. private byte[] readCharStringData(byte[] data, int subsetLocalSubrCount) throws IOException {
  552. boolean hasLocalSubroutines = localIndexSubr != null && localIndexSubr.getNumObjects() > 0;
  553. boolean hasGlobalSubroutines = globalIndexSubr != null && globalIndexSubr.getNumObjects() > 0;
  554. BytesNumber operand = new BytesNumber(-1, -1);
  555. for (int dataPos = 0; dataPos < data.length; dataPos++) {
  556. int b0 = data[dataPos] & 0xff;
  557. if (b0 == 10 && hasLocalSubroutines) {
  558. int subrNumber = getSubrNumber(localIndexSubr.getNumObjects(), operand.getNumber());
  559. int newRef = getNewRefForReference(subrNumber, localUniques, localIndexSubr, subsetLocalIndexSubr,
  560. subsetLocalSubrCount);
  561. if (newRef != -1) {
  562. byte[] newData = constructNewRefData(dataPos, data, operand, subsetLocalSubrCount,
  563. newRef, new int[] {10});
  564. dataPos -= data.length - newData.length;
  565. data = newData;
  566. }
  567. operand.clearNumber();
  568. } else if (b0 == 29 && hasGlobalSubroutines) {
  569. int subrNumber = getSubrNumber(globalIndexSubr.getNumObjects(), operand.getNumber());
  570. int newRef = getNewRefForReference(subrNumber, globalUniques, globalIndexSubr, subsetGlobalIndexSubr,
  571. subsetGlobalSubrCount);
  572. if (newRef != -1) {
  573. byte[] newData = constructNewRefData(dataPos, data, operand, subsetGlobalSubrCount,
  574. newRef, new int[] {29});
  575. dataPos -= (data.length - newData.length);
  576. data = newData;
  577. }
  578. operand.clearNumber();
  579. } else if ((b0 >= 0 && b0 <= 27) || (b0 >= 29 && b0 <= 31)) {
  580. operand.clearNumber();
  581. if (b0 == 19) {
  582. dataPos += 1;
  583. }
  584. } else if (b0 == 28 || (b0 >= 32 && b0 <= 255)) {
  585. operand = readNumber(b0, data, dataPos);
  586. dataPos += operand.getNumBytes() - 1;
  587. }
  588. }
  589. //Return the data with the modified references to our arrays
  590. return data;
  591. }
  592. private int getNewRefForReference(int subrNumber, List<Integer> uniquesArray,
  593. CFFIndexData indexSubr, List<byte[]> subsetIndexSubr, int subrCount) throws IOException {
  594. int newRef = -1;
  595. if (!uniquesArray.contains(subrNumber)) {
  596. if (subrNumber < indexSubr.getNumObjects()) {
  597. byte[] subr = indexSubr.getValue(subrNumber);
  598. subr = readCharStringData(subr, subrCount);
  599. if (!uniquesArray.contains(subrNumber)) {
  600. uniquesArray.add(subrNumber);
  601. subsetIndexSubr.add(subr);
  602. newRef = subsetIndexSubr.size() - 1;
  603. } else {
  604. newRef = uniquesArray.indexOf(subrNumber);
  605. }
  606. }
  607. } else {
  608. newRef = uniquesArray.indexOf(subrNumber);
  609. }
  610. return newRef;
  611. }
  612. private int getBias(int subrCount) {
  613. if (subrCount < 1240) {
  614. return 107;
  615. } else if (subrCount < 33900) {
  616. return 1131;
  617. } else {
  618. return 32768;
  619. }
  620. }
  621. private byte[] constructNewRefData(int curDataPos, byte[] currentData, BytesNumber operand,
  622. int fullSubsetIndexSize, int curSubsetIndexSize, int[] operatorCode) {
  623. //Create the new array with the modified reference
  624. byte[] newData;
  625. int startRef = curDataPos - operand.getNumBytes();
  626. int length = operand.getNumBytes() + 1;
  627. byte[] preBytes = new byte[startRef];
  628. System.arraycopy(currentData, 0, preBytes, 0, startRef);
  629. int newBias = getBias(fullSubsetIndexSize);
  630. int newRef = curSubsetIndexSize - newBias;
  631. byte[] newRefBytes = createNewRef(newRef, operatorCode, -1);
  632. newData = concatArray(preBytes, newRefBytes);
  633. byte[] postBytes = new byte[currentData.length - (startRef + length)];
  634. System.arraycopy(currentData, startRef + length, postBytes, 0,
  635. currentData.length - (startRef + length));
  636. return concatArray(newData, postBytes);
  637. }
  638. public static byte[] createNewRef(int newRef, int[] operatorCode, int forceLength) {
  639. byte[] newRefBytes;
  640. int sizeOfOperator = operatorCode.length;
  641. if ((forceLength == -1 && newRef <= 107) || forceLength == 1) {
  642. newRefBytes = new byte[1 + sizeOfOperator];
  643. //The index values are 0 indexed
  644. newRefBytes[0] = (byte)(newRef + 139);
  645. for (int i = 0; i < operatorCode.length; i++) {
  646. newRefBytes[1 + i] = (byte)operatorCode[i];
  647. }
  648. } else if ((forceLength == -1 && newRef <= 1131) || forceLength == 2) {
  649. newRefBytes = new byte[2 + sizeOfOperator];
  650. if (newRef <= 363) {
  651. newRefBytes[0] = (byte)247;
  652. } else if (newRef <= 619) {
  653. newRefBytes[0] = (byte)248;
  654. } else if (newRef <= 875) {
  655. newRefBytes[0] = (byte)249;
  656. } else {
  657. newRefBytes[0] = (byte)250;
  658. }
  659. newRefBytes[1] = (byte)(newRef - 108);
  660. for (int i = 0; i < operatorCode.length; i++) {
  661. newRefBytes[2 + i] = (byte)operatorCode[i];
  662. }
  663. } else if ((forceLength == -1 && newRef <= 32767) || forceLength == 3) {
  664. newRefBytes = new byte[3 + sizeOfOperator];
  665. newRefBytes[0] = 28;
  666. newRefBytes[1] = (byte)(newRef >> 8);
  667. newRefBytes[2] = (byte)newRef;
  668. for (int i = 0; i < operatorCode.length; i++) {
  669. newRefBytes[3 + i] = (byte)operatorCode[i];
  670. }
  671. } else {
  672. newRefBytes = new byte[5 + sizeOfOperator];
  673. newRefBytes[0] = 29;
  674. newRefBytes[1] = (byte)(newRef >> 24);
  675. newRefBytes[2] = (byte)(newRef >> 16);
  676. newRefBytes[3] = (byte)(newRef >> 8);
  677. newRefBytes[4] = (byte)newRef;
  678. for (int i = 0; i < operatorCode.length; i++) {
  679. newRefBytes[5 + i] = (byte)operatorCode[i];
  680. }
  681. }
  682. return newRefBytes;
  683. }
  684. public static byte[] concatArray(byte[] a, byte[] b) {
  685. int aLen = a.length;
  686. int bLen = b.length;
  687. byte[] c = new byte[aLen + bLen];
  688. System.arraycopy(a, 0, c, 0, aLen);
  689. System.arraycopy(b, 0, c, aLen, bLen);
  690. return c;
  691. }
  692. protected int writeIndex(List<byte[]> dataArray) {
  693. int hdrTotal = 3;
  694. //2 byte number of items
  695. this.writeCard16(dataArray.size());
  696. //Offset Size: 1 byte = 256, 2 bytes = 65536 etc.
  697. int totLength = 0;
  698. for (int i = 0; i < dataArray.size(); i++) {
  699. totLength += dataArray.get(i).length;
  700. }
  701. int offSize = 1;
  702. if (totLength <= (1 << 8)) {
  703. offSize = 1;
  704. } else if (totLength <= (1 << 16)) {
  705. offSize = 2;
  706. } else if (totLength <= (1 << 24)) {
  707. offSize = 3;
  708. } else {
  709. offSize = 4;
  710. }
  711. this.writeByte(offSize);
  712. //Count the first offset 1
  713. hdrTotal += offSize;
  714. int total = 0;
  715. for (int i = 0; i < dataArray.size(); i++) {
  716. hdrTotal += offSize;
  717. int length = dataArray.get(i).length;
  718. switch (offSize) {
  719. case 1:
  720. if (i == 0) {
  721. writeByte(1);
  722. }
  723. total += length;
  724. writeByte(total + 1);
  725. break;
  726. case 2:
  727. if (i == 0) {
  728. writeCard16(1);
  729. }
  730. total += length;
  731. writeCard16(total + 1);
  732. break;
  733. case 3:
  734. if (i == 0) {
  735. writeThreeByteNumber(1);
  736. }
  737. total += length;
  738. writeThreeByteNumber(total + 1);
  739. break;
  740. case 4:
  741. if (i == 0) {
  742. writeULong(1);
  743. }
  744. total += length;
  745. writeULong(total + 1);
  746. break;
  747. default:
  748. throw new AssertionError("Offset Size was not an expected value.");
  749. }
  750. }
  751. for (int i = 0; i < dataArray.size(); i++) {
  752. writeBytes(dataArray.get(i));
  753. }
  754. return hdrTotal + total;
  755. }
  756. private BytesNumber readNumber(int b0, byte[] input, int curPos) throws IOException {
  757. if (b0 == 28) {
  758. int b1 = input[curPos + 1] & 0xff;
  759. int b2 = input[curPos + 2] & 0xff;
  760. return new BytesNumber(Integer.valueOf((short) (b1 << 8 | b2)), 3);
  761. } else if (b0 >= 32 && b0 <= 246) {
  762. return new BytesNumber(Integer.valueOf(b0 - 139), 1);
  763. } else if (b0 >= 247 && b0 <= 250) {
  764. int b1 = input[curPos + 1] & 0xff;
  765. return new BytesNumber(Integer.valueOf((b0 - 247) * 256 + b1 + 108), 2);
  766. } else if (b0 >= 251 && b0 <= 254) {
  767. int b1 = input[curPos + 1] & 0xff;
  768. return new BytesNumber(Integer.valueOf(-(b0 - 251) * 256 - b1 - 108), 2);
  769. } else if (b0 == 255) {
  770. int b1 = input[curPos + 1] & 0xff;
  771. int b2 = input[curPos + 2] & 0xff;
  772. return new BytesNumber(Integer.valueOf((short)(b1 << 8 | b2)), 5);
  773. } else {
  774. throw new IllegalArgumentException();
  775. }
  776. }
  777. /**
  778. * A class used to store the last number operand and also it's size in bytes
  779. */
  780. static class BytesNumber {
  781. private int number;
  782. private int numBytes;
  783. public BytesNumber(int number, int numBytes) {
  784. this.number = number;
  785. this.numBytes = numBytes;
  786. }
  787. public int getNumber() {
  788. return this.number;
  789. }
  790. public int getNumBytes() {
  791. return this.numBytes;
  792. }
  793. public void clearNumber() {
  794. this.number = -1;
  795. this.numBytes = -1;
  796. }
  797. public String toString() {
  798. return Integer.toString(number);
  799. }
  800. @Override
  801. public boolean equals(Object entry) {
  802. assert entry instanceof BytesNumber;
  803. BytesNumber bnEntry = (BytesNumber)entry;
  804. return this.number == bnEntry.getNumber()
  805. && this.numBytes == bnEntry.getNumBytes();
  806. }
  807. @Override
  808. public int hashCode() {
  809. int hash = 1;
  810. hash = hash * 17 + number;
  811. hash = hash * 31 + numBytes;
  812. return hash;
  813. }
  814. }
  815. private void writeCharsetTable(boolean cidFont) throws IOException {
  816. writeByte(0);
  817. for (int gid : gidToSID.keySet()) {
  818. if (cidFont && gid == 0) {
  819. continue;
  820. }
  821. writeCard16((cidFont) ? gid : gidToSID.get(gid));
  822. }
  823. }
  824. protected void writePrivateDict() throws IOException {
  825. Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
  826. DICTEntry privateEntry = topDICT.get("Private");
  827. if (privateEntry != null) {
  828. writeBytes(cffReader.getPrivateDictBytes(privateEntry));
  829. }
  830. }
  831. protected void updateOffsets(int topDictOffset, int charsetOffset, int charStringOffset,
  832. int privateDictOffset, int localIndexOffset, int encodingOffset)
  833. throws IOException {
  834. Map<String, DICTEntry> topDICT = cffReader.getTopDictEntries();
  835. Map<String, DICTEntry> privateDICT = null;
  836. DICTEntry privateEntry = topDICT.get("Private");
  837. if (privateEntry != null) {
  838. privateDICT = cffReader.getPrivateDict(privateEntry);
  839. }
  840. int dataPos = 3 + (cffReader.getTopDictIndex().getOffSize()
  841. * cffReader.getTopDictIndex().getOffsets().length);
  842. int dataTopDictOffset = topDictOffset + dataPos;
  843. updateFixedOffsets(topDICT, dataTopDictOffset, charsetOffset, charStringOffset, encodingOffset);
  844. if (privateDICT != null) {
  845. //Private index offset in the top dict
  846. int oldPrivateOffset = dataTopDictOffset + privateEntry.getOffset();
  847. output = updateOffset(output, oldPrivateOffset + privateEntry.getOperandLengths().get(0),
  848. privateEntry.getOperandLengths().get(1), privateDictOffset);
  849. //Update the local subroutine index offset in the private dict
  850. DICTEntry subroutines = privateDICT.get("Subrs");
  851. if (subroutines != null) {
  852. int oldLocalSubrOffset = privateDictOffset + subroutines.getOffset();
  853. //Value needs to be converted to -139 etc.
  854. int encodeValue = 0;
  855. if (subroutines.getOperandLength() == 1) {
  856. encodeValue = 139;
  857. }
  858. output = updateOffset(output, oldLocalSubrOffset, subroutines.getOperandLength(),
  859. (localIndexOffset - privateDictOffset) + encodeValue);
  860. }
  861. }
  862. }
  863. protected void updateFixedOffsets(Map<String, DICTEntry> topDICT, int dataTopDictOffset,
  864. int charsetOffset, int charStringOffset, int encodingOffset) {
  865. //Charset offset in the top dict
  866. DICTEntry charset = topDICT.get("charset");
  867. int oldCharsetOffset = dataTopDictOffset + charset.getOffset();
  868. output = updateOffset(output, oldCharsetOffset, charset.getOperandLength(), charsetOffset);
  869. //Char string index offset in the private dict
  870. DICTEntry charString = topDICT.get("CharStrings");
  871. int oldCharStringOffset = dataTopDictOffset + charString.getOffset();
  872. output = updateOffset(output, oldCharStringOffset, charString.getOperandLength(), charStringOffset);
  873. DICTEntry encodingEntry = topDICT.get("Encoding");
  874. if (encodingEntry != null && encodingEntry.getOperands().get(0).intValue() != 0
  875. && encodingEntry.getOperands().get(0).intValue() != 1) {
  876. int oldEncodingOffset = dataTopDictOffset + encodingEntry.getOffset();
  877. output = updateOffset(output, oldEncodingOffset, encodingEntry.getOperandLength(), encodingOffset);
  878. }
  879. }
  880. protected void updateCIDOffsets(int topDictDataOffset, int fdArrayOffset, int fdSelectOffset,
  881. int charsetOffset, int charStringOffset, int encodingOffset) {
  882. LinkedHashMap<String, DICTEntry> topDict = cffReader.getTopDictEntries();
  883. DICTEntry fdArrayEntry = topDict.get("FDArray");
  884. if (fdArrayEntry != null) {
  885. output = updateOffset(output, topDictDataOffset + fdArrayEntry.getOffset() - 1,
  886. fdArrayEntry.getOperandLength(), fdArrayOffset);
  887. }
  888. DICTEntry fdSelect = topDict.get("FDSelect");
  889. if (fdSelect != null) {
  890. output = updateOffset(output, topDictDataOffset + fdSelect.getOffset() - 1,
  891. fdSelect.getOperandLength(), fdSelectOffset);
  892. }
  893. updateFixedOffsets(topDict, topDictDataOffset, charsetOffset, charStringOffset, encodingOffset);
  894. }
  895. protected byte[] updateOffset(byte[] out, int position, int length, int replacement) {
  896. switch (length) {
  897. case 1:
  898. out[position] = (byte)(replacement & 0xFF);
  899. break;
  900. case 2:
  901. if (replacement <= 363) {
  902. out[position] = (byte)247;
  903. } else if (replacement <= 619) {
  904. out[position] = (byte)248;
  905. } else if (replacement <= 875) {
  906. out[position] = (byte)249;
  907. } else {
  908. out[position] = (byte)250;
  909. }
  910. out[position + 1] = (byte)(replacement - 108);
  911. break;
  912. case 3:
  913. out[position] = (byte)28;
  914. out[position + 1] = (byte)((replacement >> 8) & 0xFF);
  915. out[position + 2] = (byte)(replacement & 0xFF);
  916. break;
  917. case 5:
  918. out[position] = (byte)29;
  919. out[position + 1] = (byte)((replacement >> 24) & 0xFF);
  920. out[position + 2] = (byte)((replacement >> 16) & 0xFF);
  921. out[position + 3] = (byte)((replacement >> 8) & 0xFF);
  922. out[position + 4] = (byte)(replacement & 0xFF);
  923. break;
  924. default:
  925. }
  926. return out;
  927. }
  928. /**
  929. * Appends a byte to the output array,
  930. * updates currentPost but not realSize
  931. */
  932. protected void writeByte(int b) {
  933. output[currentPos++] = (byte)b;
  934. realSize++;
  935. }
  936. /**
  937. * Appends a USHORT to the output array,
  938. * updates currentPost but not realSize
  939. */
  940. protected void writeCard16(int s) {
  941. byte b1 = (byte)((s >> 8) & 0xff);
  942. byte b2 = (byte)(s & 0xff);
  943. writeByte(b1);
  944. writeByte(b2);
  945. }
  946. private void writeThreeByteNumber(int s) {
  947. byte b1 = (byte)((s >> 16) & 0xFF);
  948. byte b2 = (byte)((s >> 8) & 0xFF);
  949. byte b3 = (byte)(s & 0xFF);
  950. writeByte(b1);
  951. writeByte(b2);
  952. writeByte(b3);
  953. }
  954. /**
  955. * Appends a ULONG to the output array,
  956. * at the given position
  957. */
  958. private void writeULong(int s) {
  959. byte b1 = (byte)((s >> 24) & 0xff);
  960. byte b2 = (byte)((s >> 16) & 0xff);
  961. byte b3 = (byte)((s >> 8) & 0xff);
  962. byte b4 = (byte)(s & 0xff);
  963. writeByte(b1);
  964. writeByte(b2);
  965. writeByte(b3);
  966. writeByte(b4);
  967. }
  968. /**
  969. * Returns a subset of the fonts (readFont() MUST be called first in order to create the
  970. * subset).
  971. * @return byte array
  972. */
  973. public byte[] getFontSubset() {
  974. byte[] ret = new byte[realSize];
  975. System.arraycopy(output, 0, ret, 0, realSize);
  976. return ret;
  977. }
  978. /**
  979. * Returns the parsed CFF data for the original font.
  980. * @return The CFFDataReader contaiing the parsed data
  981. */
  982. public CFFDataReader getCFFReader() {
  983. return cffReader;
  984. }
  985. }