WPF の縦書きで microsoft にお願いしていましたが聞き入れてもらえません。
https://connect.microsoft.com/VisualStudioJapan/feedback/details/420434/wpf-xps
自分でやってみました。
WPF の縦書きを表示することはできました。
こんな XAML を書きます。
msgothic で 特殊な文字を使わなければ Indices でグリフ変換するだけです。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="縦書きテスト.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="525" Height="320">
<Grid x:Name="LayoutRoot">
<Canvas RenderTransformOrigin="0.5,0.5">
<Glyphs Name="a0" Fill="#FF000000"
FontUri="C:\Windows\Fonts\msgothic.ttc"
UnicodeString="私の名前はえムナウです。"
Indices="9918;21455;4001;3667;21456;21418;21559;21537;21501;21448;21435;21395"
IsSideways="true" Canvas.Left="505" FontRenderingEmSize="20">
<Glyphs.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="90"/>
<TranslateTransform/>
</TransformGroup>
</Glyphs.RenderTransform>
</Glyphs>
<Glyphs Name="a1" Fill="#FF000000"
FontUri="C:\Windows\Fonts\msgothic.ttc"
UnicodeString="「1+1=2」、じゃーね。"
Indices="21400;21638;18439;21638;21649;21639;21401;21394;21434;21476;21582;21454;21395"
IsSideways="true" Canvas.Left="485" FontRenderingEmSize="20">
<Glyphs.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="90"/>
<TranslateTransform/>
</TransformGroup>
</Glyphs.RenderTransform>
</Glyphs>
_ </Canvas>
</Grid>
</Window>
Indices を作るプログラムは C(C++) の DLL (uniscribe.dll)です。
usp10.dll > version 1.6 を呼んでいます。
extern "C" HRESULT PASCAL EXPORT UniscribeScriptItemizeOpenType(
wchar_t* pwcInChars, int cInChars, SCRIPT_ITEM* pItems, OPENTYPE_TAG* pScriptTags, int* pcItems)
{
HRESULT hr = ScriptItemizeOpenType(pwcInChars, cInChars, cInChars, 0, 0, pItems, pScriptTags, pcItems);
return hr;
}
extern "C" HRESULT PASCAL EXPORT UniscribeScriptShapeOpenType(
HWND hWnd, wchar_t* pwcFontName, int lfHeight, SCRIPT_CACHE* psc, SCRIPT_ANALYSIS* psa,
OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys,
OPENTYPE_FEATURE_RECORD* potfRecords, int cotfRecords, int *rcRangeChars,
wchar_t* pwcChars, int cChars, WORD* pwLogClust, SCRIPT_CHARPROP* pCharProps,
WORD* pwOutGlyphs, SCRIPT_GLYPHPROP* pOutGlyphProps, int* pcGlyphs)
{
HDC hDC = GetDC(hWnd);
LOGFONT logfont;
memset(&logfont, 0, sizeof(logfont));
logfont.lfCharSet = DEFAULT_CHARSET;
logfont.lfHeight = lfHeight;
wcscpy_s(logfont.lfFaceName, sizeof(logfont.lfFaceName)/sizeof(WCHAR), pwcFontName);
HFONT hFont = CreateFontIndirect(&logfont);
HGDIOBJ hOldFont = SelectObject(hDC, hFont);
TEXTRANGE_PROPERTIES* rpRangeProperties = new ::TEXTRANGE_PROPERTIES[1];
rpRangeProperties[0].potfRecords = potfRecords;
rpRangeProperties[0].cotfRecords = cotfRecords;
HRESULT hr = ScriptShapeOpenType(hDC, psc, psa, tagScript, tagLangSys, rcRangeChars, &rpRangeProperties, 1,
pwcChars, cChars, cChars, pwLogClust, pCharProps, pwOutGlyphs, pOutGlyphProps, pcGlyphs);
delete[] rpRangeProperties;
SelectObject(hDC, hOldFont);
DeleteObject(hFont);
ReleaseDC(hWnd, hDC);
return hr;
}
C# から呼ぶには クラスを作ります。
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SCRIPT_CACHE
{
public IntPtr cache;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SCRIPT_ANALYSIS
{
public ushort flags;
public ushort scriptState;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SCRIPT_ITEM
{
public int iCharPos;
public SCRIPT_ANALYSIS a;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SCRIPT_CHARPROP
{
public ushort flags;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct OPENTYPE_TAG
{
public Byte b0;
public Byte b1;
public Byte b2;
public Byte b3;
public OPENTYPE_TAG(char b0, char b1, char b2, char b3)
{
this.b0 = (Byte)b0;
this.b1 = (Byte)b1;
this.b2 = (Byte)b2;
this.b3 = (Byte)b3;
}
public override string ToString()
{
if (b0 == 0)
{
return "<null>";
}
StringBuilder sb = new StringBuilder();
sb.Append((char)b0);
sb.Append((char)b1);
sb.Append((char)b2);
sb.Append((char)b3);
return sb.ToString();
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct OPENTYPE_FEATURE_RECORD
{
public OPENTYPE_TAG tagFeature;
public uint lParameter;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SCRIPT_GLYPHPROP
{
public ushort sva;
public ushort reserved;
}
public static class Uniscribe
{
[DllImport("uniscribe.dll", CharSet = CharSet.Unicode)]
public static extern int UniscribeScriptItemizeOpenType(
string pwcInChars,
int cInChars,
[In, Out] SCRIPT_ITEM[] pItems,
[In, Out] OPENTYPE_TAG[] pScriptTags,
ref int pcItems
);
[DllImport("uniscribe.dll", CharSet = CharSet.Unicode)]
public static extern int UniscribeScriptShapeOpenType(
IntPtr hwnd,
string pwcFontName,
int lfHeight,
ref SCRIPT_CACHE psc,
ref SCRIPT_ANALYSIS psa,
OPENTYPE_TAG tagScript,
OPENTYPE_TAG tagLangSys,
[In, Out] OPENTYPE_FEATURE_RECORD[] potfRecords,
int cotfRecords,
[In, Out] int[] rcRangeChars,
string pwcChars,
int cChars,
[In, Out] ushort[] pwLogClust,
[In, Out] SCRIPT_CHARPROP[] pCharProps,
[In, Out] ushort[] pwOutGlyphs,
[In, Out] SCRIPT_GLYPHPROP[] pOutGlyphProps,
ref int pcGlyphs
);
}
呼ぶほうはこんな風に呼びます。
//string pwcInChars = "私の名前はえムナウです。";
string pwcInChars = "「1+1=2」、じゃーね。";
int cInChars = pwcInChars.Length;
SCRIPT_ITEM[] pItems = new SCRIPT_ITEM[cInChars];
OPENTYPE_TAG[] pScriptTags = new OPENTYPE_TAG[cInChars];
for (int index = 0; index < cInChars; index++)
{
pItems[index] = new SCRIPT_ITEM();
pScriptTags[index] = new OPENTYPE_TAG('\u0000', '\u0000', '\u0000', '\u0000');
}
int pcItems = 0;
int hresult = Uniscribe.UniscribeScriptItemizeOpenType(
pwcInChars, cInChars, pItems, pScriptTags, ref pcItems);
WindowInteropHelper helper = new WindowInteropHelper(App.Current.MainWindow);
SCRIPT_CACHE psc = new SCRIPT_CACHE();
for (int itemIndex = 0; itemIndex < pcItems; itemIndex++)
{
SCRIPT_ANALYSIS psa = pItems[itemIndex].a;
int cChars = pItems[itemIndex+1].iCharPos - pItems[itemIndex].iCharPos;
string pwcChars = pwcInChars.Substring(pItems[itemIndex].iCharPos, cChars);
OPENTYPE_TAG tagScript = pScriptTags[itemIndex];
OPENTYPE_TAG tagLangSys = new OPENTYPE_TAG('\u0000', '\u0000', '\u0000', '\u0000');
OPENTYPE_FEATURE_RECORD[] potfRecords = new OPENTYPE_FEATURE_RECORD[1];
potfRecords[0] = new OPENTYPE_FEATURE_RECORD();
potfRecords[0].tagFeature = new OPENTYPE_TAG('v', 'e', 'r', 't');
potfRecords[0].lParameter = 1;
int[] rcRangeChars = new int[] { cChars };
ushort[] pwLogClust = new ushort[cChars];
SCRIPT_CHARPROP[] pCharProps = new SCRIPT_CHARPROP[cChars];
ushort[] pwOutGlyphs = new ushort[cInChars];
SCRIPT_GLYPHPROP[] pOutGlyphProps = new SCRIPT_GLYPHPROP[cChars];
for (int index = 0; index < cChars; index++)
{
pOutGlyphProps[index] = new SCRIPT_GLYPHPROP();
pwLogClust[index] = 0;
pCharProps[index] = new SCRIPT_CHARPROP();
pwOutGlyphs[index] = 0;
}
int pcGlyphs = 0;
System.Diagnostics.Debug.Print("pwcChars={0}, cChars={1} tagScript={2:x}", pwcChars, cChars, tagScript);
int hresult2 = Uniscribe.UniscribeScriptShapeOpenType(
helper.Handle, "msgothic", 200, ref psc, ref psa, tagScript, tagLangSys,
potfRecords, 1, rcRangeChars, pwcChars, cChars,
pwLogClust, pCharProps, pwOutGlyphs, pOutGlyphProps, ref pcGlyphs);
System.Diagnostics.Debug.Print("Gryphs={0}", pcGlyphs);
for (int gryphIndex = 0; gryphIndex < pcGlyphs; gryphIndex++)
{
System.Diagnostics.Debug.Print(
"Gryph pwLogClust={0}, pCharProps={1}, pwOutGlyphs={2}, pOutGlyphProps={3}",
pwLogClust[gryphIndex], pCharProps[gryphIndex].flags, pwOutGlyphs[gryphIndex], pOutGlyphProps[gryphIndex].sva);
}
}
結果の pwOutGlyphs を加工すれば一番最初の XAML にできます。
pwcChars=「, cChars=1 tagScript=hani
Gryphs=1
Gryph pwLogClust=0, pCharProps=0, pwOutGlyphs=21400, pOutGlyphProps=274
pwcChars=1, cChars=1 tagScript=<null>
Gryphs=1
Gryph pwLogClust=0, pCharProps=0, pwOutGlyphs=21638, pOutGlyphProps=274
pwcChars=+, cChars=1 tagScript=hani
Gryphs=1
Gryph pwLogClust=0, pCharProps=0, pwOutGlyphs=18439, pOutGlyphProps=274
pwcChars=1, cChars=1 tagScript=<null>
Gryphs=1
Gryph pwLogClust=0, pCharProps=0, pwOutGlyphs=21638, pOutGlyphProps=274
pwcChars==, cChars=1 tagScript=hani
Gryphs=1
Gryph pwLogClust=0, pCharProps=0, pwOutGlyphs=21649, pOutGlyphProps=274
pwcChars=2, cChars=1 tagScript=<null>
Gryphs=1
Gryph pwLogClust=0, pCharProps=0, pwOutGlyphs=21639, pOutGlyphProps=274
pwcChars=」、, cChars=2 tagScript=hani
Gryphs=2
Gryph pwLogClust=0, pCharProps=0, pwOutGlyphs=21401, pOutGlyphProps=274
Gryph pwLogClust=1, pCharProps=0, pwOutGlyphs=21394, pOutGlyphProps=274
pwcChars=じゃーね, cChars=4 tagScript=kana
Gryphs=4
Gryph pwLogClust=0, pCharProps=0, pwOutGlyphs=21434, pOutGlyphProps=274
Gryph pwLogClust=1, pCharProps=0, pwOutGlyphs=21476, pOutGlyphProps=274
Gryph pwLogClust=2, pCharProps=0, pwOutGlyphs=21582, pOutGlyphProps=274
Gryph pwLogClust=3, pCharProps=0, pwOutGlyphs=21454, pOutGlyphProps=274
pwcChars=。, cChars=1 tagScript=hani
Gryphs=1
Gryph pwLogClust=0, pCharProps=0, pwOutGlyphs=21395, pOutGlyphProps=274
WPF の縦書きに一筋の光明が見えてきました。