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 package org.apache.fontbox.cff; 18 19 import java.io.IOException; 20 import java.util.ArrayList; 21 import java.util.List; 22 23 /** 24 * This class represents a converter for a mapping into a Type2-sequence. 25 * @author Villu Ruusmann 26 * @version $Revision: 1.0 $ 27 */ 28 public class Type2CharStringParser 29 { 30 31 private DataInput input = null; 32 private int hstemCount = 0; 33 private int vstemCount = 0; 34 private List<Object> sequence = null; 35 36 /** 37 * The given byte array will be parsed and converted to a Type2 sequence. 38 * @param bytes the given mapping as byte array 39 * @return the Type2 sequence 40 * @throws IOException if an error occurs during reading 41 */ 42 public List<Object> parse(byte[] bytes) throws IOException 43 { 44 input = new DataInput(bytes); 45 46 hstemCount = 0; 47 vstemCount = 0; 48 49 sequence = new ArrayList<Object>(); 50 51 while (input.hasRemaining()) 52 { 53 int b0 = input.readUnsignedByte(); 54 55 if (b0 >= 0 && b0 <= 27) 56 { 57 sequence.add(readCommand(b0)); 58 } 59 else if (b0 == 28) 60 { 61 sequence.add(readNumber(b0)); 62 } 63 else if (b0 >= 29 && b0 <= 31) 64 { 65 sequence.add(readCommand(b0)); 66 } 67 else if (b0 >= 32 && b0 <= 255) 68 { 69 sequence.add(readNumber(b0)); 70 } 71 else 72 { 73 throw new IllegalArgumentException(); 74 } 75 } 76 77 return sequence; 78 } 79 80 private CharStringCommand readCommand(int b0) throws IOException 81 { 82 83 if (b0 == 1 || b0 == 18) 84 { 85 hstemCount += peekNumbers().size() / 2; 86 } 87 else if (b0 == 3 || b0 == 19 || b0 == 20 || b0 == 23) 88 { 89 vstemCount += peekNumbers().size() / 2; 90 } // End if 91 92 if (b0 == 12) 93 { 94 int b1 = input.readUnsignedByte(); 95 96 return new CharStringCommand(b0, b1); 97 } 98 else if (b0 == 19 || b0 == 20) 99 { 100 int[] value = new int[1 + getMaskLength()]; 101 value[0] = b0; 102 103 for (int i = 1; i < value.length; i++) 104 { 105 value[i] = input.readUnsignedByte(); 106 } 107 108 return new CharStringCommand(value); 109 } 110 111 return new CharStringCommand(b0); 112 } 113 114 private Integer readNumber(int b0) throws IOException 115 { 116 117 if (b0 == 28) 118 { 119 int b1 = input.readUnsignedByte(); 120 int b2 = input.readUnsignedByte(); 121 122 return Integer.valueOf((short) (b1 << 8 | b2)); 123 } 124 else if (b0 >= 32 && b0 <= 246) 125 { 126 return Integer.valueOf(b0 - 139); 127 } 128 else if (b0 >= 247 && b0 <= 250) 129 { 130 int b1 = input.readUnsignedByte(); 131 132 return Integer.valueOf((b0 - 247) * 256 + b1 + 108); 133 } 134 else if (b0 >= 251 && b0 <= 254) 135 { 136 int b1 = input.readUnsignedByte(); 137 138 return Integer.valueOf(-(b0 - 251) * 256 - b1 - 108); 139 } 140 else if (b0 == 255) 141 { 142 int b1 = input.readUnsignedByte(); 143 int b2 = input.readUnsignedByte(); 144 int b3 = input.readUnsignedByte(); 145 int b4 = input.readUnsignedByte(); 146 147 // The lower bytes are representing the digits after 148 // the decimal point and aren't needed in this context 149 return Integer.valueOf((short)(b1 << 8 | b2)); 150 } 151 else 152 { 153 throw new IllegalArgumentException(); 154 } 155 } 156 157 private int getMaskLength() 158 { 159 int length = 1; 160 161 int hintCount = hstemCount + vstemCount; 162 while ((hintCount -= 8) > 0) 163 { 164 length++; 165 } 166 167 return length; 168 } 169 170 private List<Number> peekNumbers() 171 { 172 List<Number> numbers = new ArrayList<Number>(); 173 174 for (int i = sequence.size() - 1; i > -1; i--) 175 { 176 Object object = sequence.get(i); 177 178 if (object instanceof Number) 179 { 180 Number number = (Number) object; 181 182 numbers.add(0, number); 183 184 continue; 185 } 186 187 return numbers; 188 } 189 190 return numbers; 191 } 192 }