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;
以下のようなリッチテキストを例に、フォント属性について見ていきます。
属性のないテキストは以下のようになります。
Colorは0(黒)、Faceは1(標準ゴシック体)、Sizeは10ポイントになります。
FONTIDFIELDS::PointSizeはフォントのサイズを表します。BYTEは符号なしの8ビット長なので、理論上255ポイントまで表現が可能ということになります。
Colorはフォント色を表します。サイズと同様8ビット長なので、256通りの色を表現できます。しかし、画面をよく見ると、Textの次にCDCOLOR、前後をCDBEGIN、CDENDで挟まれています。256色というのは過去の仕様で、現在フォント色はフルカラーを指定できるようになっています。いずれどこかで、CDCOLORレコードを含めて色について紐解いてみたいと思います。
Faceはフォントの種類を表します。0~4番は既定のフォントタイプで、5以上は別に管理されているフォントテーブルを参照する形式を取ります。0~4番のフォントタイプは、Notesクライアントで見ると以下のフォントが該当します。
標準的なフォントは「Default Sans Serif」(ゴシック系)で1番となります。
明朝系の標準フォントは「Default Serif」で0番です。
「デフォルトのUIフォント」は3番です。
「デフォルトの等幅フォント」は4番です。
「デフォルトのマルチリンガルフォント」は2番です。
例えばこれを、「メイリオ」にすると、最初の定義外番号として5番が割り振られました。
続いて「游ゴシック」にすると、6番が割り振られていました。
今回説明は割愛しますが、デフォルトのフォント以外を設定すると、「$Fonts」というリッチテキストフィールドが生成されます。この中に可変長のフォントテーブルが構成されて、5番以降のフォントが管理されます。
それでは、残りのフォント属性について一部ですが見ていきます。
太字(Bold=True)
斜体(Italic=True)
取り消し線(StrikeOut=True)
今回、この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上には残っているんですね。