• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }