目次
サンプル・プログラム
mimetexwin.msi | インストーラ |
bin/mimetexwin.exe | 実行プログラム本体 |
bin/etc/mimetex.exe | mimeTeX実行プログラム |
bin/etc/help.chm | ヘルプ・ファイル |
sour/mimetexwin.cpp | ソース・プログラム |
sour/resource.h | リソース・ヘッダ |
sour/resource.rc | リソース・ファイル |
sour/application.ico | アプリケーション・アイコン |
sour/makefile | ビルド |
バージョン | 更新日 | 内容 |
---|---|---|
1.2.6 | 2024/11/09 | 使用ライブラリ更新 |
1.2.5 | 2024/07/14 | 使用ライブラリ更新 |
1.2.4 | 2024/03/06 | 使用ライブラリ更新 |
1.2.3 | 2023/10/14 | 使用ライブラリ更新 |
1.2.2 | 2023/06/03 | 使用ライブラリ更新 |
mimeTeX の入手
今回は、Windows用実行プログラム mimetex.exe を子プロセスで起動させ、GIFファイルを生成する方式を採った。
使用ライブラリ
GIFファイルを扱うために、Windowsの GDI+ を利用する。
ライブラリなど必要はファイルは "\Windows\System32" に導入されているはずなので、追加で用意するものはない。
リソースの準備
ResEdit を起動し、resource.rc を用意する。
Eclipse に戻り、ソース・プログラム "mimetexwin.cpp" を追加する。
今回は GDI+ を使用する関係で、リンカー・フラグを -mwindows -static -lstdc++ -lgcc -lwinpthread -lgdi32 -lgdiplus "C:\Windows\system32\GdiPlus.dll" "C:\Windows\System32\ole32.dll" に設定する。
MSYS2 コマンドラインからビルドするのであれば、"makefile" を利用してほしい。
解説:初期値
mimetexwin.cpp
34: #define MAKER "pahoo.org" //作成者
35: #define APPNAME "mimetexwin" //アプリケーション名
36: #define APPNAMEJP "TEX画像ファイル作成" //アプリケーション名(日本語)
37: #define APPVERSION "1.2.6" //バージョン
38: #define APPYEAR "2020-2024" //作成年
39: #define REFERENCE "https://www.pahoo.org/e-soul/webtech/cpp01/cpp01-07-01.shtm" // 参考サイト
40:
41: //char*バッファサイズ
42: #define SIZE_BUFF 5120
43:
44: //標準フォント
45: #define FONT_FACE "MS UI Gothic"
46:
47: //現在のインターフェイス
48: static HINSTANCE hInst;
49:
50: //アプリケーション・ウィンドウ
51: HWND hParent;
52:
53: //アプリケーション・ウィンドウ位置
54: unsigned hParent_X, hParent_Y;
55:
56: //エラー・メッセージ格納用
57: string ErrorMessage;
58:
59: //TeX格納用
60: string TeX;
61:
62: //ヘルプ・ファイル
63: #define HELPFILE ".\\etc\\help.chm"
64:
65: //mimeTex.exe
66: #define MIMETEX ".\\etc\\mimetex.exe"
67:
68: //TeX文の初期値
69: #define TEX_DEF "\\large f^\\prime(x)\\ = \\lim_{\\Delta x\\to0}\\frac{f(x+\\Delta x)-f(x)}{\\Delta x}"
その他の定数は、自由に変更できる。
解説:テンポラリファイル名を取得
mimetexwin.cpp
489: /**
490: * テンポラリファイル名を取得(Windows API使用+拡張子.gif)
491: * @param char* tpmname テンポラリファイル名を格納(フルパス)
492: * @return なし
493: */
494: void getTempFname(char* tmpname) {
495: char szTempPath[MAX_PATH + 1];
496:
497: GetTempPath(sizeof(szTempPath) / sizeof(szTempPath[0]), szTempPath);
498: GetTempFileName(szTempPath, "mimetex", 0, tmpname);
499: remove(tmpname); //ごみファイル削除
500: strcat(tmpname, ".gif");
501: }
解説:mimeTeX実行
mimetexwin.cpp
503: /**
504: * mimeTeX実行(system関数使用)
505: * @param const char *tex TeX文
506: * @param const char *tpmname 一時出力するgifファイル名
507: * @return なし
508: */
509: void makeTeX(const char *tex, const char *tmpname) {
510: char cmd[SIZE_BUFF + 1];
511: for (int i = 0; i < SIZE_BUFF + 1; i++) cmd[i] = 0;
512:
513: strcat(cmd, MIMETEX);
514: strcat(cmd, " -e ");
515: strcat(cmd, tmpname);
516: strcat(cmd, " \"");
517: strcat(cmd, tex);
518: strcat(cmd, "\"");
519: system(cmd);
520: }
解説:画像ファイルの表示
mimetexwin.cpp
522: /**
523: * 画像ファイルの表示
524: * @param HWND hWnd 表示するウィンドウ・ハンドル
525: * @param const char *tpmname 表示するgifファイル名
526: * @return なし
527: */
528: void onPaint(HWND hWnd, const char *tmpname) {
529: WCHAR szFileW[SIZE_BUFF + 1];
530: IStream *pStream;
531:
532: TCHAR *szFile = TEXT((char *)tmpname);
533: MultiByteToWideChar(932, 0, szFile,-1, szFileW, sizeof(szFileW) / sizeof(TCHAR));
534: Graphics mygraphics(hWnd);
535: //表示領域クリア
536: mygraphics.Clear(Gdiplus::Color(255, 255, 255, 255));
537: Bitmap *image;
538: //ストリーム読み込み準備
539: HANDLE hFile = CreateFile(szFile, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
540: if (!hFile) return;
541: DWORD dwFileSize = GetFileSize(hFile, NULL);
542: HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
543: //ロックしてポインタ取得
544: LPVOID lpBuf = GlobalLock(hGlobal);
545: //メモリ読み込み
546: ReadFile(hFile, lpBuf, dwFileSize, &dwFileSize, NULL);
547: CloseHandle(hFile);
548: //アンロック
549: GlobalUnlock(hGlobal);
550: //ストリーム変換
551: CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
552: //ストリームから読み込み
553: image = Bitmap::FromStream(pStream);
554: //画面表示
555: mygraphics.DrawImage(image, 10, 10);
556: //メモリ解放
557: GlobalFree(hGlobal);
558: }
ここで、画像ファイルを Bitmap::FromFile メソッドで読み込もうとすると、本アプリが終了するまでファイル・ハンドルを握って離さないため、テンポラリファイルが削除できなくなってしまう。
そこで面倒ではあるが、テンポラリファイルをメモリに読み込んで、ストリームに変換。Bitmap::FromStream メソッドで画像を読み込んで表示するようにした。
解説:画像ファイルの保存
mimetexwin.cpp
560: /**
561: * 画像ファイルの保存
562: * @param const char *tpmname 一時ファイル名
563: * @return なし
564: */
565: void saveTeX(const char* tmpname) {
566: static char fname[MAX_PATH + 1];
567: strcat(fname, "mimetex.gif");
568: OPENFILENAME of;
569:
570: //OPENFILENAME構造体のサイズをセット
571: memset(&of, 0, sizeof(OPENFILENAME));
572: of.lStructSize = sizeof(OPENFILENAME);
573: //ダイアログボックスを所有するウィンドウへのハンドル
574: of.hwndOwner = hParent;
575: of.lpstrFilter = TEXT("*.gif\0*.gif\0\0");
576: //ファイル名を格納したバッファのアドレス
577: of.lpstrFile = (LPTSTR)fname;
578: //lpstrFileメンバで指定されるバッファのサイズ
579: of.nMaxFile = MAX_PATH;
580: of.Flags = OFN_OVERWRITEPROMPT;
581: //デフォルトの拡張子を格納したバッファのアドレス
582: of.lpstrDefExt = TEXT("gif");
583: //コモンダイアログの表示
584: GetSaveFileName(&of);
585: copyFile(tmpname, fname);
586: }
解説:イベントハンドラ:バージョン表示ダイアログ
mimetexwin.cpp
423: /**
424: * イベントハンドラ:バージョン表示ダイアログ
425: * @param HWND hDlg ウィンドウ・ハンドラ
426: * @paramm UINT uMsg メッセージ識別子
427: * @param WPARAM wParam メッセージの最初のパラメータ
428: * @paramL PARAM lParam メッセージの2番目のパラメータ
429: * @return INT_PTR CALLBACK TRUE:メッセージ処理完了/FALSE:未完了
430: */
431: INT_PTR CALLBACK processHelp(HWND hDlg, UINT uMsg,
432: WPARAM wParam, LPARAM lParam) {
433: switch (uMsg) {
434: //ダイアログ初期化
435: case WM_INITDIALOG:
436: CenterWindow(hDlg);
437: setStrEditBox(hDlg, IDC_TEXT_HELP, Version);
438: break;
439:
440: //ボタン押下
441: case WM_COMMAND:
442: switch (LOWORD(wParam)) {
443: //実行
444: case IDC_BUTTON_OK:
445: EndDialog(hDlg, 0);
446: break;
447: default:
448: return 1;
449: }
450: break;
451: //プログラム終了
452: case WM_CLOSE:
453: EndDialog(hDlg, 0);
454: break;
455: }
456: return 0;
457: }
表示メッセージはC++の 生文字リテラルを使っているが、ここで変数を利用できるように、Boost C++ライブラリから "format.hpp" を利用した。
解説:イベントハンドラ:メインウィンドウ
mimetexwin.cpp
603: /**
604: * イベントハンドラ:メインウィンドウ
605: * @param HWND hDlg 親ウィンドウ・ハンドラ
606: * @paramm UINT uMsg メッセージ識別子
607: * @param WPARAM wParam メッセージの最初のパラメータ
608: * @paramL PARAM lParam メッセージの2番目のパラメータ
609: * @return INT_PTR CALLBACK TRUE:メッセージ処理完了/FALSE:未完了
610: */
611: INT_PTR CALLBACK processMain(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
612: HICON hIcon;
613: GdiplusStartupInput gpSI;
614: ULONG_PTR lpToken;
615: static HWND hTex;
616: static char tmpname[MAX_PATH + 1];
617: char *str;
618:
619: switch(uMsg){
620: //ダイアログ初期化
621: case WM_INITDIALOG:
622: hParent = hDlg;
623: hIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0);
624: SendMessage(hParent, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
625: setFont(GetDlgItem(hParent, IDC_EDIT_TEX), 16, FONT_FACE);
626: //オプション読み込み
627: loadParameter();
628: setStrEditBox(hDlg, IDC_EDIT_TEX, TeX);
629: //アプリケーション・ウィンドウ移動
630: SetWindowPos(hParent, NULL, hParent_X, hParent_Y, 0, 0, (SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER));
631: getTempFname(tmpname);
632: //GDI+初期化
633: GdiplusStartup(&lpToken, &gpSI, NULL);
634: break;
635:
636: //ボタン押下
637: case WM_COMMAND:
638: switch (LOWORD(wParam)) {
639: //実行
640: case IDC_BUTTON_EXEC:
641: case IDM_EXEC:
642: TeX = getStrEditBox(hParent, IDC_EDIT_TEX);
643: makeTeX(TeX.c_str(), tmpname);
644: hTex = GetDlgItem(hParent, IDC_IMAGE_TEX);
645: onPaint(hTex, tmpname);
646: break;
647: //保存
648: case IDC_BUTTON_SAVE:
649: case IDM_SAVE:
650: saveTeX(tmpname);
651: break;
652: //設定クリア+アプリ終了
653: case IDM_CLEAR_PARAMETER:
654: delParameter();
655: //テンポラリ・ファイル削除
656: remove(tmpname);
657: EndDialog(hParent, 0);
658: return 0;
659: break;
660: //ヘルプ
661: case IDM_HELP:
662: ShellExecute(hParent, _T("open"), _T(HELPFILE), NULL, NULL, SW_RESTORE);
663: break;
664: //バージョン表示
665: case IDM_VERSION:
666: createHelp(hParent, processHelp);
667: break;
668: //解説サイト
669: case IDM_PAHOO:
670: ShellExecute(NULL, _T("open"), _T(REFERENCE), NULL, NULL, SW_RESTORE);
671: break;
672: //コピー
673: case IDM_COPY:
674: setClipboardData(getStrEditBox(hParent, IDC_EDIT_TEX));
675: break;
676: //貼り付け
677: case IDM_PASTE:
678: str = getClipboardData();
679: if (str != NULL) {
680: setStrEditBox(hParent, IDC_EDIT_TEX, str);
681: }
682: break;
683: //切り取り
684: case IDM_DELETE:
685: setStrEditBox(hParent, IDC_EDIT_TEX, "");
686: break;
687: //プログラム終了
688: case IDM_QUIT:
689: //オプション保存
690: saveParameter();
691: remove(tmpname);
692: //GDI+終了
693: GdiplusShutdown(lpToken);
694: EndDialog(hParent, 0);
695: return 0;
696: default:
697: return 1;
698: }
699: break;
700: //プログラム終了
701: case WM_CLOSE:
702: //オプション保存
703: saveParameter();
704: remove(tmpname);
705: //GDI+終了
706: GdiplusShutdown(lpToken);
707: EndDialog(hParent, 0);
708: return 0;
709: }
710: return 0;
711: }
ダイアログ初期化の際に GDI+ を初期化し、プログラム終了時に GDI+ を終了する。
いくつかのボタンとメニューは共通の機能を担っている。
解説:Windowsメインプログラム
mimetexwin.cpp
714: /**
715: * Windowsメインプログラム
716: * @param HINSTANCE hInstance インスタンスハンドル
717: * @paramm HINSTANCE hPrevInstance 未使用(常にNULL):Win16時代の名残
718: * @param LPSTR lpCmdLine コマンドライン引数
719: * @paramL int nShowCmd ウィンドウの表示方法
720: * @return int リターンコード
721: */
722: int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
723: LoadLibrary("RICHED20.DLL");
724: hInst = hInstance;
725: DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, (DLGPROC)processMain);
726:
727: return 0;
728: }
プログラムの冒頭で "RICHED20.DLL" を読み込んでやる必要がある。
その他の関数、ヘルプファイルやインストーラー作成方法については、これまでの連載で説明してきたとおりである。
参考サイト
- PHPでTeXを表示する:ぱふぅ家のホームページ
- mimeTeX
- Boost C++ Libraries
- doc2htmlhelp:s7taka氏
(2024年11月9日)使用ライブラリ更新
(2024年7月14日)使用ライブラリ更新
(2024年3月6日)使用ライブラリ更新