| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* $Id$ */ |
| package org.apache.fop.render.pcl.fonts; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.when; |
| |
| import org.apache.fop.fonts.CustomFont; |
| import org.apache.fop.fonts.SingleByteFont; |
| import org.apache.fop.render.java2d.CustomFontMetricsMapper; |
| import org.apache.fop.render.pcl.fonts.PCLFontSegment.SegmentID; |
| import org.apache.fop.render.pcl.fonts.truetype.PCLTTFFontReader; |
| |
| public class PCLTTFFontReaderTestCase { |
| |
| private CustomFontMetricsMapper customFont = mock(CustomFontMetricsMapper.class); |
| private PCLByteWriterUtil byteWriter; |
| private static final String TEST_FONT_A = "./test/resources/fonts/ttf/DejaVuLGCSerif.ttf"; |
| |
| @Before |
| public void setUp() { |
| byteWriter = new PCLByteWriterUtil(); |
| } |
| |
| @Test |
| public void verifyFontAData() throws Exception { |
| CustomFont sbFont = mock(CustomFont.class); |
| when(sbFont.getInputStream()).thenReturn(new FileInputStream(new File(TEST_FONT_A))); |
| when(customFont.getRealFont()).thenReturn(sbFont); |
| SingleByteFont font = mock(SingleByteFont.class); |
| when(font.getGIDFromChar('h')).thenReturn(104); |
| when(font.getGIDFromChar('e')).thenReturn(101); |
| when(font.getGIDFromChar('l')).thenReturn(108); |
| when(font.getGIDFromChar('o')).thenReturn(111); |
| PCLTTFFontReader reader = new MockPCLTTFFontReader(customFont, byteWriter); |
| reader.setFont(font); |
| verifyFontData(reader); |
| validateOffsets(reader); |
| validateFontSegments(reader); |
| } |
| |
| /** |
| * Compares the input font data against a sample of the data read and calculated by the reader. The assertions are |
| * made against data taken from the TrueType Font Analyzer tool. |
| * @param reader The reader |
| */ |
| private void verifyFontData(PCLTTFFontReader reader) { |
| assertEquals(reader.getCellWidth(), 5015); // Bounding box X2 - X1 |
| assertEquals(reader.getCellHeight(), 3254); // Bounding box Y2 - Y1 |
| assertEquals(reader.getCapHeight(), 0); // OS2Table.capHeight |
| assertEquals(reader.getFontName(), "DejaVu LGC Serif"); // Full name read by TTFFont object |
| assertEquals(reader.getFirstCode(), 32); // Always 32 for bound font |
| assertEquals(reader.getLastCode(), 255); // Always 255 for bound font |
| |
| // Values that require conversion tables (See PCLTTFFontReader.java) |
| assertEquals(reader.getStrokeWeight(), 0); // Weight Class 400 (regular) should be equivalent 0 |
| assertEquals(reader.getSerifStyle(), 128); // Serif Style 0 should equal 0 |
| assertEquals(reader.getWidthType(), 0); // Width Class 5 (regular) should be equivalent 0 |
| } |
| |
| private void validateOffsets(PCLTTFFontReader reader) throws IOException { |
| // Offsets are stored with their character ID with the array [offset, length] |
| Map<Integer, int[]> offsets = reader.getCharacterOffsets(); |
| |
| // Test data |
| int[] charC = {27644, 144}; // Char index = 99 |
| int[] charDollar = {16044, 264}; // Char index = 36 |
| int[] charOne = {17808, 176}; // Char index = 49 |
| int[] charUpperD = {21236, 148}; // Char index = 68 |
| int[] charUpperJ = {22140, 176}; // Char index = 74 |
| |
| assertArrayEquals(offsets.get(99), charC); |
| assertArrayEquals(offsets.get(36), charDollar); |
| assertArrayEquals(offsets.get(49), charOne); |
| assertArrayEquals(offsets.get(68), charUpperD); |
| assertArrayEquals(offsets.get(74), charUpperJ); |
| } |
| |
| /** |
| * Verifies the font segment data copied originally from the TrueType font. Data was verified using TrueType Font |
| * Analyzer and PCLParaphernalia tool. |
| * @param reader The reader |
| * @throws IOException |
| */ |
| private void validateFontSegments(PCLTTFFontReader reader) throws IOException { |
| HashMap<Character, Integer> mappedChars = new HashMap<Character, Integer>(); |
| mappedChars.put('H', 1); |
| mappedChars.put('e', 1); |
| mappedChars.put('l', 1); |
| mappedChars.put('o', 1); |
| |
| List<PCLFontSegment> segments = reader.getFontSegments(mappedChars); |
| assertEquals(segments.size(), 5); |
| for (PCLFontSegment segment : segments) { |
| if (segment.getIdentifier() == SegmentID.PA) { |
| // Panose |
| assertEquals(segment.getData().length, 10); |
| byte[] panose = {2, 6, 6, 3, 5, 6, 5, 2, 2, 4}; |
| assertArrayEquals(segment.getData(), panose); |
| } else if (segment.getIdentifier() == SegmentID.GT) { |
| verifyGlobalTrueTypeData(segment, mappedChars.size()); |
| } else if (segment.getIdentifier() == SegmentID.NULL) { |
| // Terminating segment |
| assertEquals(segment.getData().length, 0); |
| } |
| } |
| } |
| |
| private void verifyGlobalTrueTypeData(PCLFontSegment segment, int mappedCharsSize) |
| throws IOException { |
| byte[] ttfData = segment.getData(); |
| int currentPos = 0; |
| //Version |
| assertEquals(readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]}), 1); |
| assertEquals(readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]}), 0); |
| //Number of tables |
| int numTables = readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]}); |
| assertEquals(numTables, 8); |
| //Search range |
| assertEquals(readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]}), 128); |
| //Entry Selector |
| assertEquals(readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]}), 3); |
| //Range shift |
| assertEquals(readInt(new byte[]{ttfData[currentPos++], ttfData[currentPos++]}), 0); |
| String[] validTags = {"head", "hhea", "hmtx", "maxp", "gdir"}; |
| int matches = 0; |
| for (int i = 0; i < numTables; i++) { |
| String tag = readTag(new byte[]{ttfData[currentPos++], ttfData[currentPos++], |
| ttfData[currentPos++], ttfData[currentPos++]}); |
| if (Arrays.asList(validTags).contains(tag)) { |
| matches++; |
| } |
| if (tag.equals("hmtx")) { |
| currentPos += 4; |
| int offset = readLong(new byte[]{ttfData[currentPos++], ttfData[currentPos++], |
| ttfData[currentPos++], ttfData[currentPos++]}); |
| int length = readLong(new byte[]{ttfData[currentPos++], ttfData[currentPos++], |
| ttfData[currentPos++], ttfData[currentPos++]}); |
| verifyHmtx(ttfData, offset, length, mappedCharsSize); |
| } else { |
| currentPos += 12; |
| } |
| } |
| assertEquals(matches, 5); |
| } |
| |
| private void verifyHmtx(byte[] ttfData, int offset, int length, int mappedCharsSize) |
| throws IOException { |
| ByteArrayInputStream bais = new ByteArrayInputStream(ttfData); |
| byte[] subsetHmtx = new byte[length]; |
| bais.skip(offset); |
| bais.read(subsetHmtx); |
| assertEquals(subsetHmtx.length, (mappedCharsSize + 32) * 4); |
| } |
| |
| private int readInt(byte[] bytes) { |
| return ((0xFF & bytes[0]) << 8) | (0xFF & bytes[1]); |
| } |
| |
| private int readLong(byte[] bytes) { |
| return ((0xFF & bytes[0]) << 24) | ((0xFF & bytes[1]) << 16) | ((0xFF & bytes[2]) << 8) |
| | (0xFF & bytes[3]); |
| } |
| |
| private String readTag(byte[] tag) { |
| return new String(tag); |
| } |
| } |