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.

GlyfTableTestCase.java 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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.ByteArrayInputStream;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.util.Arrays;
  23. import java.util.HashMap;
  24. import java.util.Map;
  25. import junit.framework.TestCase;
  26. /**
  27. * Tests {@link GlyfTable}.
  28. */
  29. public class GlyfTableTestCase extends TestCase {
  30. private final static class DirData {
  31. final long offset;
  32. final long length;
  33. DirData(long offset, long length) {
  34. this.offset = offset;
  35. this.length = length;
  36. }
  37. }
  38. private FontFileReader subsetReader;
  39. private long[] glyphOffsets;
  40. private FontFileReader originalFontReader;
  41. @Override
  42. public void setUp() throws IOException {
  43. originalFontReader = new FontFileReader("test/resources/fonts/DejaVuLGCSerif.ttf");
  44. }
  45. /**
  46. * Tests that composed glyphs are included in the glyph subset if a composite glyph is used.
  47. *
  48. * @throws IOException if an I/O error occurs
  49. */
  50. public void testPopulateGlyphsWithComposites() throws IOException {
  51. // Glyph 408 -> U+01D8 "uni01D8" this is a composite glyph.
  52. int[] composedIndices = setupTest(408);
  53. int[] expected = new int[composedIndices.length];
  54. expected[1] = 6;
  55. expected[5] = 2;
  56. expected[6] = 4;
  57. assertArrayEquals(expected, composedIndices);
  58. }
  59. /**
  60. * Tests that no glyphs are added if there are no composite glyphs the subset.
  61. *
  62. * @throws IOException if an I/O error occurs
  63. */
  64. public void testPopulateNoCompositeGlyphs() throws IOException {
  65. int[] composedIndices = setupTest(36, 37, 38); // "A", "B", "C"
  66. int[] expected = new int[composedIndices.length];
  67. // There should be NO composite glyphs
  68. assertArrayEquals(expected, composedIndices);
  69. }
  70. /**
  71. * Tests that glyphs aren't remapped twice if the glyph before a composite glyph has 0-length.
  72. *
  73. * @throws IOException if an I/O error occurs
  74. */
  75. public void testGlyphsNotRemappedTwice() throws IOException {
  76. int composedGlyph = 12;
  77. // The order of these glyph indices, must NOT be changed! (see javadoc above)
  78. int[] composedIndices = setupTest(1, 2, 3, 16, 2014, 4, 7, 8, 13, 2015, composedGlyph);
  79. // There are 2 composed glyphs within the subset
  80. int[] expected = new int[composedIndices.length];
  81. expected[10] = composedGlyph;
  82. assertArrayEquals(expected, composedIndices);
  83. }
  84. /**
  85. * Tests that the correct glyph is included in the subset, when a composite glyph composed of a
  86. * composite glyph is used.
  87. *
  88. * @throws IOException if an I/O error occurs
  89. */
  90. public void testSingleRecursionStep() throws IOException {
  91. // Glyph 2077 -> U+283F "uni283F" this is composed of a composite glyph (recursive).
  92. int[] composedIndices = setupTest(2077);
  93. int[] expected = new int[composedIndices.length];
  94. expected[1] = 2;
  95. assertArrayEquals(expected, composedIndices);
  96. }
  97. private int[] setupTest(int... glyphIndices) throws IOException {
  98. Map<Integer, Integer> glyphs = new HashMap<Integer, Integer>();
  99. int index = 0;
  100. glyphs.put(0, index++); // Glyph 0 (.notdef) must ALWAYS be in the subset
  101. for (int glyphIndex : glyphIndices) {
  102. glyphs.put(glyphIndex, index++);
  103. }
  104. setupSubsetReader(glyphs);
  105. readLoca();
  106. return retrieveIndicesOfComposedGlyphs();
  107. }
  108. private void setupSubsetReader(Map<Integer, Integer> glyphs) throws IOException {
  109. TTFSubSetFile fontFile = new TTFSubSetFile();
  110. byte[] subsetFont = fontFile.readFont(originalFontReader, "Deja", glyphs);
  111. InputStream intputStream = new ByteArrayInputStream(subsetFont);
  112. subsetReader = new FontFileReader(intputStream);
  113. }
  114. private void readLoca() throws IOException {
  115. DirData loca = getTableData("loca");
  116. int numberOfGlyphs = (int) (loca.length - 4) / 4;
  117. glyphOffsets = new long[numberOfGlyphs];
  118. subsetReader.seekSet(loca.offset);
  119. for (int i = 0; i < numberOfGlyphs; i++) {
  120. glyphOffsets[i] = subsetReader.readTTFULong();
  121. }
  122. }
  123. private int[] retrieveIndicesOfComposedGlyphs() throws IOException {
  124. DirData glyf = getTableData("glyf");
  125. int[] composedGlyphIndices = new int[glyphOffsets.length];
  126. for (int i = 0; i < glyphOffsets.length; i++) {
  127. long glyphOffset = glyphOffsets[i];
  128. if (i != glyphOffsets.length - 1 && glyphOffset == glyphOffsets[i + 1]) {
  129. continue;
  130. }
  131. subsetReader.seekSet(glyf.offset + glyphOffset);
  132. short numberOfContours = subsetReader.readTTFShort();
  133. if (numberOfContours < 0) {
  134. subsetReader.skip(8);
  135. subsetReader.readTTFUShort(); // flags
  136. int glyphIndex = subsetReader.readTTFUShort();
  137. composedGlyphIndices[i] = glyphIndex;
  138. }
  139. }
  140. return composedGlyphIndices;
  141. }
  142. private DirData getTableData(String tableName) throws IOException {
  143. subsetReader.seekSet(0);
  144. subsetReader.skip(12);
  145. String name;
  146. do {
  147. name = subsetReader.readTTFString(4);
  148. subsetReader.skip(4 * 3);
  149. } while (!name.equals(tableName));
  150. subsetReader.skip(-8); // We've found the table, go back to get the data we skipped over
  151. return new DirData(subsetReader.readTTFLong(), subsetReader.readTTFLong());
  152. }
  153. private void assertArrayEquals(int[] expected, int[] actual) {
  154. assertTrue(Arrays.equals(expected, actual));
  155. }
  156. }