更改某些非文本字符的字体时,如何使 TRichEdit 在 Windows 7 上表现得像写字板?

Posted

技术标签:

【中文标题】更改某些非文本字符的字体时,如何使 TRichEdit 在 Windows 7 上表现得像写字板?【英文标题】:How to make TRichEdit behave like WordPad on Windows 7 when changing font for certain non-text characters? 【发布时间】:2012-07-03 23:34:39 【问题描述】:

Sertac Akyuz的帮助下,直接原因似乎与\bullet的字符集有关:在我本地化的Windows中,通过输入Alt(0149)输入的\bullet总是得到\fcharset134,并尝试通过EM_SETCHARFORMAT 更改字体总是失败(嗯,颜色、大小、样式确实可以更改,但不能更改字体系列名称)。

因此,最简单的解决方法是首先重置字符集,然后更改字体。

注意:应该使用 RichEdit (version >= 4.1)

注意:可以在 MSDN 的 About Rich Edit Controls、Murray Sargent 的 MSDN 博客 RichEdit versions 和 RichEdit Versions Update to 7.0 找到 RichEdit 版本。后面几页提到了高于 4.1 的 RichEdit 版本。作为测试,我将 Office 2010 附带的 RICHED20.DLL 连同应用程序一起复制到了 Windows 2000 中,一切正常!

procedure TMainForm.ButtonFontClick(Sender: TObject);
var
  format: TCharFormat2;
begin
  if dlgFontCdxTxt.Execute then
  begin
    FillChar(format, sizeof(format), 0);
    format.cbSize:= Sizeof(format);
    format.dwMask:= CFM_CHARSET;
    format.bCharSet := 1; // or 0;
    redtTextBlock.Perform(EM_SETCHARFORMAT, SCF_SELECTION, Integer(@format));

    FillChar(format, sizeof(format), 0);
    format.cbSize:= Sizeof(format);
    format.dwMask:= CFM_FACE;
    StrPLCopy(format.szFaceName, dlgFontCdxTxt.Font.Name, High(format.szFaceName));
    redtTextBlock.Perform(EM_SETCHARFORMAT, SCF_SELECTION, Integer(@format));
  end;
  redtTextBlock.SetFocus;
end;

================================================ ===

根据Wikipedia,WordPad使用了微软的RichEdit控件,分别在Windows 95、98和Windows 2000中的1.0、2.0和3.0版本。在 Windows XP SP1 及更高版本中,写字板使用 RichEdit 4.1,包括 Windows 7。

假设在写字板中编辑的 rtf 文档包含非文本字符 Alt(0149)the bullet dot •。 (或 U+2022)

在 Windows 2000 SP4 或 XP SP2 中,该圆点的字体只能采用 WordPad 的默认字体。也就是说,不能在写字板中交互地更改该项目符号点的字体。

但是,在 Windows 7 SP1 中,可以通过first changing to "Arial Unicode MS" 更改其字体,然后无限次更改为任何所需的字体。

此外,在 Windows 7 中使用 WordPad 创建的包含不同圆点字体的 WordPad 文档可以在 Windows 2000 或 XP 中的 WordPad 中正确打开和查看。

TRichEdit(Delphi XE,Windows 7)也可以通过TRichEdit.Lines.LoadFromFile在Windows 7中正确打开和查看使用写字板创建的写字板文档。

TRichEdit(Delphi XE,Windows 7)以交互方式允许将the bullet dot 的字体更改为“Arial Unicode MS”。但是,在 TRichEdit 中不能更进一步以交互方式更改为其他字体。

因此我想知道 (1) Windows 7 中写字板行为不同的原因,以及 (2) 是否有可能使 TRichEdit 行为相似?

PS:可能需要多次键入 Alt(0149) 才能在写字板中获取点。正如here 所建议的那样,输入 2022 和 Alt+x 始终有效。

PS:如Why TFontDialog gives less fonts than Screen.Fonts?的回答中提到的,需要“激活”写字板中的字体

PS:在 Word 中可以随时将点更改为不同的字体。

sample.rtf(粘贴到纯文本文件中,然后将扩展名改为rtf即可使用)

