| 1 |
package jp.cssj.sakae.font.otf; |
| 2 |
|
| 3 |
import java.awt.Shape; |
| 4 |
import java.awt.geom.AffineTransform; |
| 5 |
import java.awt.geom.GeneralPath; |
| 6 |
import java.awt.geom.Rectangle2D; |
| 7 |
import java.io.IOException; |
| 8 |
|
| 9 |
import jp.cssj.sakae.font.FontSource; |
| 10 |
import jp.cssj.sakae.font.ShapedFont; |
| 11 |
import jp.cssj.sakae.gc.GC; |
| 12 |
import jp.cssj.sakae.gc.GraphicsException; |
| 13 |
import jp.cssj.sakae.gc.font.FontStyle; |
| 14 |
import jp.cssj.sakae.gc.font.util.FontUtils; |
| 15 |
import jp.cssj.sakae.gc.text.Text; |
| 16 |
import jp.cssj.sakae.gc.text.hyphenation.impl.BitSetCharacterSet; |
| 17 |
import jp.cssj.sakae.gc.text.hyphenation.impl.CharacterSet; |
| 18 |
import net.zamasoft.font.Glyph; |
| 19 |
import net.zamasoft.font.table.Feature; |
| 20 |
import net.zamasoft.font.table.FeatureList; |
| 21 |
import net.zamasoft.font.table.FeatureTags; |
| 22 |
import net.zamasoft.font.table.GsubTable; |
| 23 |
import net.zamasoft.font.table.LangSys; |
| 24 |
import net.zamasoft.font.table.Lookup; |
| 25 |
import net.zamasoft.font.table.LookupList; |
| 26 |
import net.zamasoft.font.table.Script; |
| 27 |
import net.zamasoft.font.table.ScriptList; |
| 28 |
import net.zamasoft.font.table.ScriptTags; |
| 29 |
import net.zamasoft.font.table.SingleSubst; |
| 30 |
import net.zamasoft.font.table.Table; |
| 31 |
import net.zamasoft.font.table.XmtxTable; |
| 32 |
|
| 33 |
public abstract class OpenTypeFont implements ShapedFont { |
| 34 |
private static final long serialVersionUID = 2L; |
| 35 |
|
| 36 |
protected static final int DEFAULT_VERTICAL_ORIGIN = 880; |
| 37 |
|
| 38 |
protected static final boolean ADJUST_VERTICAL = false; |
| 39 |
|
| 40 |
protected final OpenTypeFontSource source; |
| 41 |
|
| 42 |
protected final SingleSubst vSubst; |
| 43 |
|
| 44 |
protected final XmtxTable vmtx, hmtx; |
| 45 |
|
| 46 |
protected OpenTypeFont(OpenTypeFontSource source) { |
| 47 |
this.source = source; |
| 48 |
net.zamasoft.font.OpenTypeFont ttfFont = source.getOpenTypeFont(); |
| 49 |
this.hmtx = (XmtxTable) ttfFont.getTable(Table.hmtx); |
| 50 |
|
| 51 |
if (this.source.getDirection() == FontStyle.DIRECTION_TB) { |
| 52 |
// ��������������� |
| 53 |
GsubTable gsub = (GsubTable) ttfFont.getTable(Table.GSUB); |
| 54 |
ScriptList scriptList = gsub.getScriptList(); |
| 55 |
Script script = scriptList.findScript(ScriptTags.SCRIPT_TAG_KANA); |
| 56 |
if (script == null) { |
| 57 |
script = scriptList.findScript(ScriptTags.SCRIPT_TAG_HANI); |
| 58 |
} |
| 59 |
if (script == null) { |
| 60 |
script = scriptList.findScript(ScriptTags.SCRIPT_TAG_LATN); |
| 61 |
} |
| 62 |
if (script == null) { |
| 63 |
script = scriptList.findScript(ScriptTags.SCRIPT_TAG_HANG); |
| 64 |
} |
| 65 |
if (script != null) { |
| 66 |
LangSys langSys = script.getDefaultLangSys(); |
| 67 |
FeatureList featureList = gsub.getFeatureList(); |
| 68 |
Feature feature = featureList.findFeature(langSys, FeatureTags.FEATURE_TAG_VERT); |
| 69 |
if (feature != null) { |
| 70 |
LookupList lookupList = gsub.getLookupList(); |
| 71 |
Lookup lookup = lookupList.getLookup(feature, 0); |
| 72 |
this.vSubst = (SingleSubst) lookup.getSubtable(0); |
| 73 |
this.vmtx = (XmtxTable) ttfFont.getTable(Table.vmtx); |
| 74 |
return; |
| 75 |
} |
| 76 |
} |
| 77 |
} |
| 78 |
this.vSubst = null; |
| 79 |
this.vmtx = null; |
| 80 |
} |
| 81 |
|
| 82 |
protected final boolean isVertical() { |
| 83 |
return this.vmtx != null; |
| 84 |
} |
| 85 |
|
| 86 |
protected final Shape adjustShape(Shape shape, int gid) { |
| 87 |
if (!this.isVertical()) { |
| 88 |
return shape; |
| 89 |
} |
| 90 |
if (ADJUST_VERTICAL) { |
| 91 |
double advance = this.getAdvance(gid); |
| 92 |
Rectangle2D bound = shape.getBounds2D(); |
| 93 |
double bottom = bound.getY() + bound.getHeight() + DEFAULT_VERTICAL_ORIGIN; |
| 94 |
if (bottom > advance) { |
| 95 |
// ��������������������������������������������� |
| 96 |
GeneralPath path = new GeneralPath(shape); |
| 97 |
path.transform(AffineTransform.getTranslateInstance(0, advance - bottom)); |
| 98 |
shape = path; |
| 99 |
} |
| 100 |
} |
| 101 |
int cid = this.toChar(gid); |
| 102 |
if (cid == 0xFF0D || cid == 0xFF1C || cid == 0xFF1E || cid == 0x2212 || cid == 0x226A || cid == 0x226B) { |
| 103 |
GeneralPath path = new GeneralPath(shape); |
| 104 |
Rectangle2D bound = shape.getBounds2D(); |
| 105 |
path.transform(AffineTransform.getRotateInstance(Math.PI / 2.0, bound.getCenterX(), bound.getCenterY())); |
| 106 |
shape = path; |
| 107 |
} |
| 108 |
return shape; |
| 109 |
} |
| 110 |
|
| 111 |
protected final short getHAdvance(int gid) { |
| 112 |
final OpenTypeFontSource source = (OpenTypeFontSource) this.getFontSource(); |
| 113 |
final short advance = (short) (this.hmtx.getAdvanceWidth(gid) * FontSource.DEFAULT_UNITS_PER_EM |
| 114 |
/ source.getUnitsPerEm()); |
| 115 |
return advance; |
| 116 |
} |
| 117 |
|
| 118 |
protected final short getVAdvance(int gid) { |
| 119 |
if (this.vmtx == null) { |
| 120 |
return FontSource.DEFAULT_UNITS_PER_EM; |
| 121 |
} |
| 122 |
final OpenTypeFontSource source = (OpenTypeFontSource) this.getFontSource(); |
| 123 |
final short advance = (short) (this.vmtx.getAdvanceWidth(gid) * FontSource.DEFAULT_UNITS_PER_EM |
| 124 |
/ source.getUnitsPerEm()); |
| 125 |
return advance; |
| 126 |
} |
| 127 |
|
| 128 |
public FontSource getFontSource() { |
| 129 |
return this.source; |
| 130 |
} |
| 131 |
|
| 132 |
public int toGID(int c) { |
| 133 |
OpenTypeFontSource source = (OpenTypeFontSource) this.getFontSource(); |
| 134 |
int gid = source.getCmapFormat().mapCharCode(c); |
| 135 |
if (this.vSubst != null) { |
| 136 |
gid = this.vSubst.substitute(gid); |
| 137 |
} |
| 138 |
return gid; |
| 139 |
} |
| 140 |
|
| 141 |
public Shape getShapeByGID(int gid) { |
| 142 |
OpenTypeFontSource source = (OpenTypeFontSource) this.getFontSource(); |
| 143 |
Glyph glyph = source.getOpenTypeFont().getGlyph(gid); |
| 144 |
if (glyph == null) { |
| 145 |
return null; |
| 146 |
} |
| 147 |
Shape shape = glyph.getPath(); |
| 148 |
shape = this.adjustShape(shape, gid); |
| 149 |
return shape; |
| 150 |
} |
| 151 |
|
| 152 |
public short getAdvance(int gid) { |
| 153 |
if (this.isVertical()) { |
| 154 |
return this.getVAdvance(gid); |
| 155 |
} |
| 156 |
return this.getHAdvance(gid); |
| 157 |
} |
| 158 |
|
| 159 |
public short getWidth(int gid) { |
| 160 |
return this.getHAdvance(gid); |
| 161 |
} |
| 162 |
|
| 163 |
public void drawTo(GC gc, Text text) throws IOException, GraphicsException { |
| 164 |
FontUtils.drawText(gc, this, text); |
| 165 |
} |
| 166 |
|
| 167 |
protected abstract int toChar(int gid); |
| 168 |
|
| 169 |
// ��������������� |
| 170 |
private static final CharacterSet CL01 = new BitSetCharacterSet("��������������������������������������������"); |
| 171 |
// ��������������� |
| 172 |
private static final CharacterSet CL02 = new BitSetCharacterSet("�����������������������������������������������"); |
| 173 |
// ��������� |
| 174 |
private static final CharacterSet CL0607 = new BitSetCharacterSet("������������"); |
| 175 |
|
| 176 |
public short getKerning(int sgid, int gid) { |
| 177 |
int scid = this.toChar(sgid); |
| 178 |
// ������������������������������ |
| 179 |
if (CL01.contains((char) scid) && this.getWidth(sgid) > 500) { |
| 180 |
int cid = this.toChar(gid); |
| 181 |
if (CL01.contains((char) cid) && this.getWidth(gid) > 500) { |
| 182 |
return 500; |
| 183 |
} |
| 184 |
} else if (CL02.contains((char) scid) && this.getWidth(sgid) > 500) { |
| 185 |
int cid = this.toChar(gid); |
| 186 |
if ((CL01.contains((char) cid) || CL02.contains((char) cid) || CL0607.contains((char) cid)) |
| 187 |
&& this.getWidth(gid) > 500) { |
| 188 |
return 500; |
| 189 |
} |
| 190 |
} else if (CL0607.contains((char) scid) && this.getWidth(sgid) > 500) { |
| 191 |
int cid = this.toChar(gid); |
| 192 |
if ((CL01.contains((char) cid) || (CL02.contains((char) cid)) && this.getWidth(gid) > 500)) { |
| 193 |
return 500; |
| 194 |
} |
| 195 |
} |
| 196 |
return 0; |
| 197 |
} |
| 198 |
|
| 199 |
public int getLigature(int gid, int cid) { |
| 200 |
return -1; |
| 201 |
} |
| 202 |
} |