AFsoft WebSite(エーエフソフト・ウェブサイト)
 

オペレーティング・システムについて

プログラミングについて
ホームページについて
キャドについて
電子カタログについて
書籍・雑誌
イベント
リンク集
CADを考える:文章
図面では、図面表題、各種の表、寸法値、注記、等で文字を書く場合が多いですが、そういった場合には通常、文字データを利用して作図を行えば良いのですが、稀に、何行にも渡る文章を作図したい場合が出てきます。
1行ずつに分解して、その行数分の文字データを作図すれば良い、という対処法もありますが、そうなると、文字入力も1行ずつ入力・作図(データ登録)を繰り返すという事になり、編集する際も1行ずつの編集になるかもしれませんし、仮にまとめて編集できたとしても、行の前後関係が本当に正しく出来るのか?という問題もあります。ワープロとまでは行かずともメモ帳程度の機能はあって欲しいという場合があります。
 
という訳で、文章データという独自データ要素があると後々便利になってくるやもしれません。Jw_cadにはありませんが、AutoCADでは、マルチテキストというデータ要素があり、複数行の文字を扱う事が出来るようになっています。
 
そのAutoCADでのマルチテキストでは、書式を指定する事が出来るようになっており、途中で文字の大きさ等を変えたりする事が出来るようになっています。AutoCADのマルチテキストの書式は、AutoCAD独自の形式です。例えば本プログラムの文章データでも、書式を扱えるようにして簡易ワープロのような事をやりたいという場合、特に、DXFデータでのやり取りを重視するのでしたら、AutoCADのマルチテキストの書式をそのまま使えるようにする、というのが1つ。勿論、独自のマルチテキストエディタを作成する必要があります。また、本プログラム独自形式の書式を考えて実装する、というのが1つ。これは自分のやりたいように出来ますが、どこまで出来るようにするのか等、考える事は結構ありそうな雰囲気はします。そして、ワードやワードパッドとのやり取りを考えたい場合のリッチテキストを扱えるようにする、というのが1つ。
 
リッチテキストを扱うには、リッチテキストエディタ=RichEditコンポーネントを利用するのが簡単です。1ポイントを何mmにするかという事を決めておいて、1文字ずつ選択、文字属性を読み取って、文字のフォント・大きさ・色・スタイルを取得し、1文字ずつ作図していけば良いでしょう。文字データ内容は、例えば普通にRichEditで「ABC」と入力した場合は、
{\rtf1\ansi\ansicpg932\deff0\deflang1033\deflangfe1041{\fonttbl{\f0\fswiss\fcharset0 Arial;}{\f1\fnil \'82l\'82r \'83S\'83V\'83b\'83N;}}
\viewkind4\uc1\pard\lang1041\f0\fs20 ABC\f1\par
}
のようになります。
仮に、SXFやDXFでデータ変換をする場合には、書式は全く無視されてプレーンテキストとなるでしょう。元々、本プログラムでは、SXF仕様での変換は考慮するけれども、DXFでの変換は一切考慮しない、としていますから、AutoCADのマルチテキストの書式に合わせるというのはナンセンスです(※DXFでのデータ変換を重視した作りにしている場合には、合わせるのが必須でしょう)。独自の書式を設定するのもそれなりに大変そうです。となれば、
 
1)複数の文字データ集合体的な書式無し文章データ
2)リッチテキストを利用する文章データ
 
の二者択一でしょうか?
 
1)の場合、文字のデータ構造は
UnitData.pas
 TDataText = record   // 幾何要素/表記要素|文字
  exf : Boolean ;    // 存在フラグ(True:有り False:無し)
  Layer : Integer ;   // レイヤ(1〜256)
  Color : Integer ;   // 色 (0:レイヤ色  1〜256)
  Font : Integer ;   // フォント (0:レイヤフォント 1〜1024)
  str : string ;    // 文字列 (最大256バイト)
  text_x : double ;   // 文字列配置基点X座標
  text_y : double ;   // 文字列配置基点Y座標
  Height : double ;   // 文字範囲高
  Width : double ;   // 文字範囲幅
  Spc : double ;    // 文字間隔
  Angle : double ;   // 文字列回転角[°]
  Slant : double ;   // スラント角[°]
  b_pnt : Integer ;   // 文字配置基点
  Direct: Integer ;   // 文字書出し方向(1:横書き, 2:縦書き)
 end;
