CDテキストのフォント情報 - リッチテキスト - 「NotesPeek」をQtでリメイク

先週の体調不良が、嘘のように回復しました。また気を引き締めてNotesPeekをリメイクしていきます。

前回は、CDデータをテンプレートクラスベースで実装を簡略化し、その手始めとしてCDテキストを「cd::Textクラス」として実装してみました。今回は、CDテキストが持つもう1つの情報について掘り下げます。

CDテキストは、構造的には「CDTEXT構造体」と可変長の「文字列データ(LMBCS)」で構成されています。可変長の文字列データの長さは、CDTEXT::Header::Lengthが持つ全体の長さからODSLength(_CDTEXT)が返す長さを引けば算出できます。

CDTEXT構造体には、Headerの他にFONTID型のメンバを持っています。FONTID自体は単なるDWORD型ですが、同じバイト長を持つFONTIDFIELDSという構造体でも表せます。これは、FIDユニオンとして定義されています。

// #include <global.h>

typedef DWORD FONTID;
// #include <fontid.h>

/*  Font ID sub-fields */

typedef struct {
#ifdef LITTLE_ENDIAN_ORDER
  BYTE Face;       /* Font face (FONT_FACE_xxx) */
  BYTE Attrib;     /* Attributes (ISBOLD,etc - see below) */
  BYTE Color;      /* Color index (FONT_COLOR_xxx) */
  BYTE PointSize;  /* Size of font in points */
#else
  BYTE PointSize;  /* Size of font in points */
  BYTE Color;      /* Color index (FONT_COLOR_xxx) */
  BYTE Attrib;     /* Attributes (ISBOLD,etc - see below) */
  BYTE Face;       /* Font face (FONT_FACE_xxx) */
#endif
} FONTIDFIELDS;

/*  Font Union */

typedef union FID {
  FONTIDFIELDS x;
  FONTID FontID;
}FID;

以下のようなリッチテキストを例に、フォント属性について見ていきます。

f:id:takahide-kondoh:20170903124210p:plain

属性のないテキストは以下のようになります。

f:id:takahide-kondoh:20170903124310p:plain

Colorは0(黒)、Faceは1(標準ゴシック体)、Sizeは10ポイントになります。

FONTIDFIELDS::PointSizeはフォントのサイズを表します。BYTEは符号なしの8ビット長なので、理論上255ポイントまで表現が可能ということになります。

f:id:takahide-kondoh:20170903124520p:plain

Colorはフォント色を表します。サイズと同様8ビット長なので、256通りの色を表現できます。しかし、画面をよく見ると、Textの次にCDCOLOR、前後をCDBEGIN、CDENDで挟まれています。256色というのは過去の仕様で、現在フォント色はフルカラーを指定できるようになっています。いずれどこかで、CDCOLORレコードを含めて色について紐解いてみたいと思います。

f:id:takahide-kondoh:20170903124710p:plain

Faceはフォントの種類を表します。0~4番は既定のフォントタイプで、5以上は別に管理されているフォントテーブルを参照する形式を取ります。0~4番のフォントタイプは、Notesクライアントで見ると以下のフォントが該当します。

f:id:takahide-kondoh:20170903125304p:plain

標準的なフォントは「Default Sans Serif」(ゴシック系)で1番となります。

f:id:takahide-kondoh:20170903125139p:plain

明朝系の標準フォントは「Default Serif」で0番です。

f:id:takahide-kondoh:20170903125756p:plain

「デフォルトのUIフォント」は3番です。

f:id:takahide-kondoh:20170903125107p:plain

「デフォルトの等幅フォント」は4番です。

f:id:takahide-kondoh:20170903125953p:plain

「デフォルトのマルチリンガルフォント」は2番です。

f:id:takahide-kondoh:20170903130902p:plain

例えばこれを、「メイリオ」にすると、最初の定義外番号として5番が割り振られました。

f:id:takahide-kondoh:20170903130216p:plain

続いて「游ゴシック」にすると、6番が割り振られていました。

f:id:takahide-kondoh:20170903130338p:plain

今回説明は割愛しますが、デフォルトのフォント以外を設定すると、「$Fonts」というリッチテキストフィールドが生成されます。この中に可変長のフォントテーブルが構成されて、5番以降のフォントが管理されます。

それでは、残りのフォント属性について一部ですが見ていきます。

太字(Bold=True)

f:id:takahide-kondoh:20170903130037p:plain

斜体(Italic=True)

f:id:takahide-kondoh:20170903131014p:plain

取り消し線(StrikeOut=True)

