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

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