ですので、文章データ構造は
UnitData.pas
 TDataDocument = record // 幾何要素/表記要素|独自要素|文章
  exf : Boolean ;    // 存在フラグ(True:有り False:無し)
  Layer : Integer ;   // レイヤ(1〜256)
  Color : Integer ;   // 色 (0:レイヤ色  1〜256)
  Font : Integer ;   // フォント (0:レイヤフォント 1〜1024)
  Lines : Integer ;   // 行数(文字列数)
  str : array of string;// 文字列 (最大256バイト)
  text_x : double ;   // 文章配置基点X座標
  text_y : double ;   // 文章配置基点Y座標
  Height : double ;   // 文章範囲高
  Width : double ;   // 文章範囲幅
  Spc : double ;    // 文字間隔
  Lspc : double ;    // 行間隔
  Angle : double ;   // 文章回転角[°]
  Slant : double ;   // スラント角[°]
  b_pnt : Integer ;   // 文章配置基点
  Direct: Integer ;   // 文章書出し方向(1:横書き, 2:縦書き)
  align : Integer ;   // 位置揃え(1:左(上) 2:中 3:右(下))
 end;
のような感じでしょうか。行数&文字列の配列ではなく、1文字列にしてしまって文字列内の改行コードを読み取ったら改行処理を行う、という手法もありますが、その分の手数は増えてしまいます。
デメリットは、プレーンテキスト(書式無しテキスト)である、という事ですが、メリットは、既に作成済みの文字データの処理をほぼそのまま流用できる、という事です。文字範囲高・文字範囲幅は、複数文字対象となるため確定できませんから、文字高・文字幅となるでしょう。その辺りの変更は必要となります。
 
2)の場合は、リッチテキストとなる以上、色・フォント・文字サイズはリッチテキスト内での記述となります。文字範囲幅は作図時には不明状態(指定無し状態)となります。文字間隔を指定するとその分、文字スタイルの下線等は寸断されるでしょう。リッチテキストにする以上、スラント角の指定は変ですから外す事になるでしょう。描画は、RichEditコンポーネントとの突合せをしながら、という事にならざるを得ないでしょう(※独自書式であれば独自ルールでの記述となりますのでこういう事にはなりません)。
この場合のデータ構造は
UnitData.pas
 TDataDocument = record // 幾何要素/表記要素|独自要素|文章
  exf : Boolean ;    // 存在フラグ(True:有り False:無し)
  Layer : Integer ;   // レイヤ(1〜256)
  str : string;     // リッチテキスト
  text_x : double ;   // 文章配置基点X座標
  text_y : double ;   // 文章配置基点Y座標
  Height : double ;   // 文章範囲高
  Width : double ;   // 文章範囲幅
  Spc : double ;    // 文字間隔
  Lspc : double ;    // 行間隔
  Angle : double ;   // 文章回転角[°]
  b_pnt : Integer ;   // 文章配置基点
  Direct: Integer ;   // 文章書出し方向(1:横書き, 2:縦書き)
 end;
といった感じでしょうか? 特に、色・フォントが他の要素データとは異なってしまい、図面構造フィーチャの範疇外となってしまうため、かなりのイレギュラーな存在になってしまう事は容易に想像できてしまいます。SXFやDXF等で保存する場合は、1文字ずつ分解する方法、1行ずつ分解する方法(文字属性は消えます)が有り得ますが、色・フォントの扱いには注意が必要になるでしょう。
 
さぁ、どちらの方式を採りましょうか?
 
ありきたりな1)の手法は、まぁやればいつでも出来そうな感じもしますし、面白くもないでしょうから、ここでは敢えて、2)の手法をやってみたいと思います。但し、描画については、SXF保存をするときのことを考えて、既存の文字データ描画を利用するようにしてみます。
 
画面上に[文章入力]ボタンをつけ、それをクリックしたら、RichEditコンポーネントを貼り付けた画面 Form2 (Unit2.pas)を表示されて、文章入力を行うようなテストプログラムを作ってみます。当面、テストという事で、文章入力は Windowsの「ワードパッド」で行い、それをコピー&ペーストすることでテストしてみます。
ワードパッドで文章入力し「コピー」




文章入力画面で「ペースト」([Ctrl]+[V])



Unit1.pas
// [文章入力]
procedure TForm1.Button10Click(Sender: TObject);
var
 s : TMemoryStream;
 m : TStringList ;
 st: string ;
