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

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