Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

OTFSubSetFile.java 49KB

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