begin
 Form2.ShowModal ;
 s := TMemoryStream.Create;
 m := TStringList.Create ;
 Form2.RichEdit1.Lines.SaveToStream(s,TEncoding.Unicode);
 s.Position := 0;
 m.LoadFromStream(s);
 s.Free ;
 st := m.Text ;
 m.Free ;
 showMessage(st);
end;
メモリストリームを経由していますが、これは、RichEditのLinesコンポーネントをそのまま取得・表示しただけでは、リッチテキストは取得出来ないからです。プレーンテキストを取得するのでは意味がありません。格納したメモリストリーム内容をそのままテキスト化するのに、TStringListを利用しています。
 
それでは、これを解釈させるために、RichEditを動的生成させて読み込ませるようにしてみます。
Unit1.pas
// [文章入力]
procedure TForm1.Button10Click(Sender: TObject);
var
 ・・・
 r : TRichEdit ;
begin
 ・・・(上記の続き)
 m := TStringList.Create ;
 s := TMemoryStream.Create;
 m.Add(st);
 m.SaveToStream(s);
 m.Free ;
 r := TRichEdit.Create(Form1) ;
 r.Name := 'RichEditA';
 r.Width := 32 ;
 r.Height := 32 ;
 r.ScrollBars := ssBoth ;
 r.Visible := False ;
 r.Parent := Form1 ;
 s.Position := 0;
 r.Lines.LoadFromStream(s,TEncoding.Unicode);
 s.Free ;
として、RichEditオブジェクトの r に対して各種読み込みをしていけばよいわけですが、ここで、はたと気付きます。実際の文章データの読み込み・表示は、UnitData.pas、UnitDataGraph.pas にて行いますが、それらでは、フォームが存在しません。フォームがありませんので、フォームの上に RichEditオブジェクトを動的生成させる、という事が出来ません。従って、この手法では無理だという事が分かってきました。
 
という訳で、簡易であっても、独自に書式を考えて実装するしか無さそうですので少し考えてみます。という事で、書式のルールを考えてます。
 
○書式は「\〜」で指定する
○文字「\」は「\\」とする
○一度決めたら以降はその状態を保持する
○大文字小文字は区別する
○無効なものを指定した場合は「\」及び次の1字を無視する
 
1)文字揃え(文字位置合わせ)
左端揃え
中央揃え
右端揃え
RichEditでは「Paragraph.Alignment」の値を調べ
  左側揃え・・・taLeftJustify
  中央揃え・・・taCenter
  右側揃え・・・taRightJustify
になるという事で分かります。行の最初に調べれば良いでしょう。
 
文字揃えは、Alignmentの「A」という事で、「\A」という事にして
 「\A1」(左:初期値) 「\A2」(中) 「\A3」(右)
とします。
 
2)箇条書き
  • あいうえお
  • 12345
  • ABC
RichEditでは「Paragraph.Numbering」の値を調べ
  ・nsNone :左側をインデントしない
  ・nsBullet:黒丸スタイルのナンバリングを指定する。
        黒丸用に十分なインデントを施す
になるという事で分かります。インデント値(黒丸を表示する位置)は、「Paragraph.FirstIndent」値で取得出来ます。黒丸と文字の間は「Paragraph.LeftIndent」値で取得出来ますが、2値を扱うのも煩雑になりますしそれほど頻繁に利用する訳でもないのでこちらは無視します。また、中位の大きさの黒丸の文字記号というのがよく分かりませんので普通の「・」で表示する事とします。
箇条書きは「\K」とします。インデント値(ピクセル値)はその後に続けて最後に「;」と書く事とします。例えば「\K18;」という具合です。ピクセル値なので「px」と書いてもいいですがバイト数節約=メモリ節約のため「;」とします。行頭にこれがあれば箇条書き、無ければ通常状態、とします。なお、数字やアルファベットの場合というのは、このプロパティでは取得出来ませんし、よく分かりませんので無視することとします。
 