f:id:takahide-kondoh:20170903131052p:plain

今回、このFONTID(FONTIDFIELDS)を管理するに当たり、cd::Textクラスのインナークラスとしてcd::Text::FontIdクラスを定義しました。以下はその実装例です。

// #include <ntlx/cd/text.h>

#ifndef NTLX_CD_TEXT_H
#define NTLX_CD_TEXT_H

#include <ntlx/cd/base.h>

#if defined(NT)
#pragma pack(push, 1)
#endif

#include <editods.h>

#if defined(NT)
#pragma pack(pop)
#endif

namespace ntlx
{
namespace cd
{

class NTLXSHARED_EXPORT Text
    : public Base<CDTEXT, _CDTEXT, SIG_CD_TEXT>
{
public:
  class NTLXSHARED_EXPORT FontId
  {
  public:
    FontId(FONTID id);
    FontId(const FontId& other);
    FontId& operator=(const FontId& other);

    BYTE size() const;
    BYTE color() const;
    bool isBold() const;
    bool isItalice() const;
    bool isUnderline() const;
    bool isStrikeOut() const;
    bool isSuper() const;
    bool isSub() const;
    bool isEffect() const;
    bool isShadow() const;
    bool isEmboss() const;
    bool isExtrude() const;
    BYTE face() const;

  private:
    FONTID id_;
  };

  Text(char** ppRecord);
  Text(const Text& other);
  Text& operator=(const Text& other);

  QString value() const;
  FontId fontId() const;

  virtual QString toString() const;

private:
  QString value_;
};

} // namespace cd

} // namespace ntlx

#endif // NTLX_CD_TEXT_H

APIで提供される、FONTIDFIELDSから必要な情報と取得するための仕組みは、ほぼマクロなので実装もあまり難しいことはないと思います。

// cd/text.cpp

#include "ntlx/cd/text.h"

#include "ntlx/lmbcs.h"

namespace ntlx
{
namespace cd
{

Text::Text(char** ppRecord)
  : Base<CDTEXT, _CDTEXT, SIG_CD_TEXT>(ppRecord)
  , value_()
{
  WORD len = record_.Header.Length - ODSLength(_CDTEXT);
  Lmbcs lmbcs(*ppRecord, len);
  value_ = lmbcs.toQString();
  nullToCR(value_);
}

Text::Text(const Text &other)
  : Base<CDTEXT, _CDTEXT, SIG_CD_TEXT>(other)
  , value_(other.value_)
{
}

Text& Text::operator=(const Text& other)
{
  Base<CDTEXT, _CDTEXT, SIG_CD_TEXT>::operator=(other);
  if (this != &other)
    value_ = other.value_;
  return *this;
}

QString Text::value() const
{
  return value_;
}

Text::FontId Text::fontId() const
{
  return Text::FontId(record_.FontID);
}

QString Text::toString() const
{
  return value();
}

Text::FontId::FontId(FONTID id)
  : id_(id)
{
}

Text::FontId::FontId(const Text::FontId &other)
  : id_(other.id_)
{
}

Text::FontId& Text::FontId::operator=(const Text::FontId& other)
{
  if (this == &other) return *this;
  id_ = other.id_;
  return *this;
}

BYTE Text::FontId::size() const
{
  return FontGetSize(id_);
}

BYTE Text::FontId::color() const
{
  return FontGetColor(id_);
}

bool Text::FontId::isBold() const
{
  return FontIsBold(id_);
}

bool Text::FontId::isItalice() const
{
  return FontIsItalic(id_);
}

bool Text::FontId::isUnderline() const
{
  return FontIsUnderline(id_);
}

bool Text::FontId::isStrikeOut() const
{
  return FontIsStrikeOut(id_);
}

bool Text::FontId::isSuper() const
{
  return FontIsSuperScript(id_);
}

bool Text::FontId::isSub() const
{
  return FontIsSubScript(id_);
}

bool Text::FontId::isEffect() const
{
  return FontIsEffect(id_);
}

bool Text::FontId::isShadow() const
{
  return FontIsShadow(id_);
}

bool Text::FontId::isEmboss() const
{
  return FontIsEmboss(id_);
}

bool Text::FontId::isExtrude() const
{
  return FontIsExtrude(id_);
}

BYTE Text::FontId::face() const
{
  return FontGetFaceID(id_);
}

} // namespace cd

} // namespace ntlx

Notesの古いバージョンでは、色数の制限がひどく、たいした表現ができなかったことを思い出します。その片鱗が、このようにAPI上には残っているんですね。