1 package com.google.typography.font.sfntly.table.core; 2 3 import com.google.typography.font.sfntly.data.FontData; 4 import com.google.typography.font.sfntly.data.ReadableFontData; 5 import com.google.typography.font.sfntly.data.WritableFontData; 6 import com.google.typography.font.sfntly.table.core.CMapTable.CMapId; 7 import com.google.typography.font.sfntly.table.core.CMapTable.Offset; 8 9 import java.util.Iterator; 10 11 /** 12 * A cmap format 2 sub table. 13 * 14 * The format 2 cmap is used for multi-byte encodings such as SJIS, 15 * EUC-JP/KR/CN, Big5, etc. 16 */ 17 public final class CMapFormat2 extends CMap { 18 CMapFormat2(ReadableFontData data, CMapId cmapId)19 protected CMapFormat2(ReadableFontData data, CMapId cmapId) { 20 super(data, CMapFormat.Format2.value, cmapId); 21 } 22 subHeaderOffset(int subHeaderIndex)23 private int subHeaderOffset(int subHeaderIndex) { 24 int subHeaderOffset = this.data.readUShort( 25 Offset.format2SubHeaderKeys.offset + subHeaderIndex * FontData.DataSize.USHORT.size()); 26 return subHeaderOffset; 27 } 28 firstCode(int subHeaderIndex)29 private int firstCode(int subHeaderIndex) { 30 int subHeaderOffset = subHeaderOffset(subHeaderIndex); 31 int firstCode = 32 this.data.readUShort(subHeaderOffset + Offset.format2SubHeaderKeys.offset 33 + Offset.format2SubHeader_firstCode.offset); 34 return firstCode; 35 } 36 entryCount(int subHeaderIndex)37 private int entryCount(int subHeaderIndex) { 38 int subHeaderOffset = subHeaderOffset(subHeaderIndex); 39 int entryCount = 40 this.data.readUShort(subHeaderOffset + Offset.format2SubHeaderKeys.offset 41 + Offset.format2SubHeader_entryCount.offset); 42 return entryCount; 43 } 44 idRangeOffset(int subHeaderIndex)45 private int idRangeOffset(int subHeaderIndex) { 46 int subHeaderOffset = subHeaderOffset(subHeaderIndex); 47 int idRangeOffset = this.data.readUShort(subHeaderOffset + Offset.format2SubHeaderKeys.offset 48 + Offset.format2SubHeader_idRangeOffset.offset); 49 return idRangeOffset; 50 } 51 idDelta(int subHeaderIndex)52 private int idDelta(int subHeaderIndex) { 53 int subHeaderOffset = subHeaderOffset(subHeaderIndex); 54 int idDelta = 55 this.data.readShort(subHeaderOffset + Offset.format2SubHeaderKeys.offset 56 + Offset.format2SubHeader_idDelta.offset); 57 return idDelta; 58 } 59 60 /** 61 * Returns how many bytes would be consumed by a lookup of this character 62 * with this cmap. This comes about because the cmap format 2 table is 63 * designed around multi-byte encodings such as SJIS, EUC-JP, Big5, etc. 64 * 65 * @param character 66 * @return the number of bytes consumed from this "character" - either 1 or 67 * 2 68 */ bytesConsumed(int character)69 public int bytesConsumed(int character) { 70 int highByte = (character >> 8) & 0xff; 71 int offset = subHeaderOffset(highByte); 72 73 if (offset == 0) { 74 return 1; 75 } 76 return 2; 77 } 78 79 @Override glyphId(int character)80 public int glyphId(int character) { 81 if (character > 0xffff) { 82 return CMapTable.NOTDEF; 83 } 84 85 int highByte = (character >> 8) & 0xff; 86 int lowByte = character & 0xff; 87 int offset = subHeaderOffset(highByte); 88 89 // only consume one byte 90 if (offset == 0) { 91 lowByte = highByte; 92 highByte = 0; 93 } 94 95 int firstCode = firstCode(highByte); 96 int entryCount = entryCount(highByte); 97 98 if (lowByte < firstCode || lowByte >= firstCode + entryCount) { 99 return CMapTable.NOTDEF; 100 } 101 102 int idRangeOffset = idRangeOffset(highByte); 103 104 // position of idRangeOffset + value of idRangeOffset + index for low byte 105 // = firstcode 106 int pLocation = (offset + Offset.format2SubHeader_idRangeOffset.offset) + idRangeOffset 107 + (lowByte - firstCode) * FontData.DataSize.USHORT.size(); 108 int p = this.data.readUShort(pLocation); 109 if (p == 0) { 110 return CMapTable.NOTDEF; 111 } 112 113 if (offset == 0) { 114 return p; 115 } 116 int idDelta = idDelta(highByte); 117 return (p + idDelta) % 65536; 118 } 119 120 @Override language()121 public int language() { 122 return this.data.readUShort(Offset.format2Language.offset); 123 } 124 125 @Override iterator()126 public Iterator<Integer> iterator() { 127 return new CharacterIterator(0, 0xffff); 128 } 129 130 public static class Builder extends CMap.Builder<CMapFormat2> { Builder(WritableFontData data, int offset, CMapId cmapId)131 protected Builder(WritableFontData data, int offset, CMapId cmapId) { 132 super(data == null ? null : data.slice( 133 offset, data.readUShort(offset + Offset.format2Length.offset)), CMapFormat.Format2, 134 cmapId); 135 } 136 Builder(ReadableFontData data, int offset, CMapId cmapId)137 protected Builder(ReadableFontData data, int offset, CMapId cmapId) { 138 super(data == null ? null : data.slice( 139 offset, data.readUShort(offset + Offset.format2Length.offset)), CMapFormat.Format2, 140 cmapId); 141 } 142 143 @Override subBuildTable(ReadableFontData data)144 protected CMapFormat2 subBuildTable(ReadableFontData data) { 145 return new CMapFormat2(data, this.cmapId()); 146 } 147 } 148 }