3)改行
改行コードは文字コード13(#13)で現れます。
これは「\N」とする事にします。
 
4)タブ文字
タブ文字コードは、文字コード9(#9)です。通常のプレーンテキストの場合には、半角8文字ずつの位置になるよう位置合わせされる、というのがDOS時代からの通例ですが、アプリケーションによっては、4文字毎、3文字毎、等と指定出来るようになっていきました。
RichEdit では、行頭にて「Paragraph.Tab[n]」プロパティでタブ位置のピクセル値を取得する事が出来るようになっています(n:何個目のTABか;0〜Paragraph.TabCount-1)。但しこれはタブ位置を定義している場合にのみ現れます(しかも行単位での定義です!)。定義されていない場合はどうしようもありません。上記の箇条書きの際に参照した「Paragraph.FirstIndent」値なども「0」を返します。おそらくタブ位置を定義する場合もほとんど無いでしょう。スクリーン解像度の基本値は「72」ですが、ワードパッドで見る限り、見た目、その半分の「36ピクセル」程毎に移動している感じがありますので、それをタブ位置とします。
タブ文字は「\T」とする事とします。
 
以下のフォント名・大きさ・色・スタイルは文字単位で指定する事が出来るようになっています。
 
5)フォント名
RichEditにおいてフォント名は、「SelAttributes.Name」プロパティで取得出来ます。フォント名は「\Fフォント名;」のようにする事とします。文字フォントの番号を指定出来るようにするというのも1つの手ですが、登録していないフォント名の場合にはどうなるのか?初期値で代替するか、或いは、文字フォントに追加登録する、という事になりそうです。ここでは、文章データは特殊な独自データであるから、という事で、フォント名をそのまま記述する事とします。 勿論、データを受け渡しして、存在しないフォント名、という場合もありえます。
 
6)文字セット
フォントには、文字セットの指定があります。「SelAttributes.Charset」プロパティで取得出来ます。通常の日本語であれば「DEFAULT_CHARSET」で良いのですが、欧文フォント等では異なってくる場合があり、「DEFAULT_CHARSET」のままだと想定した文字表示が出来なくなってしまいます。ですのでこれも扱うようにします。Charsetプロパティの型は「TFontCharset」となっていますがこれは、Byte型(0〜255)となります。文字セットは「\M128;」のように記述する事とします。
 
7)大きさ
RichEditにおいて文字の大きさは、「SelAttributes.Size」プロパティで文字の高さをポイント値で、「SelAttributes.Height」プロパティで文字の高さをピクセル値で、取得出来ます。インデント値などはピクセル値ですのでここも同じくピクセル値に合わせて、「SelAttributes.Height」から取得することとします。サイズは「\H13;」のように記述する事とします。
 
8)色
RichEditにおいて文字の大きさは、「SelAttributes.Color」プロパティで取得する事が出来ます。これをこのまま利用すると、Windowsのシステムカラーをマイナス値で受け取ることになります。システムカラーはWindowsの画面色の設定で変化しますが、利用者は意図せず勝手に色が変わってしまうと問題になるでしょうから、ColorTORGB関数でシステムカラーをその時の通常のRGB値に変換してしまう事とします。色は「\Cxxxxxx」(xxxxxx:16進数のカラー値)と記述する事にします。
 
9)スタイル
スタイル(文字装飾、文字属性)には、太字、斜体、下線付き、打ち消し線付き、があります。これらは、「SelAttributes.Style」プロパティで取得する事が出来ます。
  fsBold   :太字になる
  fsItalic  :斜体になる
  fsUnderline :下線付きになる
  fsStrikeOut :打ち消し線付きになる
これらをそれぞれ「\B」「\I」「\U」「\S」にする事とします。この4つは同時指定する事が出来ます。「\b」「\i」「\u」「\s」でそれぞれ解除するものとします。「\c」で全て解除するものとします。
他にもあるようですが(上付き文字・下付き文字・ハイライト等)、情報の取得方法が不明ですので無視します。
 
書式は、以上とします。まとめます。
書式初期値内容
\A1(左) 文字揃え
「\A1」:左 「\A2」:中 「\A3」:右
\K箇条書き 例「\K18;」:インデント18px
\N改行
\Tタブ文字
\FMS ゴシックフォント名 例「\FMS 明朝;」
\MDEFAULT_CHARSET文字セット 例「\M128;」
\H13文字大きさ 例「\H13;」:高さ13px
\C000000(黒)文字色   例「\C0000FF」:赤色
\B
\I
\U
\S
文字スタイル
「\B」太字、「\I」斜体、
「\U」下線付き、「\S」打ち消し線付
\b
\i
\u
\s
\c
文字スタイル解除
「\b」太字、「\i」斜体、
「\u」下線付き、「\s」打ち消し線付
「\c」全て解除
 
RichEdit→書式の変換、書式→RichEditの変換、書式による画面表示、書式からプレーンテキストへの変換、が必要になるでしょう。
 