\rtf1\ansi\ansicpg936\deff0\deflang1033\deflangfe2052\fonttbl\f0\fswiss\fprq2\fcharset134 Arial Unicode MS;\f1\fnil\fcharset0 Arial Unicode MS;\f2\froman\fprq2\fcharset0 Times New Roman;\f3\fscript\fprq2\fcharset0 Comic Sans MS;\f4\fnil\fcharset0 Comic Sans MS;\f5\fmodern\fprq1\fcharset0 Consolas;\f6\fnil\fcharset0 Consolas;\f7\fmodern\fprq1\fcharset0 Lucida Console;\f8\fnil\fcharset0 Lucida Console;\f9\froman\fprq2\fcharset2 Symbol;\f10\froman\fprq2\fcharset0 Symbol;\f11\fnil\fcharset134 \'cb\'ce\'cc\'e5;
\*\generator Msftedit 5.41.21.2510;\viewkind4\uc1\pard\nowidctlpar\sa200\sl276\slmult1\lang2052\f0\fs22 Arial sample text \lang1033\f1\bullet\f2\par
\b\f3 Comic sample text \f4\bullet\f2\par
\b0\f5 Consolas sample text \f6\bullet\f2\par
\f7 Lucida sample text \f8\bullet\f2\par
\pard\nowidctlpar\qj\lang2052\f9 symbl sample text \lang1033\f10\u149?\kerning2\fs21\par
\pard\sa200\sl276\slmult1\lang2052\kerning0\f11\fs22\par

uMainForm.dfm查看TRichEdit的行格式

object MainForm: TMainForm
  Left = 0
  Top = 0
  Caption = 'MainForm'
  ClientHeight = 362
  ClientWidth = 637
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object pnlBtn: TPanel
    Left = 0
    Top = 0
    Width = 637
    Height = 57
    Align = alTop
    Caption = 'pnlBtn'
    TabOrder = 0
    object Button1: TButton
      Left = 240
      Top = 14
      Width = 137
      Height = 31
      Caption = 'Analyze Rich Edit'
      TabOrder = 0
      OnClick = Button1Click
    end
  end
  object pnlClient: TPanel
    Left = 0
    Top = 57
    Width = 637
    Height = 305
    Align = alClient
    Caption = 'pnlClient'
    TabOrder = 1
    object redtTextBlock: TRichEdit
      Left = 1
      Top = 1
      Width = 225
      Height = 303
      Align = alLeft
      Font.Charset = GB2312_CHARSET
      Font.Color = clWindowText
      Font.Height = -11
      Font.Name = 'Tahoma'
      Font.Style = []
      Lines.Strings = (
        'redt1')
      ParentFont = False
      TabOrder = 0
    end
    object mmo1: TMemo
      Left = 226
      Top = 1
      Width = 410
      Height = 303
      Align = alClient
      Lines.Strings = (
        'mmo1')
      TabOrder = 1
    end
  end
  object Button2: TButton
    Left = 36
    Top = 14
    Width = 171
    Height = 31
    Caption = 'Font...'
    TabOrder = 2
    OnClick = Button2Click
  end
  object dlgFontCdxTxt: TFontDialog
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'Tahoma'
    Font.Style = []
    Left = 480
    Top = 16
  end
end

uMainForm.pas查看TRichEdit的行格式

unit uMainForm;

interface

uses
  Contnrs,
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type

  TCdxmlStyle = class
  public
    FontName: string;
    Str: string;
  end;

  TCdxmlText = class
  public
    Styles: TObjectList;
    constructor Create;
  end;

  TMainForm = class(TForm)
    redtTextBlock: TRichEdit;
    mmo1: TMemo;
    pnlBtn: TPanel;
    pnlClient: TPanel;
    Button1: TButton;
    Button2: TButton;
    dlgFontCdxTxt: TFontDialog;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
     Private declarations 
    procedure TestLoadFromFile;
    procedure AnalyzeRichEdit;
  public
     Public declarations 
  end;

var
  MainForm: TMainForm;

implementation

$R *.dfm

uses
  RichEdit, StrUtils;

 TCdxmlText 

constructor TCdxmlText.Create;
begin
  Styles := TObjectList.Create;
end;

var
  l_HiddenRichEdit: TRichEdit;

 TMainForm 

procedure TMainForm.FormCreate(Sender: TObject);
begin
  TestLoadFromFile;
  AnalyzeRichEdit;
end;

procedure TMainForm.Button2Click(Sender: TObject);
var
  format: TCharFormat2;
begin
  if dlgFontCdxTxt.Execute then
  begin
    FillChar(format, sizeof(format), 0);
    format.cbSize:= Sizeof(format);
    format.dwMask:= CFM_FACE;

    StrPLCopy(format.szFaceName, dlgFontCdxTxt.Font.Name, High(format.szFaceName));

    redtTextBlock.Perform(EM_SETCHARFORMAT, SCF_SELECTION, Integer(@format));
  end;
  redtTextBlock.SetFocus;
end;

