none
RichTextBox で書式付きのまま印刷したい RRS feed

  • 質問

  • お世話になります。

    表題の通り、フォームにrichTextBoxを貼り書式を設定(文字色やバックグラウンド、アンダーライン等)の状態のテキストをそのイメージで印刷日たいのですが、どうしたらいいですか?

     ワードで装飾した内容を印刷するようなことをしたいです。

    教えてください。

    VS2015 C# WinFormアプリ Windows10

    2018年11月2日 5:18

回答

すべての返信

  • 魔界の仮面弁士様、お世話になります。


    試しましたが、装飾部分が印刷されませんでした。
    質問時に書きませんでしたが、下記のようにNHunspellComponentで単語チェックで間違いの時(単語未登録)
    に単語の下に赤色の波線を引くコンポーネントに印刷を追加しています。

    テキストは印刷しますので、やっていることは正しいように思いますが、表示するアンダーライン(赤い波線)
    は、印刷してきません。
     どこか、やり方がまずいのか、はたまた、実行不可能なのか知りたいのですが、教えてください。

    コードを一部載せましたが、長くて申し訳ありません。

    これは下記よりインストールしました。
    https://www.nuget.org/packages/NHunspell/
    パッケージマネージャー
    PM> Install-Package NHunspell -Version 1.2.5554.16953


    namespace NHunspellComponent
    {
        public class CustomPaintRichText : RichTextBox, IUnderlineableSpellingControl
        {
            :

            protected override void WndProc(ref System.Windows.Forms.Message m)
            {
                :
               ここの OnPaintで、DrawWaveを読んでいる
            }
                             // アンダーライン位置に波線を引く、開始位置と終了位置
            private void DrawWave(Graphics graphics, Point Start, Point End)
            {
                StartOfLine.Y--;
                EndOfLine.Y--;

                Pen newPen = Pens.Red;

                if ((EndOfLine.X - StartOfLine.X) > 4)
                {
                    ArrayList pl = new ArrayList();
                    for (int i = StartOfLine.X; i <= (EndOfLine.X - 2); i += 4)
                    {
                        pl.Add(new Point(i, StartOfLine.Y));
                        pl.Add(new Point(i + 2, StartOfLine.Y + 2));
                    }

                    Point[] p = (Point[])pl.ToArray(typeof(Point));
                    graphics.DrawLines(newPen, p);
                }
                else
                {
                    graphics.DrawLine(newPen, StartOfLine, EndOfLine);
                }
            }

            // ///////////////////////////////////////////////////////////////////
            // 印刷
            // ///////////////////////////////////////////////////////////////////
            //
           private const double anInch = 14.4;

            [StructLayout(LayoutKind.Sequential)]
            public struct RECT
            {
                public int Left;
                public int Top;
                public int Right;
                public int Bottom;
            }

            [StructLayout(LayoutKind.Sequential)]
            private struct CHARRANGE
            {
                public int cpMin;         //First character of range (0 for start of doc)
                public int cpMax;           //Last character of range (-1 for end of doc)
            }

            [StructLayout(LayoutKind.Sequential)]
            private struct FORMATRANGE
            {
                public IntPtr hdc;             //Actual DC to draw on
                public IntPtr hdcTarget;       //Target DC for determining text formatting
                public RECT rc;                //Region of the DC to draw to (in twips)
                public RECT rcPage;            //Region of the whole DC (page size) (in twips)
                public CHARRANGE chrg;         //Range of text to draw (see earlier declaration)
            }

            private const int WM_USER = 0x0400;
            private const int EM_FORMATRANGE = WM_USER + 57;
            private const int EM_GETLINE = 0xc4;
            private const int EM_GETLINECOUNT = 0xba;
            private const int EM_LINEFROMCHAR = 0xc9;
            private const int EM_LINEINDEX = 0xbb;
            private const int EM_LINELENGTH = 0xc1;
            private const int EM_REPLACESEL = 0xc2;

            [DllImport("USER32.dll")]
            private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);

            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);

                              印刷する文字の開始、終了             実際に印刷する位置
            public int Print(int charFrom, int charTo, PrintPageEventArgs e, int top, int left, int right, int bottom)
            {
                RECT rectToPrint;
                rectToPrint.Top = (int)((double)top * anInch);
                rectToPrint.Bottom = (int)((double)bottom * anInch);
                rectToPrint.Left = (int)((double)left * anInch);
                rectToPrint.Right = (int)((double)right * anInch);

                //Calculate the size of the page
                RECT rectPage;
                rectPage.Top = (int)(e.PageBounds.Top * anInch);
                rectPage.Bottom = (int)(e.PageBounds.Bottom * anInch);
                rectPage.Left = (int)(e.PageBounds.Left * anInch);
                rectPage.Right = (int)(e.PageBounds.Right * anInch);

                IntPtr hdc = e.Graphics.GetHdc();

                FORMATRANGE fmtRange;
                fmtRange.chrg.cpMax = charTo;           //Indicate character from to character to
                fmtRange.chrg.cpMin = charFrom;
                fmtRange.hdc = hdc;                    //Use the same DC for measuring and rendering
                fmtRange.hdcTarget = hdc;              //Point at printer hDC
                fmtRange.rc = rectToPrint;             //Indicate the area on page to print
                fmtRange.rcPage = rectPage;            //Indicate size of page

                IntPtr res = IntPtr.Zero;

                IntPtr wparam = IntPtr.Zero;
                wparam = new IntPtr(1);

                //Get the pointer to the FORMATRANGE structure in memory
                IntPtr lparam = IntPtr.Zero;
                lparam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange));
                Marshal.StructureToPtr(fmtRange, lparam, false);

                //Send the rendered data for printing
                res = SendMessage(this.Handle, EM_FORMATRANGE, wparam, lparam);

                //Free the block of memory allocated
                Marshal.FreeCoTaskMem(lparam);

                //Release the device context handle obtained by a previous call
                e.Graphics.ReleaseHdc(hdc);

                //Return last + 1 character printer
                return res.ToInt32();
            }
        }
    }
     
     
    実際の印刷を呼ぶ
             public bool Print_HoukokushoForm(string PrinterName, CustomPaintRichText rich1, CustomPaintRichText rich2)
            {
                PrintPreviewDialog ppvw;
                try
                {
                    customPaintRichText1 = rich1;
                    customPaintRichText2 = rich2;
                     :
                    printDoc.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(PrintDoc_PrintPage);
                }
           }
            private void PrintDoc_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
            {
               :

               int checkPrint = customPaintRichText1.Print(0, pos3 + len, e, posY, posX, width, posY + height);

    2018年11月5日 2:49
  • お世話になります。自己レスです。

    解決したわけではありませんが、richTextBoxに

                customPaintRichText1.SelectionStart = 0;
                customPaintRichText1.SelectionLength = 10;
                customPaintRichText1.SelectionFont = new Font(fontFamily, float.Parse(fontSize), FontStyle.Italic);
                customPaintRichText1.SelectionColor = System.Drawing.Color.Red;

    のように文字フォントを指定し印刷(実際にはプレビュー)したところ赤文字イタリックの表示を確認しました。

    上記のNHunspellComponentのDrawWave()が特殊な描画をしてるようですね。

    ここを工夫出来たらうまくいくかもしえませんね。

    2018年11月5日 6:53
  • カスタムドローしている部分については自分で描画する必要があるので

    protected override void WndProc(ref Message m) {
        switch (m.Msg) {
            case EM_FORMATRANGE:
                base.WndProc(ref m);
                FORMATRANGE fmtRange = (FORMATRANGE)(Marshal.PtrToStructure(m.LParam, typeof(FORMATRANGE)));
                using (Graphics g = Graphics.FromHdc(fmtRange.hdc)) {
                    // ここでアンダーラインを描画
                }
                break;
            default:
                base.WndProc(ref m);
                break;
        }
    }
    
    こんな感じで描画はできますが、プリンタとディスプレイの解像度の違いなど、解決しなければならない問題は多いと思います。

    • 回答としてマーク ferret001 2018年11月6日 6:22
    2018年11月6日 1:17
  • お世話になります。

    実現しようとしていたものが、文字装飾でなく、ビットマップで作った画像で合成されていることに気づくまでに時間がかかり大変でした。

    実際には、アンダーラインを付ける文字列は取れていたので、印刷前に無理矢理つけて出すようにしました。

    FORMATRANGE fmtRange = (FORMATRANGE)(Marshal.PtrToStructure(m.LParam, typeof(FORMATRANGE)));
               
    using (Graphics g = Graphics.FromHdc(fmtRange.hdc)) {
                   
    // ここで装飾する関数を呼び出す・・・のですね
               
    }
    試します。 ・・・解像度の問題もあるのかぁーー

    ありがとうございました。

    2018年11月6日 6:21