MFC から SVG のテキスト

## テキスト
MFC と SVG のフォントサイズやベースラインなどの取り扱いの違いにより配置場所に差分が発生します。
SVG での出力頂点を左揃え、縦はベースラインに沿わせて配置するものとし、座標の調整は MFC 側で計算しておくものとしてコードを用意しました。

文字化けすることを考慮する場合は MFC で文字を PathData に変換してパスとして配置するのが良いでしょう。その場合は MFC と SVG の差分は考慮しなくて済むようになります。

本稿ではテキストを出力させるためテキストタグを使用します。

```diff_cpp:SvgImage.h

+#define	STRING_ATTR_NORMAL		0		// 通常
+#define	STRING_ATTR_ITALIC		0x01	// イタリック
+#define	STRING_ATTR_BOLD		0x02	// ボールド
+#define	STRING_ATTR_UNDERLINE	0x04	// アンダーライン

class SvgImage
{
public:
+   void AddText(mdPoint pn, LPCTSTR strText, double dFontSize,
+                LPCTSTR strFontName, COLORREF crFill, BYTE byStrAttr,
+                int nDegree, int nDepth = 0);
}
```
```cpp:SvgImage.cpp
inline CString ConvertEscapeValue(CString &strValue, BOOL bEscape)
{
	CString strResult = strValue;

	if (bEscape == TRUE)
	{
		strResult.Replace(L"&", L"&amp;");	// &
		strResult.Replace(L" ", L"&#xA0;");		// 半角スペース
		strResult.Replace(L"\r\n", L"&#x0A;");	// 改行
		strResult.Replace(L"\r", L"&#x0A;");	// 改行
		strResult.Replace(L"\n", L"&#x0A;");	// 改行
		strResult.Replace(L"\t", L"&#x08;");	// タブ
		strResult.Replace(L"<", L"&lt;");	// <
		strResult.Replace(L">", L"&gt;");	// >
		strResult.Replace(L"""", L"&quot;");	// "
		strResult.Replace(L"'", L"&apos;");	// '
	}
	else
	{
		strResult.Replace(L"&#xA0;", L" ");		// 半角スペース
		strResult.Replace(L"&#x0A;", L"\r");	// 改行
		strResult.Replace(L"&#x08;", L"\t");	// タブ
		strResult.Replace(L"&lt;", L"<");	// <
		strResult.Replace(L"&gt;", L">");	// >
		strResult.Replace(L"&amp;", L"&");	// &
		strResult.Replace(L"&quot;", L"""");	// "
		strResult.Replace(L"&apos;", L"'");	// '
	}

	return strResult;
}

void SvgImage::AddText(CPoint pn, LPCTSTR strText, double dFontSize,
                       LPCTSTR strFontName, COLORREF crFill, BYTE byStrAttr,
                       int nDegree, int nDepth)
{
	double dSize = dFontSize;
	CString strProp = strText;
    strProp = ConvertEscapeValue(strProp, TRUE);

	BOOL bVertical = FALSE;
	int nDiffDegree = 0;

	CString strFont = strFontName;
	if (strFont.GetAt(0) == '@')
	{
		bVertical = TRUE;
		nDiffDegree = 90;
	}

    SvgTag *svgText = new SvgTag();
    svgText->m_strName = L"text";
    svgText->m_strValue = ConvertEscapeValue(aryText.GetAt(i).Trim(), FALSE);
    svgText->m_nDepth = nDepth + 1;
    svgText->m_bOne = FALSE;

    CString csStr;
    csStr.Format(_T("translate(%lf,%lf)rotate(%d)"),
        pn.x - m_rectAll.left, m_rectAll.bottom - pn.y,
        -nDegree - nDiffDegree);
    svgText->m_arrAttr.Add(L"transform");
    svgText->m_arrAttrValue.Add(strProp);

    CString csSize;
    csSize.Format(L"%d", (int)dSize);
    svgText->m_arrAttr.Add(L"font-size");
    svgText->m_arrAttrValue.Add(csSize);
    svgText->m_dFontSize = dFontSize;

    svgText->m_arrAttr.Add(L"ominant-baseline");
    svgText->m_arrAttrValue.Add(L"central");

    svgText->m_arrAttr.Add(L"text-anchor");
    svgText->m_arrAttrValue.Add(L"left");
    svgText->m_nAnchor = 1;

    svgText->m_arrAttr.Add(L"font-family");
    svgText->m_arrAttrValue.Add(strFontName);
    svgText->m_strFontName = strFontName;

    if (bVertical)
    {
        svgText->m_arrAttr.Add(L"writing-mode");
        svgText->m_arrAttrValue.Add(L"tb");
    }

    m_arrayTag.Add(svgText);

    AddAttrColor(L"fill", crFill);

    if ((byStrAttr & STRING_ATTR_ITALIC) > 0)
    {
        AddAttribute(L"font-style", L"italic");
    }
    if ((byStrAttr & STRING_ATTR_BOLD) > 0)
    {
        AddAttribute(L"font-weight", L"bold");
    }
    if ((byStrAttr & STRING_ATTR_UNDERLINE) > 0)
    {
        AddAttribute(L"text-decoration", L"underline");
    }
}
```
エスケープ文字を変換する関数を追加しています。
関数の中を煩雑にしないための措置として inline 宣言しています。
AddText 関数内部にそのまま含めて構いません。
読み込みの場合にも流用できるよう bEscape フラグで書き込みようのエスケープ文字の置き換えかどうかを判定します。
```cpp
inline CString ConvertEscapeValue(CString &strValue, BOOL bEscape)
```
フォントのサイズ調整です。
できるだけ MFC の出力に合うようここで演算するなどして調整してください。
```cpp
	double dSize = dFontSize;
```

