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 48KB

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