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.

TTFSubSetFile.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  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.Iterator;
  21. import java.util.Map;
  22. import java.util.Set;
  23. /**
  24. * Reads a TrueType file and generates a subset
  25. * that can be used to embed a TrueType CID font.
  26. * TrueType tables needed for embedded CID fonts are:
  27. * "head", "hhea", "loca", "maxp", "cvt ", "prep", "glyf", "hmtx" and "fpgm".
  28. * The TrueType spec can be found at the Microsoft
  29. * Typography site: http://www.microsoft.com/truetype/
  30. */
  31. public class TTFSubSetFile extends TTFFile {
  32. private byte[] output = null;
  33. private int realSize = 0;
  34. private int currentPos = 0;
  35. /*
  36. * Offsets in name table to be filled out by table.
  37. * The offsets are to the checkSum field
  38. */
  39. private int cffDirOffset = 0;
  40. private int cvtDirOffset = 0;
  41. private int fpgmDirOffset = 0;
  42. private int glyfDirOffset = 0;
  43. private int headDirOffset = 0;
  44. private int hheaDirOffset = 0;
  45. private int hmtxDirOffset = 0;
  46. private int locaDirOffset = 0;
  47. private int maxpDirOffset = 0;
  48. private int prepDirOffset = 0;
  49. private int checkSumAdjustmentOffset = 0;
  50. private int locaOffset = 0;
  51. /**
  52. * Default Constructor
  53. */
  54. public TTFSubSetFile() {
  55. this(false, false);
  56. }
  57. /**
  58. * Constructor
  59. * @param useKerning true if kerning data should be loaded
  60. * @param useAdvanced true if advanced typographic tables should be loaded
  61. */
  62. public TTFSubSetFile ( boolean useKerning, boolean useAdvanced ) {
  63. super(useKerning, useAdvanced);
  64. }
  65. /**
  66. * Initalize the output array
  67. */
  68. private void init(int size) {
  69. output = new byte[size];
  70. realSize = 0;
  71. currentPos = 0;
  72. // createDirectory()
  73. }
  74. private int determineTableCount() {
  75. int numTables = 4; //4 req'd tables: head,hhea,hmtx,maxp
  76. if (isCFF()) {
  77. numTables += 1; //1 req'd table: CFF
  78. } else {
  79. if (hasCvt()) {
  80. numTables++;
  81. }
  82. if (hasFpgm()) {
  83. numTables++;
  84. }
  85. if (hasPrep()) {
  86. numTables++;
  87. }
  88. numTables += 2; //1 req'd table: loca,glyf
  89. }
  90. return numTables;
  91. }
  92. /**
  93. * Create the directory table
  94. */
  95. private void createDirectory() {
  96. int numTables = determineTableCount();
  97. // Create the TrueType header
  98. if (isCFF() ) {
  99. writeByte((byte)'O');
  100. writeByte((byte)'T');
  101. writeByte((byte)'T');
  102. writeByte((byte)'O');
  103. } else {
  104. writeByte((byte)0);
  105. writeByte((byte)1);
  106. writeByte((byte)0);
  107. writeByte((byte)0);
  108. }
  109. realSize += 4;
  110. writeUShort(numTables);
  111. realSize += 2;
  112. // Create searchRange, entrySelector and rangeShift
  113. int maxPow = maxPow2(numTables);
  114. int searchRange = maxPow * 16;
  115. writeUShort(searchRange);
  116. realSize += 2;
  117. writeUShort(maxPow);
  118. realSize += 2;
  119. writeUShort((numTables * 16) - searchRange);
  120. realSize += 2;
  121. // Create space for the table entries
  122. writeString("head");
  123. headDirOffset = currentPos;
  124. currentPos += 12;
  125. realSize += 16;
  126. writeString("hhea");
  127. hheaDirOffset = currentPos;
  128. currentPos += 12;
  129. realSize += 16;
  130. writeString("hmtx");
  131. hmtxDirOffset = currentPos;
  132. currentPos += 12;
  133. realSize += 16;
  134. writeString("maxp");
  135. maxpDirOffset = currentPos;
  136. currentPos += 12;
  137. realSize += 16;
  138. if (hasCvt()) {
  139. writeString("cvt ");
  140. cvtDirOffset = currentPos;
  141. currentPos += 12;
  142. realSize += 16;
  143. }
  144. if (hasFpgm()) {
  145. writeString("fpgm");
  146. fpgmDirOffset = currentPos;
  147. currentPos += 12;
  148. realSize += 16;
  149. }
  150. if (hasPrep()) {
  151. writeString("prep");
  152. prepDirOffset = currentPos;
  153. currentPos += 12;
  154. realSize += 16;
  155. }
  156. if (isCFF()) {
  157. writeString("CFF ");
  158. cffDirOffset = currentPos;
  159. currentPos += 12;
  160. realSize += 16;
  161. } else {
  162. writeString("loca");
  163. locaDirOffset = currentPos;
  164. currentPos += 12;
  165. realSize += 16;
  166. writeString("glyf");
  167. glyfDirOffset = currentPos;
  168. currentPos += 12;
  169. realSize += 16;
  170. }
  171. }
  172. /**
  173. * Copy the cvt table as is from original font to subset font
  174. */
  175. private boolean createCvt(FontFileReader in) throws IOException {
  176. TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("cvt ");
  177. if (entry != null) {
  178. pad4();
  179. seekTab(in, "cvt ", 0);
  180. System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
  181. 0, output, currentPos, (int)entry.getLength());
  182. int checksum = getCheckSum(currentPos, (int)entry.getLength());
  183. writeULong(cvtDirOffset, checksum);
  184. writeULong(cvtDirOffset + 4, currentPos);
  185. writeULong(cvtDirOffset + 8, (int)entry.getLength());
  186. currentPos += (int)entry.getLength();
  187. realSize += (int)entry.getLength();
  188. return true;
  189. } else {
  190. return false;
  191. //throw new IOException("Can't find cvt table");
  192. }
  193. }
  194. private boolean hasCvt() {
  195. return dirTabs.containsKey("cvt ");
  196. }
  197. private boolean hasFpgm() {
  198. return dirTabs.containsKey("fpgm");
  199. }
  200. private boolean hasPrep() {
  201. return dirTabs.containsKey("prep");
  202. }
  203. /**
  204. * Copy the fpgm table as is from original font to subset font
  205. */
  206. private boolean createFpgm(FontFileReader in) throws IOException {
  207. TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("fpgm");
  208. if (entry != null) {
  209. pad4();
  210. seekTab(in, "fpgm", 0);
  211. System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
  212. 0, output, currentPos, (int)entry.getLength());
  213. int checksum = getCheckSum(currentPos, (int)entry.getLength());
  214. writeULong(fpgmDirOffset, checksum);
  215. writeULong(fpgmDirOffset + 4, currentPos);
  216. writeULong(fpgmDirOffset + 8, (int)entry.getLength());
  217. currentPos += (int)entry.getLength();
  218. realSize += (int)entry.getLength();
  219. return true;
  220. } else {
  221. return false;
  222. }
  223. }
  224. /**
  225. * Create an empty loca table without updating checksum
  226. */
  227. private void createLoca(int size) throws IOException {
  228. pad4();
  229. locaOffset = currentPos;
  230. writeULong(locaDirOffset + 4, currentPos);
  231. writeULong(locaDirOffset + 8, size * 4 + 4);
  232. currentPos += size * 4 + 4;
  233. realSize += size * 4 + 4;
  234. }
  235. /**
  236. * Copy the maxp table as is from original font to subset font
  237. * and set num glyphs to size
  238. */
  239. private void createMaxp(FontFileReader in, int size) throws IOException {
  240. TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp");
  241. if (entry != null) {
  242. pad4();
  243. seekTab(in, "maxp", 0);
  244. System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
  245. 0, output, currentPos, (int)entry.getLength());
  246. writeUShort(currentPos + 4, size);
  247. int checksum = getCheckSum(currentPos, (int)entry.getLength());
  248. writeULong(maxpDirOffset, checksum);
  249. writeULong(maxpDirOffset + 4, currentPos);
  250. writeULong(maxpDirOffset + 8, (int)entry.getLength());
  251. currentPos += (int)entry.getLength();
  252. realSize += (int)entry.getLength();
  253. } else {
  254. throw new IOException("Can't find maxp table");
  255. }
  256. }
  257. /**
  258. * Copy the prep table as is from original font to subset font
  259. */
  260. private boolean createPrep(FontFileReader in) throws IOException {
  261. TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("prep");
  262. if (entry != null) {
  263. pad4();
  264. seekTab(in, "prep", 0);
  265. System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
  266. 0, output, currentPos, (int)entry.getLength());
  267. int checksum = getCheckSum(currentPos, (int)entry.getLength());
  268. writeULong(prepDirOffset, checksum);
  269. writeULong(prepDirOffset + 4, currentPos);
  270. writeULong(prepDirOffset + 8, (int)entry.getLength());
  271. currentPos += (int)entry.getLength();
  272. realSize += (int)entry.getLength();
  273. return true;
  274. } else {
  275. return false;
  276. }
  277. }
  278. /**
  279. * Copy the hhea table as is from original font to subset font
  280. * and fill in size of hmtx table
  281. */
  282. private void createHhea(FontFileReader in, int size) throws IOException {
  283. TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hhea");
  284. if (entry != null) {
  285. pad4();
  286. seekTab(in, "hhea", 0);
  287. System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
  288. 0, output, currentPos, (int)entry.getLength());
  289. writeUShort((int)entry.getLength() + currentPos - 2, size);
  290. int checksum = getCheckSum(currentPos, (int)entry.getLength());
  291. writeULong(hheaDirOffset, checksum);
  292. writeULong(hheaDirOffset + 4, currentPos);
  293. writeULong(hheaDirOffset + 8, (int)entry.getLength());
  294. currentPos += (int)entry.getLength();
  295. realSize += (int)entry.getLength();
  296. } else {
  297. throw new IOException("Can't find hhea table");
  298. }
  299. }
  300. /**
  301. * Copy the head table as is from original font to subset font
  302. * and set indexToLocaFormat to long and set
  303. * checkSumAdjustment to 0, store offset to checkSumAdjustment
  304. * in checkSumAdjustmentOffset
  305. */
  306. private void createHead(FontFileReader in) throws IOException {
  307. TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head");
  308. if (entry != null) {
  309. pad4();
  310. seekTab(in, "head", 0);
  311. System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()),
  312. 0, output, currentPos, (int)entry.getLength());
  313. checkSumAdjustmentOffset = currentPos + 8;
  314. output[currentPos + 8] = 0; // Set checkSumAdjustment to 0
  315. output[currentPos + 9] = 0;
  316. output[currentPos + 10] = 0;
  317. output[currentPos + 11] = 0;
  318. output[currentPos + 50] = 0; // long locaformat
  319. output[currentPos + 51] = 1; // long locaformat
  320. int checksum = getCheckSum(currentPos, (int)entry.getLength());
  321. writeULong(headDirOffset, checksum);
  322. writeULong(headDirOffset + 4, currentPos);
  323. writeULong(headDirOffset + 8, (int)entry.getLength());
  324. currentPos += (int)entry.getLength();
  325. realSize += (int)entry.getLength();
  326. } else {
  327. throw new IOException("Can't find head table");
  328. }
  329. }
  330. /**
  331. * Create the glyf table and fill in loca table
  332. */
  333. private void createGlyf(FontFileReader in,
  334. Map glyphs) throws IOException {
  335. TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf");
  336. int size = 0;
  337. int start = 0;
  338. int endOffset = 0; // Store this as the last loca
  339. if (entry != null) {
  340. pad4();
  341. start = currentPos;
  342. /* Loca table must be in order by glyph index, so build
  343. * an array first and then write the glyph info and
  344. * location offset.
  345. */
  346. int[] origIndexes = new int[glyphs.size()];
  347. Iterator e = glyphs.keySet().iterator();
  348. while (e.hasNext()) {
  349. Integer origIndex = (Integer)e.next();
  350. Integer subsetIndex = (Integer)glyphs.get(origIndex);
  351. origIndexes[subsetIndex.intValue()] = origIndex.intValue();
  352. }
  353. for (int i = 0; i < origIndexes.length; i++) {
  354. int glyphLength = 0;
  355. int nextOffset = 0;
  356. int origGlyphIndex = origIndexes[i];
  357. if (origGlyphIndex >= (mtxTab.length - 1)) {
  358. nextOffset = (int)lastLoca;
  359. } else {
  360. nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset();
  361. }
  362. glyphLength = nextOffset - (int)mtxTab[origGlyphIndex].getOffset();
  363. // Copy glyph
  364. System.arraycopy(
  365. in.getBytes((int)entry.getOffset() + (int)mtxTab[origGlyphIndex].getOffset(),
  366. glyphLength), 0,
  367. output, currentPos,
  368. glyphLength);
  369. // Update loca table
  370. writeULong(locaOffset + i * 4, currentPos - start);
  371. if ((currentPos - start + glyphLength) > endOffset) {
  372. endOffset = (currentPos - start + glyphLength);
  373. }
  374. currentPos += glyphLength;
  375. realSize += glyphLength;
  376. }
  377. size = currentPos - start;
  378. int checksum = getCheckSum(start, size);
  379. writeULong(glyfDirOffset, checksum);
  380. writeULong(glyfDirOffset + 4, start);
  381. writeULong(glyfDirOffset + 8, size);
  382. currentPos += 12;
  383. realSize += 12;
  384. // Update loca checksum and last loca index
  385. writeULong(locaOffset + glyphs.size() * 4, endOffset);
  386. checksum = getCheckSum(locaOffset, glyphs.size() * 4 + 4);
  387. writeULong(locaDirOffset, checksum);
  388. } else {
  389. throw new IOException("Can't find glyf table");
  390. }
  391. }
  392. /**
  393. * Create the CFF table
  394. */
  395. private void createCff(FontFileReader in, Map glyphs) throws IOException {
  396. TTFDirTabEntry entry = (TTFDirTabEntry) dirTabs.get("CFF ");
  397. int size = 0;
  398. int start = 0;
  399. if ( entry != null ) {
  400. pad4();
  401. start = currentPos;
  402. byte[] cffData = CFFUtil.extractGlyphSubset ( in, entry, glyphs );
  403. int cffDataLength = cffData.length;
  404. System.arraycopy ( cffData, 0, output, currentPos, cffDataLength );
  405. currentPos += cffDataLength;
  406. realSize += cffDataLength;
  407. size = currentPos - start;
  408. int checksum = getCheckSum(start, size);
  409. writeULong(cffDirOffset, checksum);
  410. writeULong(cffDirOffset + 4, start);
  411. writeULong(cffDirOffset + 8, size);
  412. currentPos += 12;
  413. realSize += 12;
  414. } else {
  415. throw new IOException("Can't find CFF table");
  416. }
  417. }
  418. /**
  419. * Create the hmtx table by copying metrics from original
  420. * font to subset font. The glyphs Map contains an
  421. * Integer key and Integer value that maps the original
  422. * metric (key) to the subset metric (value)
  423. */
  424. private void createHmtx(FontFileReader in,
  425. Map glyphs) throws IOException {
  426. TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx");
  427. int longHorMetricSize = glyphs.size() * 2;
  428. int leftSideBearingSize = glyphs.size() * 2;
  429. int hmtxSize = longHorMetricSize + leftSideBearingSize;
  430. if (entry != null) {
  431. pad4();
  432. //int offset = (int)entry.offset;
  433. Iterator e = glyphs.keySet().iterator();
  434. while (e.hasNext()) {
  435. Integer origIndex = (Integer)e.next();
  436. Integer subsetIndex = (Integer)glyphs.get(origIndex);
  437. writeUShort(currentPos + subsetIndex.intValue() * 4,
  438. mtxTab[origIndex.intValue()].getWx());
  439. writeUShort(currentPos + subsetIndex.intValue() * 4 + 2,
  440. mtxTab[origIndex.intValue()].getLsb());
  441. }
  442. int checksum = getCheckSum(currentPos, hmtxSize);
  443. writeULong(hmtxDirOffset, checksum);
  444. writeULong(hmtxDirOffset + 4, currentPos);
  445. writeULong(hmtxDirOffset + 8, hmtxSize);
  446. currentPos += hmtxSize;
  447. realSize += hmtxSize;
  448. } else {
  449. throw new IOException("Can't find hmtx table");
  450. }
  451. }
  452. /**
  453. * Returns a subset of the original font.
  454. *
  455. * @param in FontFileReader to read from
  456. * @param name Name to be checked for in the font file
  457. * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
  458. * new index as (Integer) value)
  459. * @return A subset of the original font
  460. * @throws IOException in case of an I/O problem
  461. */
  462. public byte[] readFont(FontFileReader in, String name,
  463. Map<Integer, Integer> glyphs) throws IOException {
  464. //Check if TrueType collection, and that the name exists in the collection
  465. if (!checkTTC(in, name)) {
  466. throw new IOException("Failed to read font");
  467. }
  468. //Copy the Map as we're going to modify it
  469. Map<Integer, Integer> subsetGlyphs = new java.util.HashMap<Integer, Integer>(glyphs);
  470. output = new byte[in.getFileSize()];
  471. readDirTabs(in);
  472. readFontHeader(in);
  473. getNumGlyphs(in);
  474. readHorizontalHeader(in);
  475. readHorizontalMetrics(in);
  476. if (!isCFF()) {
  477. readIndexToLocation(in);
  478. scanGlyphs(in, subsetGlyphs);
  479. }
  480. createDirectory(); // Create the TrueType header and directory
  481. createHead(in);
  482. createHhea(in, subsetGlyphs.size()); // Create the hhea table
  483. createHmtx(in, subsetGlyphs); // Create hmtx table
  484. createMaxp(in, subsetGlyphs.size()); // copy the maxp table
  485. boolean optionalTableFound;
  486. optionalTableFound = createCvt(in); // copy the cvt table
  487. if (!optionalTableFound) {
  488. // cvt is optional (used in TrueType fonts only)
  489. log.debug("TrueType: ctv table not present. Skipped.");
  490. }
  491. optionalTableFound = createFpgm(in); // copy fpgm table
  492. if (!optionalTableFound) {
  493. // fpgm is optional (used in TrueType fonts only)
  494. log.debug("TrueType: fpgm table not present. Skipped.");
  495. }
  496. optionalTableFound = createPrep(in); // copy prep table
  497. if (!optionalTableFound) {
  498. // prep is optional (used in TrueType fonts only)
  499. log.debug("TrueType: prep table not present. Skipped.");
  500. }
  501. if (isCFF()) {
  502. createCff(in, subsetGlyphs);
  503. } else {
  504. createLoca(subsetGlyphs.size()); // create empty loca table
  505. createGlyf(in, subsetGlyphs); //create glyf table and update loca table
  506. }
  507. pad4();
  508. createCheckSumAdjustment();
  509. byte[] ret = new byte[realSize];
  510. System.arraycopy(output, 0, ret, 0, realSize);
  511. return ret;
  512. }
  513. private void scanGlyphs(FontFileReader in, Map<Integer, Integer> subsetGlyphs)
  514. throws IOException {
  515. TTFDirTabEntry glyfTableInfo = (TTFDirTabEntry) dirTabs.get("glyf");
  516. if (glyfTableInfo == null) {
  517. throw new IOException("Glyf table could not be found");
  518. }
  519. GlyfTable glyfTable = new GlyfTable(in, mtxTab, glyfTableInfo, subsetGlyphs);
  520. glyfTable.populateGlyphsWithComposites();
  521. }
  522. /**
  523. * writes a ISO-8859-1 string at the currentPosition
  524. * updates currentPosition but not realSize
  525. * @return number of bytes written
  526. */
  527. private int writeString(String str) {
  528. int length = 0;
  529. try {
  530. byte[] buf = str.getBytes("ISO-8859-1");
  531. System.arraycopy(buf, 0, output, currentPos, buf.length);
  532. length = buf.length;
  533. currentPos += length;
  534. } catch (java.io.UnsupportedEncodingException e) {
  535. // This should never happen!
  536. }
  537. return length;
  538. }
  539. /**
  540. * Appends a byte to the output array,
  541. * updates currentPost but not realSize
  542. */
  543. private void writeByte(byte b) {
  544. output[currentPos++] = b;
  545. }
  546. /**
  547. * Appends a USHORT to the output array,
  548. * updates currentPost but not realSize
  549. */
  550. private void writeUShort(int s) {
  551. byte b1 = (byte)((s >> 8) & 0xff);
  552. byte b2 = (byte)(s & 0xff);
  553. writeByte(b1);
  554. writeByte(b2);
  555. }
  556. /**
  557. * Appends a USHORT to the output array,
  558. * at the given position without changing currentPos
  559. */
  560. private void writeUShort(int pos, int s) {
  561. byte b1 = (byte)((s >> 8) & 0xff);
  562. byte b2 = (byte)(s & 0xff);
  563. output[pos] = b1;
  564. output[pos + 1] = b2;
  565. }
  566. /**
  567. * Appends a ULONG to the output array,
  568. * updates currentPos but not realSize
  569. */
  570. private void writeULong(int s) {
  571. byte b1 = (byte)((s >> 24) & 0xff);
  572. byte b2 = (byte)((s >> 16) & 0xff);
  573. byte b3 = (byte)((s >> 8) & 0xff);
  574. byte b4 = (byte)(s & 0xff);
  575. writeByte(b1);
  576. writeByte(b2);
  577. writeByte(b3);
  578. writeByte(b4);
  579. }
  580. /**
  581. * Appends a ULONG to the output array,
  582. * at the given position without changing currentPos
  583. */
  584. private void writeULong(int pos, int s) {
  585. byte b1 = (byte)((s >> 24) & 0xff);
  586. byte b2 = (byte)((s >> 16) & 0xff);
  587. byte b3 = (byte)((s >> 8) & 0xff);
  588. byte b4 = (byte)(s & 0xff);
  589. output[pos] = b1;
  590. output[pos + 1] = b2;
  591. output[pos + 2] = b3;
  592. output[pos + 3] = b4;
  593. }
  594. /**
  595. * Read a signed short value at given position
  596. */
  597. private short readShort(int pos) {
  598. int ret = readUShort(pos);
  599. return (short)ret;
  600. }
  601. /**
  602. * Read a unsigned short value at given position
  603. */
  604. private int readUShort(int pos) {
  605. int ret = output[pos];
  606. if (ret < 0) {
  607. ret += 256;
  608. }
  609. ret = ret << 8;
  610. if (output[pos + 1] < 0) {
  611. ret |= output[pos + 1] + 256;
  612. } else {
  613. ret |= output[pos + 1];
  614. }
  615. return ret;
  616. }
  617. /**
  618. * Create a padding in the fontfile to align
  619. * on a 4-byte boundary
  620. */
  621. private void pad4() {
  622. int padSize = currentPos % 4;
  623. for (int i = 0; i < padSize; i++) {
  624. output[currentPos++] = 0;
  625. realSize++;
  626. }
  627. }
  628. /**
  629. * Returns the maximum power of 2 <= max
  630. */
  631. private int maxPow2(int max) {
  632. int i = 0;
  633. while (Math.pow(2, i) < max) {
  634. i++;
  635. }
  636. return (i - 1);
  637. }
  638. private int log2(int num) {
  639. return (int)(Math.log(num) / Math.log(2));
  640. }
  641. private int getCheckSum(int start, int size) {
  642. return (int)getLongCheckSum(start, size);
  643. }
  644. private long getLongCheckSum(int start, int size) {
  645. // All the tables here are aligned on four byte boundaries
  646. // Add remainder to size if it's not a multiple of 4
  647. int remainder = size % 4;
  648. if (remainder != 0) {
  649. size += remainder;
  650. }
  651. long sum = 0;
  652. for (int i = 0; i < size; i += 4) {
  653. int l = (output[start + i] << 24);
  654. l += (output[start + i + 1] << 16);
  655. l += (output[start + i + 2] << 16);
  656. l += (output[start + i + 3] << 16);
  657. sum += l;
  658. if (sum > 0xffffffff) {
  659. sum = sum - 0xffffffff;
  660. }
  661. }
  662. return sum;
  663. }
  664. private void createCheckSumAdjustment() {
  665. long sum = getLongCheckSum(0, realSize);
  666. int checksum = (int)(0xb1b0afba - sum);
  667. writeULong(checkSumAdjustmentOffset, checksum);
  668. }
  669. }