エスケープ文字の置き換えをします。

```cpp
	CString strProp = strText;
    strProp = ConvertEscapeValue(strProp, TRUE);
```
縦書きフォントを考慮して回転の設定を行います。
```cpp
	BOOL bVertical = FALSE;
	int nDiffDegree = 0;

	CString strFont = strFontName;
	if (strFont.GetAt(0) == '@')
	{
		bVertical = TRUE;
		nDiffDegree = 90;
	}
```
座標変換を行います。
左寄せの縦ベースライン固定ですが、拡張しやすいよう taranslate で平行移動して rotate をかけています。
このため、文字自体の座標は原点 (0,0) となり、属性値として記載する必要がなくなります。

```cpp
    CString csStr;
    csStr.Format(_T("translate(%lf,%lf)rotate(%d)"),
        pn.x - m_rectAll.left, m_rectAll.bottom - pn.y,
        -nDegree - nDiffDegree);
    svgText->m_arrAttr.Add(L"transform");
    svgText->m_arrAttrValue.Add(strProp);
```

描画位置を左揃え、縦ベースライン表示に設定し、フォントネームも設定します。

```cpp

    CString csSize;
    csSize.Format(L"%d", (int)dSize);
    svgText->m_arrAttr.Add(L"font-size");
    svgText->m_arrAttrValue.Add(csSize);
    svgText->m_dFontSize = dFontSize;

    svgText->m_arrAttr.Add(L"ominant-baseline");
    svgText->m_arrAttrValue.Add(L"central");

    svgText->m_arrAttr.Add(L"text-anchor");
    svgText->m_arrAttrValue.Add(L"left");
    svgText->m_nAnchor = 1;

    svgText->m_arrAttr.Add(L"font-family");
    svgText->m_arrAttrValue.Add(strFontName);
    svgText->m_strFontName = strFontName;

    if (bVertical)
    {
        svgText->m_arrAttr.Add(L"writing-mode");
        svgText->m_arrAttrValue.Add(L"tb");
    }

    m_arrayTag.Add(svgText);
```
塗りの設定とフォントの斜体やボールドの設定を行います。
フォントの装飾はヘッダに追記した STRING_ATTR_NORMAL、STRING_ATTR_ITALIC、STRING_ATTR_BOLD、STRING_ATTR_UNDERLINE を入力値の BYTE byStrAttr に設定しておくことで指定できます。
```cpp
    AddAttrColor(L"fill", crFill);

    if ((byStrAttr & STRING_ATTR_ITALIC) > 0)
    {
        AddAttribute(L"font-style", L"italic");
    }
    if ((byStrAttr & STRING_ATTR_BOLD) > 0)
    {
        AddAttribute(L"font-weight", L"bold");
    }
    if ((byStrAttr & STRING_ATTR_UNDERLINE) > 0)
    {
        AddAttribute(L"text-decoration", L"underline");
    }
```