///////////////////////////////////////////////////////////////////////////// [Delphi-ML:41971] Re: RichEdit のタブサイズ設定:AboutDelphiTipsから Yamamoto Satoshi Sun, 29 Aug 1999 23:04:53 +0900 おばQ(8D|です。 お世話になってます。 花井さん、すごいっす。 さっき、ML41921、41923のコードを実行してみてましたが、 もう、ばっちりな動作です。 コードを動かして始めて花井さんが意図していらっしゃた動作が 理解できたような気がします。 わたしはなんか勘違いしてたみたいっす 等幅でもいろいろ複雑なことがあるんですね。 丸め誤差だけの話かと思ってました、未熟ものだ。>おばQ うぅ、しかし、すでにVerUpしましたね。 ひゃー、大変です〜〜〜 とりあえず検証しましたんで結果報告 ML41921でありました関数 procedure RichEditFillTabWidthsB(ARichEdit: TRichEdit; WidthByChars: Integer); //2を指定すると全角1文字分の幅のタブ位置が設定される //全角文字で構成される文章に適する。 procedure RichEditFillTabWidthsC(ARichEdit: TRichEdit; WidthByBytes: Integer); //2を指定すると半角2文字分の幅のタブ位置が設定される //半角文字で構成される文章に適する ★とにかく等幅フォントでの実装しか考えていません。 全角と半角でそれぞれ仕様をばっちり満たしています。 インストールフォントを変えても完璧でした。 で、この関数が同じタブ位置を設定する時 これはたぶん 全角1文字の幅 = 半角2文字の幅 の時ですが これがインストールフォントによって変わるのでしょうか…? 8〜22のフォントサイズを調べて見ると 双方の関数が同じタブ位置に設定されるのは ●小さいフォント(96dpi)の時、 9,12,15,18,21と3の倍数のフォントサイズの場合 ●大きいフォント(120dpi)の時 11,12,13,17,18,19のフォントサイズの場合、(3コとばし) このような条件でした。 何かの参考になればいいですが。 さて、早速ML41963のコードでいろいろ調べてみます。 もうそろそろ、更なる、d(_・)グッド!なTips化が出来そうですね。 すばらしぃ。 楽しい話題をありがとうございます。 Delphi使っててヨカッタ。 -- Yamamoto Satoshi mailto:satorin@iris.dti.ne.jp この記事へのリンクアドレス (ご自由にリンクしてください) : http://www2.big.or.jp/~osamu/Delphi/delphi-browse.cgi?index=041971 [32662457] ///////////////////////////////////////////////////////////////////////////// [Delphi-ML:41921] Re: RichEditのタブサイズ設定:AboutDelphiTips から HANAI Tohru Fri, 27 Aug 1999 22:02:50 +0900 花井@自宅です。 花井@客先さんの [Delphi:41902] Re: RichEdit のタブサイズ設定: AboutDelphiTips から について。 > # 等幅でも、RichEdit が幅算出用につかっているのはシステムフォント > # だったり、それとも別枠で確保してるフォントだったりするような気が。 たぶん、気のせいです。(^^; おばQさんのサンプル中にある、私が作った関数は、 B - 計算精度が甘かった C - バグってた のでした。 で、改善&修正してみると、作るつもりがないと宣言した 【等幅フォント用タブ位置敷き詰め関数】 ができてしまいました。 # なんてうまくいかない人生なんでしょう。(;_;) Tips だけに反映させようかとも思いましたけど、ここに話が出たということ で、コードは下の方に貼り付けておきます。 ただ、全角幅:半角幅 = 2:1 になるようなフォントサイズにしておかないと 綺麗には表示されないです。 # この辺りは過去ログ参照ということで。 で、そうじゃないフォントのとき、Bは全角寄りで Cは半角寄りで位置設定 されます。 可変幅フォントでは、Bは以前と同じく漢字一文字を基準したタブ位置設定 ですが、Cはなんかよくわからなくなりました。たぶん、まだなにか勘違い があるかもしれません。何度か寝てるうちに新たな考えが湧いてくるようで したらいいのですが。 # うーん、可変幅フォント用ヘルパ関数作るのが面倒になってきました。 # 自分じゃ使わないし、RichEdit。 -- // AFont.Size を小数点付きで取得 // (返る値は、AControl に AFont を適用した場合の値) function GetFontSize(AControl: TWinControl; AFont: TFont): Currency; var TM: TTextMetric; ACanvas: TControlCanvas; begin ACanvas := TControlCanvas.Create; try ACanvas.Control := AControl; ACanvas.Font := AFont; GetTextMetrics(ACanvas.Handle, TM); Result := (TM.tmHeight - TM.tmInternalLeading) * 72 / ACanvas.Font.PixelsPerInch; finally ACanvas.Free; end; end; // TRichEdit: 等幅フォント用タブ位置敷き詰め関数 // (どちらかといえば、全角文字の多い文書向け) procedure RichEditFillTabWidthsB(ARichEdit: TRichEdit; WidthByChars: Integer); var Para: TParaFormat; TabWidthAsTwips: Integer; TmpSelStart: Integer; TmpSelLength: Integer; i: Integer; AFontSize: Currency; begin { Tabサイズの設定 } // 高速化のため、Tab プロパティではなく、RichEditControl の // メッセージ(EM_SETPARAFORMAT)を使用します。 // パラメータは TParaFormat 型で、タブ幅設定に使うのは、 // TParaFormat.rgxTabs 配列です。 // この配列には、タブ位置を twips 単位(20 twips = 1 points) // で指定します。 { タブ幅の計算 - 単位は twips (20 twips = 1 points) } // RichEdit は、全角使用可環境の場合、指定した twips の // “漢字一文字”をタブ位置算出に使用してしまいます。 // ここでは、そうした環境の場合に限り、twips を2 で割ります。 AFontSize := GetFontSize(ARichEdit, ARichEdit.Font); if SysLocale.FarEast then TabWidthAsTwips := Trunc(0.5 + (10 * WidthByChars * AFontSize)) else TabWidthAsTwips := Trunc(0.5 + (20 * WidthByChars * AFontSize)); // 計算した値を Para.rgxTabs 配列に敷き詰める Para.cbSize := SizeOf(TParaFormat); Para.dwMask := PFM_TABSTOPS; Para.cTabCount := MAX_TAB_STOPS; ASSERT(Low(Para.rgxTabs) = 0); for i := Low(Para.rgxTabs) to High(Para.rgxTabs) do Para.rgxTabs[i] := (i + 1) * TabWidthAsTwips; // タブ位置の設定 with ARichEdit do begin Lines.BeginUpdate; // 選択位置の一時保存 TmpSelStart := SelStart; TmpSelLength := SelLength; // 全パラグラフを選択し、タブ位置を設定 SelectAll; Perform(EM_SETPARAFORMAT, 0, Longint(@Para)); // 選択位置の復帰 SelStart := TmpSelStart; SelLength := TmpSelLength; Lines.EndUpdate; end; end; // TRichEdit: 等幅フォント用タブ位置敷き詰め関数 // (どちらかといえば、半角文字の多い文書向け) procedure RichEditFillTabWidthsC(ARichEdit: TRichEdit; WidthByBytes: Integer); // ARichEdit.Font で、半角文字最大幅とフォント全文字最大幅との // 比率を求める function CalcMaxCharWidthRatio: Currency; var TM: TTextMetric; begin with TControlCanvas.Create do try Control := ARichEdit; Font := ARichEdit.Font; GetTextMetrics(Handle, TM); Result := (2 * TextWidth('W')) / TM.tmMaxCharWidth; finally Free; end; end; var Para: TParaFormat; TabWidthAsTwips: Integer; TmpSelStart: Integer; TmpSelLength: Integer; i: Integer; AFontSize: Currency; begin AFontSize := GetFontSize(ARichEdit, ARichEdit.Font); if SysLocale.FarEast then TabWidthAsTwips := Trunc(0.5 + (10 * WidthByBytes * AFontSize * CalcMaxCharWidthRatio)) else TabWidthAsTwips := Trunc(0.5 + (20 * WidthByBytes * AFontSize * CalcMaxCharWidthRatio)); Para.cbSize := SizeOf(TParaFormat); Para.dwMask := PFM_TABSTOPS; Para.cTabCount := MAX_TAB_STOPS; ASSERT(Low(Para.rgxTabs) = 0); for i := Low(Para.rgxTabs) to High(Para.rgxTabs) do Para.rgxTabs[i] := (i + 1) * TabWidthAsTwips; with ARichEdit do begin Lines.BeginUpdate; TmpSelStart := SelStart; TmpSelLength := SelLength; SelectAll; Perform(EM_SETPARAFORMAT, 0, Longint(@Para)); SelStart := TmpSelStart; SelLength := TmpSelLength; Lines.EndUpdate; end; end; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-  @客先 (hanai@cc.canon-sales.co.jp)      [Win95 4.00.950a + D3.0J Pro. M-Rel]  @自宅 (honey@din.or.jp)      [Win98 4.10.1998 + D4.0J Pro. Upd-3]   花井 達 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- この記事へのリンクアドレス (ご自由にリンクしてください) : http://www2.big.or.jp/~osamu/Delphi/delphi-browse.cgi?index=041921 [32662456] ///////////////////////////////////////////////////////////////////////////// [Delphi-ML:41923] Re: RichEditのタブサイズ設定:AboutDelphiTips から HANAI Tohru Fri, 27 Aug 1999 22:39:28 +0900 花井@自宅です。 [Delphi:41921] Re: RichEdit のタブサイズ設定: AboutDelphiTips から への自己レスです。 お風呂入ろうかと思った時に気がつきました。 RichEditFillTabWidthsC の関数内関数 CalcMaxCharWidthRatio の 戻り値計算行からタブ幅計算行までを以下のに置きかえると、妙な Syslocal.FarEast 判定をしなくてもよくなりました。 # 万能な判定とは思ってなかったので1つでも減らしたく... -- - Result := TextWidth('W') / TM.tmMaxCharWidth; finally Free; end; end; var Para: TParaFormat; TabWidthAsTwips: Integer; TmpSelStart: Integer; TmpSelLength: Integer; i: Integer; AFontSize: Currency; begin AFontSize := GetFontSize(ARichEdit, ARichEdit.Font); TabWidthAsTwips := Trunc(0.5 + (20 * WidthByBytes * AFontSize * CalcMaxCharWidthRatio)); -- - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-  @客先 (hanai@cc.canon-sales.co.jp)      [Win95 4.00.950a + D3.0J Pro. M-Rel]  @自宅 (honey@din.or.jp)      [Win98 4.10.1998 + D4.0J Pro. Upd-3]   花井 達 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- この記事へのリンクアドレス (ご自由にリンクしてください) : http://www2.big.or.jp/~osamu/Delphi/delphi-browse.cgi?index=041923 [32662465] ///////////////////////////////////////////////////////////////////////////// [Delphi-ML:41963] Re: RichEditのタブサイズ設定:AboutDelphiTips から HANAI Tohru Sun, 29 Aug 1999 20:23:39 +0900 花井@自宅です。 [Delphi:41923] Re: RichEdit のタブサイズ設定: AboutDelphiTips から への自己レスです。 根本的なトコで勘違いをしていました。無知ってやばいっす。 自分で | 1 twip は、プリンタの 1 ポイントの 1/20 で、1,440 twip | で 1 インチ、567 twip で 1cm となります。 こうして引用しといてなお、1ポイントという大きさが使用してる フォントでころころ変わると思ってるんですから、もうバカっす。 <タブ位置 : TRichEdit.Paragraph.Tag[]> これをポイントで指定するというのは間違い無くて、例えば 72 と指定 すればコントロール左端から72 ポイント、1インチの場所、といった ことになるんですよね、内部論理では。 ふぅ。 <タブ位置の値とピクセル> でもって、ディスプレイでは表示倍率をいじれるから、72 ポイントと いっても論理的に1インチなのであって、例えば表示倍率 200 % に なってるときには、RichEdit が1インチで表示しようと思ってたのが ディスプレイドライバ潜り抜けていざ画面にでてみると2インチで表示 されたりするんですね。それで PixelsPerInch といった、1インチ 何ピクセルで表示するかという設定値があるんですね。 Tab[]が何ピクセルなのかは、こうですよね?(及び腰。) (Tab[]の値) / 72 * (水平PixelsPerInch) { 72: points, = 1 inch } ...ようやくに理解しつつあります。 ふぅ。 -- 興味無いとかいいつつ意地になって作った、等幅フォント用タブ位置 敷き詰め関数<最新版>。でもどっかおかしいかもしれません。 デバイスコンテキスト取得まわりはあんまり自信ないですし、まだ 考え違いあるかもしれませんし。 # でも、MS ゴシック 57pts 太字斜体という無茶なフォントでも案外 # いい精度で表示するので結構嬉しかったりして。 備考) ・当然ながら、全角:半角 = 2:1 の大きさで表示されてない状態では タブ位置もずれます。 ・RichEdit って、文字セル間の間隔を指定できるんでしょうか? ExtTextOut ぐらいきっちり指定できれば、Delphi IDE CodeEditor みたくフォントスタイル咲き乱れる美しきエディタが作れそうですが。 # できなければ作れなそうなんですが。 とにかくこの辺一切考慮してません。 ・画面右からはみ出た場所にタブ位置設定しても無視されるような。 -- // TRichEdit: 等幅フォント用タブ位置敷き詰め関数 procedure RichEditFillTabWidthsForFixedFont(ARichEdit: TRichEdit; CharCount: Integer); forward; function PtsToPixels(DC: HDC; bHorizontal: Boolean; ptsValue: Currency): Currency; forward; function PixelsToPts(DC: HDC; bHorizontal: Boolean; pxlValue: Currency): Currency; forward; function GetCharacterWidthI_(DC: HDC; wCharCode: Word): Currency; forward; function RoundOffPositive(X: Extended): Cardinal; forward; // TRichEdit: 等幅フォント用タブ位置敷き詰め関数 procedure RichEditFillTabWidthsForFixedFont(ARichEdit: TRichEdit; CharCount: Integer); function GetCharSize: Currency; var DC: HDC; hOldFont: HFONT; fAveCharWidth: Currency; // TM: TextMetric; begin DC := GetDC(ARichEdit.Handle); hOldFont := SelectObject(DC, ARichEdit.Font.Handle); fAveCharWidth := GetCharacterWidthI_(DC, Ord('M')); // GetTextMetrics(DC, TM); // fAveCharWidth := TM.tmAveCharWidth; Result := PixelsToPts(DC, True, fAveCharWidth); SelectObject(DC, hOldFont); ReleaseDC(ARichEdit.Handle, DC); end; procedure FillTabWidths(ptsCharSize: Currency); var Para: TParaFormat; TmpSelStart: Integer; TmpSelLength: Integer; i: Integer; begin { Tabサイズの設定 } // 高速化のため、Tab プロパティではなく、RichEditControl の // メッセージ(EM_SETPARAFORMAT)を使用します。 // パラメータは TParaFormat 型で、タブ幅設定に使うのは、 // TParaFormat.rgxTabs 配列です。 // この配列には、タブ位置を twip 単位(20 twip = 1 point) // で指定します。 Para.cbSize := SizeOf(TParaFormat); Para.dwMask := PFM_TABSTOPS; Para.cTabCount := High(Para.rgxTabs) - Low(Para.rgxTabs) + 1; ASSERT(Low(Para.rgxTabs) = 0); // タブ位置の値を Para.rgxTabs 配列に敷き詰める。単位は twip for i := Low(Para.rgxTabs) to High(Para.rgxTabs) do Para.rgxTabs[i] := RoundOffPositive(20 * ((i + 1) * (ptsCharSize * CharCount))); // タブ位置の設定 with ARichEdit do begin Lines.BeginUpdate; // 選択位置の一時保存 TmpSelStart := SelStart; TmpSelLength := SelLength; // 全パラグラフを選択し、タブ位置を設定 SelectAll; Perform(EM_SETPARAFORMAT, 0, Longint(@Para)); // 選択位置の復帰 SelStart := TmpSelStart; SelLength := TmpSelLength; Lines.EndUpdate; end; end; var ptsCharSize: Currency; begin ptsCharSize := GetCharSize; FillTabWidths(ptsCharSize); end; // 四捨五入(正数専用) function RoundOffPositive(X: Extended): Cardinal; begin ASSERT(X >= 0); if x >= 0 then Result := Trunc(x + 0.5) else Result := 0; end; // point -> pixel 変換 function PtsToPixels(DC: HDC; bHorizontal: Boolean; ptsValue: Currency): Currency; const Directions: array[Boolean] of Integer = (LOGPIXELSY, LOGPIXELSX); begin Result := (ptsValue / 72) * GetDeviceCaps(DC, Directions[bHorizontal]); end; // pixel -> point 変換 function PixelsToPts(DC: HDC; bHorizontal: Boolean; pxlValue: Currency): Currency; const Directions: array[Boolean] of Integer = (LOGPIXELSY, LOGPIXELSX); begin Result := 72 * (pxlValue / GetDeviceCaps(DC, Directions[bHorizontal])); end; // 文字幅を小数点付きで取得する。 // (Windows2000 以降ではこんなけったいな関数は使わずに // API を使用されたい。) function GetCharacterWidthI_(DC: HDC; wCharCode: Word): Currency; var P: PWord; i: Integer; szWidth: TSize; cMaxWidths: array[0..511] of Char; wTmpCharCode: Word; begin ASSERT( 1 = ((High(cMaxWidths) - Low(cMaxWidths)) and 1), 'cMaxWidths のサイズを偶数にしてください。'); // cMaxWidths を文字で埋め尽くす。 if wCharCode <= $FF then FillChar(cMaxWidths, SizeOf(cMaxWidths), wCharCode) else begin cMaxWidths[Low(cMaxWidths) + 0] := Char(HiByte(wCharCode)); cMaxWidths[Low(cMaxWidths) + 1] := Char(LoByte(wCharCode)); p := PWord(@cMaxWidths[Low(cMaxWidths)]); wTmpCharCode := p^; Inc(p); for i := 2 to SizeOf(cMaxWidths) div SizeOf(Word) do begin p^ := wTmpCharCode; Inc(p); end; end; // 文字列全体での表示幅を取得 i := SizeOf(cMaxWidths); while (i > 0) and (not GetTextExtentPoint(DC, @cMaxWidths[Low(cMaxWidths)], i, szWidth)) do Dec(i, 2); if (i = 0) then Result := 0 else if wCharCode <= $FF then // 1文字あたりの文字幅を取得 Result := szWidth.cx / i else Result := szWidth.cx / (i shr 1); end; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-  @客先 (hanai@cc.canon-sales.co.jp)      [Win95 4.00.950a + D3.0J Pro. M-Rel]  @自宅 (honey@din.or.jp)      [Win98 4.10.1998 + D4.0J Pro. Upd-3]   花井 達 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- この記事へのリンクアドレス (ご自由にリンクしてください) : http://www2.big.or.jp/~osamu/Delphi/delphi-browse.cgi?index=041963 [32662469] /////////////////////////////////////////////////////////////////////////////