データの登録・読み込みについてはこれまでと同様ですので別段問題はないでしょう。但し、文字大きさやインデント値でピクセル値を扱いますからこれをmm値に変換しないといけません。文字範囲高・幅から比率として計算させるにも無理がありますので、ピクセル=ドット値→mmへの変換比率をDPI値として記述するようにします。スクリーン座標系のDPI値は通常 72DPIだと言われますが、例えば、10ピクセルの場合、10×25.4÷72=3.52777…mmとなりますから、概ね、そのような感じのように思われますので、デフォルト値を72DPIとして、これを登録するようにします。求められるmm値は小数点以下2桁で丸める事とします。文字の大きさは1文字1文字異なりますし、データ構造は先に記述した状態だと扱いにくくなりますので少し変更します。
UnitData.pas
 TDataDocument = record // 幾何要素/表記要素|独自要素|文章
  exf : Boolean ;    // 存在フラグ(True:有り False:無し)
  Layer : Integer ;   // レイヤ(1〜256)
  str : string;     // リッチテキスト
  text_x : double ;   // 文章配置基点X座標
  text_y : double ;   // 文章配置基点Y座標
  Dpi : double ;    // ピクセル→mm変換比率(標準72)
  Width : double ;   // 文章範囲幅
  Spc : double ;    // 文字間隔
  Lspc : double ;    // 行間隔
  Angle : double ;   // 文章回転角[°]
  b_pnt : Integer ;   // 文章配置基点
  Direct: Integer ;   // 文章書出し方向(1:横書き, 2:縦書き)
 end;
縦書きの場合は、文章範囲幅は、文章範囲高の意となります。文章範囲幅がもしも実際の文字幅よりも小さい場合は、実際の文字幅の方を優先します。文章各行の文字幅・文字高を計算してその最大値を取得してから、基点の処理を行い、描画を行うようにします。
 
データ追加用の関数は
function AddDataDocument(s,st:string;lay,bp,dir:integer;
 px,py,pd,pw,ps,pl,an:double) : Boolean;
のようにします。線分・文字等と同様、最初に「データ追加先の複合図形名(幾何要素) null:用紙へ追加(表記要素)」を指定するようにしています。文章に文字を記入していない場合はデータ登録せずエラーで返すようにするため、プレーンテキストを取得する関数 GetPlainText、及び、プレーンテキストからタブ・改行・空白を除去する関数 KTrim を UnitFunc.pas に追加し、これにより文字列チェックを行うようにしています。
 
これまでと同様に、文章データの描画は UnitDataGraph.pas にて行うようにします。
// 文章内の文字の表示
procedure DisplayDocumentSub(dir,sc,sa,sm,ih,iw:integer;
 xp,xq,yp,pd:double;sf,st:string;t:TMatrix);
 
// 文章の表示
procedure DisplayDocument(lay,bp,dir:integer;
 px,py,pd,pw,ps,pl,an:double;st:string;t:TMatrix);
 
文章データは、1文字1文字が前後と状況が異なる事になりますので、1文字ずつ分割して、それぞれに対して描画を行うようにしています。
 
また、文字の実際の画面への描画は UnitGraph.pas にて行いますが、GDI使用時、文字セット Charset を指定するため、手続き G_Text2 を別に用意しています。内容は、文字セットの指定を追加し、スラント角度を除去しただけでそれ以外は G_Text1 と同じです。
// 文字描画
// at : 属性 +1:太さ +2:斜体 +4:下線 +8:取消線
// x,y: 文字位置(左上点)
// h,w: 文字高・文字幅
// ch : フォント キャラクタセット番号
// an : 文字角度[°]
// fo : フォント名
// st : 文字列
// ※GDI+、Direct2Dは縦書用フォント(@〜)は使用不可能なので注意
procedure TGraphClass.G_Text2(at,x,y,h,w,ch:integer;
 an:double;fo,st:string);
 
 
それでは、ここまでのテストプログラムです。実行ファイル、gdiplus.dll、gdipフォルダは入っていません。ソースのみです。
 
CAD装置(1)
CAD装置(2)
メディア
AutoCADの
DIESELマクロ
CSV
DXF
PCES
IGES
STEP
数学とCAD
CAD作ろ!
CADを考える
 ▲PREV
 ▼NEXT
M7
Jw_cad
 
お問い合わせ 
本サイトはリンクフリーです
リンクバナー
(C)Copyright 1999-2015 By AFsoft All Rights Reserved.