procedure TMainForm.Button1Click(Sender: TObject);
begin
  AnalyzeRichEdit;
end;

procedure TMainForm.TestLoadFromFile;
begin
  redtTextBlock.Clear;

  redtTextBlock.Lines.LoadFromFile('sample.rtf');
end;

procedure TMainForm.AnalyzeRichEdit;
var
  l_MemStream: TMemoryStream;
  l_Format: TCharFormat2;

  I, J: Integer;
  l_CdxmlStyle, l_CdxmlStyleWorker: TCdxmlStyle;
  l_StyleFont: string;

  l_CdxmlText: TCdxmlText;
begin
  l_CdxmlStyle := nil;
  l_CdxmlStyleWorker := nil;

  mmo1.Clear;

  l_MemStream := TMemoryStream.Create;
  redtTextBlock.Lines.SaveToStream(l_MemStream);
  l_MemStream.Seek(0, soFromBeginning);
  l_HiddenRichEdit.Lines.LoadFromStream(l_MemStream);

  l_CdxmlText := TCdxmlText.Create;
  for I := 0 to Length(TrimRight(l_HiddenRichEdit.Text)) - 1 do
  begin
    l_CdxmlStyleWorker := TCdxmlStyle.Create;

    FillChar(l_Format, sizeof(l_Format), 0);
    l_Format.cbSize:= Sizeof(l_Format);
    l_Format.dwMask:= CFM_FACE;

    l_HiddenRichEdit.SelStart := I;
    l_HiddenRichEdit.SelLength := 1;
    l_HiddenRichEdit.Perform(EM_GETCHARFORMAT, SCF_SELECTION, Integer(@l_Format));

    l_CdxmlStyleWorker.FontName := l_Format.szFaceName;

    l_CdxmlStyleWorker.Str := AnsiReplaceStr(l_HiddenRichEdit.SelText, #13, #13#10);

    if l_CdxmlStyle = nil then
    begin
      l_CdxmlText.Styles.Add(l_CdxmlStyleWorker);
      l_CdxmlStyle := l_CdxmlStyleWorker;
    end
    else if (l_CdxmlStyleWorker.FontName  <> l_CdxmlStyle.FontName ) then
    begin
      l_CdxmlText.Styles.Add(l_CdxmlStyleWorker);
      l_CdxmlStyle := l_CdxmlStyleWorker;
    end
    else
    begin
      l_CdxmlStyle.Str := l_CdxmlStyle.Str + l_CdxmlStyleWorker.Str;
    end;
  end;

  for I := 0 to l_CdxmlText.Styles.Count - 1 do
  begin
    l_CdxmlStyle := TCdxmlStyle(l_CdxmlText.Styles[I]);
    mmo1.Lines.Add(l_CdxmlStyle.Str + ':' + l_CdxmlStyle.FontName);
  end;
end;

initialization

  l_HiddenRichEdit := TRichEdit.CreateParented(HWND_MESSAGE);

end.

【问题讨论】:

只是一个旁注,Françoisthis blog post 中写了如何使用不同版本的富编辑控件。您也可以尝试使用它。 @TLama:非常感谢您的评论!我刚刚按照您的建议尝试了 RichEdit 4.1(通过粘贴在彩色表格中进行验证),但我仍然无法在 TRichEdit 中将点的字体更改为例如 Comic 或 Lucida。 【参考方案1】:

要检查的一件事是查看 WordPad 和 TRichEdit 使用的 Richedit 控件是否相同。我建议您检查 (Spy++) Spyxx.exe 以确保控件具有相同的类和相同的样式。如果它们相同,那么我还会检查以确保控件接收到相同的消息,再次使用 Spy++。我猜控件不一样,或者它们的配置不一样。

如果它们不是同一个控件,那么您应该能够使用与写字板相同的控件(假设它是标准 Windows 自定义控件的一部分)。并使用 Spy++ 以与写字板相同的方式设置样式。此外,您可能还需要向其发送相同的消息。

【讨论】:

以上是关于更改某些非文本字符的字体时,如何使 TRichEdit 在 Windows 7 上表现得像写字板?的主要内容,如果未能解决你的问题,请参考以下文章

无论字体系列或字体大小如何,有没有办法使文本垂直居中?

在代码中更改 UWP 文本时,某些字体无法正确显示,尤其是图标字体

当在反应本机中输入超过 6 个字符时,如何更改 textInput 文本中的字体大小?

如何将新字体应用于以特定字体编写的字符?

如何使用不同颜色和字体的文本改进图像的 OCR?

iPhone 的啥代码,以便用户可以通过单击某些按钮来更改文本视图中的字体大小?