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.

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