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

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