CDフォントテーブルを列挙 - リッチテキスト - 「NotesPeek」をQtでリメイク

前回、CD(Composite Data、リッチテキスト内の構成要素)のテキスト要素「CDTEXT」に含まれるフォントの修飾データを明示化しました。この中にはもちろんフォントの書体も含まれますが、5種類の書体定義しか存在しません。書体の種類は、パソコンの黎明期ならいざ知らず、現在ではあまたの書体が存在します。

Notesのリッチテキストは、これを専用のリッチテキスト形式フィールドに書体テーブルを作って、書体番号を振り、参照することで解決しています。書体番号は、0~4がデフォルトの書体定義で、5~255(推定)が書体番号になります。同一文書(Note)内の「$Fonts」リッチテキストフィールド内にあるCDFONTTABLEデータと、後続のCDFACE配列を参照すると、同じ書体番号が振られた書体データを取得することができることになります。

typedef struct {
  WSIG  Header;  /* Tag and length */
  WORD  Fonts;   /* Number of CDFACEs following */
} CDFONTTABLE;   /* Now come the CDFACE records... */

typedef struct {
  BYTE Face;    /* ID number of face */
  BYTE Family;  /* Font Family */
  char Name[MAXFACESIZE];
} CDFACE;

CDFONTTABLE::Fontsは、後続のCDFACE配列の数を表すので、CDFONTTABLEを取得した後にFonts分だけCDFACEデータを取得すればよいことになります。

CDFACE::Faceは、CDTEXT::FontID::Faceが参照する書体番号を表します。

CDFACE::Familyは、フォントファミリーを表します。ビットマスクで構成されていて、APIリファレンスによればWindowsの定義を用いているようです。以下に、関連する定義を抜粋しておきます。

/* EnumFonts Masks */
#define RASTER_FONTTYPE     0x0001
#define DEVICE_FONTTYPE     0x0002
#define TRUETYPE_FONTTYPE   0x0004

#define DEFAULT_PITCH           0
#define FIXED_PITCH             1
#define VARIABLE_PITCH          2
#if(WINVER >= 0x0400)
#define MONO_FONT               8
#endif /* WINVER >= 0x0400 */

/* Font Families */
#define FF_DONTCARE         (0<<4)  /* Don't care or don't know. */
#define FF_ROMAN            (1<<4)  /* Variable stroke width, serifed. */
                                    /* Times Roman, Century Schoolbook, etc. */
#define FF_SWISS            (2<<4)  /* Variable stroke width, sans-serifed. */
                                    /* Helvetica, Swiss, etc. */
#define FF_MODERN           (3<<4)  /* Constant stroke width, serifed or sans-serifed. */
                                    /* Pica, Elite, Courier, etc. */
#define FF_SCRIPT           (4<<4)  /* Cursive, etc. */
#define FF_DECORATIVE       (5<<4)  /* Old English, etc. */

素朴な疑問として、マルチプラットフォームであるNotesでは、MacLinuxではどのようになるのか、どこかで検証してみたいと思います。

CDFACE::Nameは、書体名を表します。書体名は最大32バイトで、これもWindows APIの影響を受けています。Shift-JISやUnicodeなら全角は2バイトで済み、16文字まで入りますが、Notesの文字列格納はLMBCS形式なので、全角や半角カナが3バイトになってしまい、すべて全角では10文字しか入りません。心配でなりません。夜も眠れません。

それはさておき、このCDFONTTABLEとCDFACE配列を、ntlx::cd::Baseテンプレートクラスから派生させたntlx::cd::FontTableクラスとして実装した例が以下になります。

// ntlx/cd/fonttable.h

#ifndef NTLX_CD_FONTTABLE_H
#define NTLX_CD_FONTTABLE_H

#include <ntlx/cd/base.h>
#include <QVariant>

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

#include <editods.h>

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

Q_DECLARE_METATYPE(CDFACE)

namespace ntlx
{
namespace cd
{

class NTLXSHARED_EXPORT FontTable
    : public Base<CDFONTTABLE, _CDFONTTABLE, SIG_CD_FONTTABLE>
{
public:
  FontTable(char** ppRecord);
  FontTable(const FontTable& other);
  FontTable& operator=(const FontTable& other);

  virtual QString toString() const;

private:
  QList<CDFACE> faceList_;
};

} // namespace cd

} // namespace ntlx

#endif // NTLX_CD_FONTTABLE_H
// cd/fonttable.cpp

#include "ntlx/cd/fonttable.h"
#include "ntlx/lmbcs.h"

namespace ntlx
{
namespace cd
{

FontTable::FontTable(char** ppRecord)
  : Base<CDFONTTABLE, _CDFONTTABLE, SIG_CD_FONTTABLE>(ppRecord)
  , faceList_()
{
  for (WORD i = 0; i < record_.Fonts; ++i)
  {
    CDFACE face;
    ODSReadMemory(ppRecord, _CDFACE, &face, 1);
    faceList_.append(face);
  }
}

FontTable::FontTable(const FontTable &other)
  : Base<CDFONTTABLE, _CDFONTTABLE, SIG_CD_FONTTABLE>(other)
  , faceList_(other.faceList_)
{
}

FontTable& FontTable::operator=(const FontTable& other)
{
  Base<CDFONTTABLE, _CDFONTTABLE, SIG_CD_FONTTABLE>::operator=(other);
  if (this != &other)
    faceList_ = other.faceList_;
  return *this;
}

QString FontTable::toString() const
{
  const QString templ("[%1:%2:%3]");
  QStringList result;
  for (auto it = faceList_.constBegin(); it != faceList_.constEnd(); ++it)
  {
    Lmbcs name((*it).Name);
    result.append(
          templ
          .arg((ushort)(*it).Face)
          .arg((ushort)(*it).Family)
          .arg(name.toQString())
          );
  }
  return result.join(",");
}

} // namespace cd

} // namespace ntlx

この実装を使って、NSFinderを拡張した例を見ていきます。サンプルの文書は、以下のようになります。

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

この文書の$Fontsフィールドの、Font Tableを見ると以下のようになります。

【前半】

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

【後半】

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

デフォルトの書体定義を除くと、251種類までしか指定ができない書体の種類。これは多いのか、少ないのか。Notesが本格的な文書作成ツールではないという観点と、そんなに多くの書体が定義された文書は見るに堪えないという観点に立てば、十分すぎるかもしれないですね。ただ、余裕がないようにも見えてしまいます。APIではちょくちょくビット長の限界を見せられます。Notesのアーキテクチャは、バージョンを追うごとに拡張もされていますが、レガシーな部分も多く引きずっています。旧世代の仕様のままでも構いませんが、柔軟な構造を活用して、大胆な構造改革を起こすことも必要かもしれません。