テキスト表現力


文字列の原点

前回までの内容で、ある程度ビットマップを扱えるようになりました
これで、アドベンチャーや小説ゲームなどを作ることも可能でしょう

グラフィックス関連のアプリケーション、CG制作や加工などのツールや
上記したゲームなどの場合、これに加えて高度なフォント操作能力が必要です
ここからは、テキスト操作やフォントなどについて解説していくことにします

文字列を画面の中央などには位置したい場合、文字列の長さが必要になります
文字列の長さは、フォントサイズと文字列の文字数などから割り出すことができますが
Windows はこれらの計算を行って描画してくれるファンクションを用意しています

文字列の原点を定めるには SetTextAlign() 関数を使います
これを用いることで、文字列描画関数で指定する原点の位置を変えることができます

UINT SetTextAlign(HDC hdc , UINT fMode);

hdc にはデバイスコンテキストのハンドルを
fMode にはテキスト配置を決定するフラグを定数で指定します
関数が成功すると以前の設定値が、失敗すると GDI_ERROR が返ります

フラグについては、下記のリファレンスを参照してください
ここでは、文字列の水平位置中央を表す TA_CENTER と
垂直位置ベースラインを表す TA_BASELINE を指定します
#include <windows.h>

#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);
		SetTextAlign(hdc , TA_CENTER | TA_BASELINE);
		GetClientRect(hWnd , &rect);
		TextOut(hdc , rect.right / 2 , rect.bottom / 2 , TITLE , lstrlen(TITLE));
		EndPaint(hWnd , &ps);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc		= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}


テキスト描画関数の原点が、表示する文字列の中央になっていることが確認できますね
この機能を使えば、中央揃えや右揃えの計算を Windows に託すことができます

因みに、現在のテキスト配置の設定値を取得するには
GetTextAlign() ファンクションを用います

UINT GetTextAlign(HDC hdc);

hdc には、デバイスコンテキストのハンドルを指定します
成功すれば現在のテキスト配置の設定値が、失敗すれば、GDI_ERROR が返ります


タブの表現

これまで、TextOut() 関数を使って様々な文字列を表示してきましたが
この関数は、タブ文字などの処理を行うことができませんでした

Windows はタブ文字 \t (0x09) を描画するために
TextOut () の機能を拡張した、タブ文字も処理することができるテキスト描画関数
TabbedTextOut() を提供しています
LONG TabbedTextOut(
	HDC hDC,
	int X , int Y ,
	LPCTSTR lpString , int nCount ,
	int nTabPositions , LPINT lpnTabStopPositions ,
	int nTabOrigin
);
hDC には、デバイスコンテキストのハンドルを
X は文字列の描画する X 座標、Y には Y 座標をそれぞれ指定します
lpString は描画する文字列、nCout は lpString の文字数を指定します
ここまでは、TextOut() 関数と同様の引数です

nTabPositions は、lpnTabStopPositions の配列要素数を指定します
lpnTabStopPositions には、タブストップ位置が格納されている INT 配列へのポインタです
これはどういうことかというと、nTabPositions が 1 で
lpnTabStopPositions が 100 の場合、タブの間隔は 100 ピクセルになるという仕組みです
lpnTabStopPositions の配列のは、昇順である必要があります

nTabPositions が 0 の場合、タブストップは平均文字幅の8倍に設定されます
このとき、lpnTabStopPositions は無視されます
nTabOrigin にはタブの展開を開始する X 座標を指定します

戻り値は、文字列のサイズが返されます
上位ワードに高さ、下位ワードに幅が格納されています
#include <windows.h>

#define TITLE TEXT("\tKitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	TEXTMETRIC tm;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);
		GetTextMetrics(hdc , &tm);
		TextOut(hdc , 0 , 0 , TITLE , lstrlen(TITLE));
		TabbedTextOut(hdc , 0 , tm.tmHeight ,
			 TITLE , lstrlen(TITLE) , 0 , NULL , 0);
		EndPaint(hWnd , &ps);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc		= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}


TextOut() 関数でタブ文字が含まれた文字列を表示すると上のようになりましたが
TabbedTextOut() 関数は、図の下の文字を見てわかるように正しく処理されています

DrawText() 関数で DT_TABSTOP を指定すればタブの間隔指定が可能でしたが
文字間隔の指定に引数の上位バイトを使用するため
他の上位バイトを用いたフラグと併用できないという問題点があります
DrawText() でタブを処理したい場合 DrawTextEx() が推奨されます
int DrawTextEx(
	HDC hdc,
	LPTSTR lpchText , int cchText,
	LPRECT lprc , UINT dwDTFormat ,
	LPDRAWTEXTPARAMS lpDTParams
);
hdc には、デバイスコンテキストのハンドルを
lpchText は描画する文字列へのポインタ、cchText は文字数を指定します
この仕様は DrawText() と同じなので、NULL で終わる文字の場合は -1 を指定できます
lprc はテキストが書式化される矩形領域を表す RECT 構造体へのポインタ

dwDTFormat は、書式制御フラグを指定します
書式制御フラグは DT_TABSTOP を除いて DrawText() と同様です

lpDTParams は、その他の書式制御情報を格納する
DRAWTEXTPARAMS 構造体へのポインタを指定します
特に必要としなければ NULL を指定することができます

関数が成功すれば、描画したテキストの高さが、そうでなければ 0 が返ります
DRAWTEXTPARAMS 構造体は次のような定義になっています
typedef struct { 
    UINT cbSize; 
    int  iTabLength; 
    int  iLeftMargin; 
    int  iRightMargin; 
    UINT uiLengthDrawn; 
} DRAWTEXTPARAMS, FAR *LPDRAWTEXTPARAMS;
cbSize には、この構造体のサイズを指定します
iTabLength は、各タブストップの平均文字幅単位のサイズを指定します
iLeftMargin は左マージン、iRightMargin は右マージンを平均文字幅単位で指定します
uiLengthDrawn は DrawTextEx() によって処理された空白も含む文字数を表します
#include <windows.h>

#define TITLE TEXT("\tKitty on your lap\n\t\tTokyo mew mew")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;
	static DRAWTEXTPARAMS dtpParam;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		dtpParam.cbSize = sizeof (DRAWTEXTPARAMS);
		dtpParam.iTabLength = 5;
		dtpParam.iLeftMargin =
		dtpParam.iRightMargin = 0;
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);

		GetClientRect(hWnd , &rect);
		DrawTextEx(hdc , TITLE , -1 , &rect ,
			DT_EXPANDTABS | DT_TABSTOP , &dtpParam);

		EndPaint(hWnd , &ps);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc		= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}
DrawTextEx() 関数を用いてタブとタブの空白文字数を処理するプログラムの例です
DRAWTEXTPARAMS 構造体の働きに注目してください


文字間隔

TextOut() 関数などでは、文字列の文字間隔を調整することはできません
そこで、この関数を拡張した ExtTextOut() を用いれば
数値配列を用いて、それぞれの文字の間隔を同時にしていすることができます
BOOL ExtTextOut(
	HDC hdc,
	int X , int Y ,
	UINT fuOptions , CONST RECT *lprc,
	LPCTSTR lpString , UINT cbCount,
	CONST INT *lpDx
);
hdc にはデバイスコンテキストのハンドルを
X には文字列を描画する X 座標、Y には Y 座標をそれぞれ指定します

fuOptions には、文字列を描画する対象の矩形に対する処理方法を定数で指定します
定数については、下記のリファレンスを参照してください
サンプルでは、矩形をクリッピングする ETO_CLIPPED を用います
lprc は対象矩形を表す RECT 構造体へのポインタを指定します

lpString は描画する文字列を、cbCount は文字列に含まれる文字数を指定します
lpDx は、文字列の各文字間隔を格納する配列へのポインタを指定します
NULL を指定すれば、デフォルトの文字間隔で処理されます
関数が成功すれば TRUE、そうでなければ FALSE が返ります
#include <windows.h>

#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;
	int count;
	static int *iDis;

	switch (msg) {
	case WM_DESTROY:
		free(iDis);
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		iDis = malloc(lstrlen(TITLE) * sizeof ( int ));
		for (count = 0 ; count < lstrlen(TITLE) ; count++)
			iDis[count] = count + 10;
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);

		GetClientRect(hWnd , &rect);
		ExtTextOut(hdc , 0 , 0 , ETO_CLIPPED , 
			&rect , TITLE , lstrlen(TITLE) , iDis);

		EndPaint(hWnd , &ps);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc		= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}


上の図を見れば、各文字の間隔が異なっていることがわかります
このように、この関数を使えばより細かい文字列の描画を可能とします

ただし、よほど特殊なテキストレイアウトを除いて文字列の文字間隔は常に一定でしょう
全ての文字間隔を設定するには SetTextCharacterExtra() を用います
これはデバイスコンテキストの属性として保存されるため、全ての文字列に反映されます

int SetTextCharacterExtra(HDC hdc , int nCharExtra);

hdc には、デバイスコンテキストのハンドルを
nCharExtra は各文字に追加するスペースの幅をピクセル単位で指定します
関数が成功すると以前の文字間隔、失敗すると 0x80000000 が返ります
文字間隔は、デフォルトで 0 の状態です
#include <windows.h>

#define TITLE TEXT("Kitty on your lap")

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);

		SetTextCharacterExtra(hdc , 10);
		TextOut(hdc , 0 , 0 , TITLE , lstrlen(TITLE));

		EndPaint(hWnd , &ps);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc		= WndProc;
	winc.cbClsExtra		= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}
このプログラムを実行すれば、TextOut() で描画した全ての文字が
均一の文字幅で描画されていることが確認できます

因みに、現在の文字間隔を取得する GetTextCharacterExtra() もあります
この関数を使えば、デバイスコンテキストに設定されている文字間隔を取得できます

int GetTextCharacterExtra(HDC hdc);

hdc にはデバイスコンテキストのハンドルを指定します
成功すれば、現在の文字間隔、失敗すれば 0x80000000 が返ります


SetTextAlign()

UINT SetTextAlign(HDC hdc , UINT fMode);

デバイスコンテキストのテキスト配置フラグを設定します

hdc - デバイスコンテキストのハンドルを指定します
fMode - テキスト配置を決定するフラグを定数で指定します

戻り値 - 成功すると以前の設定値。失敗すると GDI_ERROR

fMode には以下の定数を組み合わせて指定することができます

定数解説
TA_BASELINE 参照点と、テキストのベースラインを揃えます
TA_BOTTOM 参照点と、境界長方形の下端を揃えます
TA_TOP 参照点と、境界長方形の上端を揃えます
デフォルトです
TA_CENTER 参照点と、境界長方形の水平方向の中心を揃えます
TA_LEFT 参照点と、境界長方形の左端を揃えます
デフォルトです
TA_RIGHT 参照点と、境界長方形の右端を揃えます
TA_NOUPDATECP 各テキスト出力関数が呼び出されても、カレントポジションを更新しません
デフォルトです
TA_RTLREADING Windows 95 :
右から左への読み取り順序で配置します
デバイスコンテキストでヘブライ語か
アラビア語のフォントを選択しているときにのみ有効です
TA_UPDATECP 各テキスト出力関数が呼び出されると、カレントポジションを更新します
カレントポジションが、参照点として使用されます
VTA_BASELINE 参照点と、テキストのベースラインを揃えます
現在のフォントが、漢字にあるような垂直方向の
デフォルトベースラインを持っている場合に
TA_BASELINE の代わりに指定します
VTA_CENTER 参照点と、境界長方形の垂直方向の中心を揃えます
現在のフォントが、漢字にあるような垂直方向の
デフォルトベースラインを持っている場合に
TA_CENTER の代わりに指定します

GetTextAlign()

UINT GetTextAlign(HDC hdc);

現在のテキスト配置の設定を取得します

hdc - デバイスコンテキストのハンドルを指定します

戻り値 -現在のテキスト配置の設定値。失敗すれば、GDI_ERROR

テキスト配置の設定値は、SetTextAlign() を参照してください

TabbedTextOut()

LONG TabbedTextOut(
	HDC hDC,
	int X , int Y ,
	LPCTSTR lpString , int nCount ,
	int nTabPositions , LPINT lpnTabStopPositions ,
	int nTabOrigin
);
タブ文字を含む文字列を描画することができます

hDC - デバイスコンテキストのハンドルを指定します
X - 文字列の描画する原点の X 座標を指定します
Y - 文字列の描画する原点の Y 座標を指定します
lpString - 描画する文字列を指定します
nCout - lpString の文字数を指定します
nTabPositions - lpnTabStopPositions の配列要素数を指定します
lpnTabStopPositions - 、タブストップ位置の配列へのポインタを指定します
nTabOrigin - タブの展開を開始する X 座標を指定します

戻り値 - 文字列のサイズ。上位ワードに高さ、下位ワードに幅

DrawTextEx()

int DrawTextEx(
	HDC hdc,
	LPTSTR lpchText , int cchText,
	LPRECT lprc , UINT dwDTFormat ,
	LPDRAWTEXTPARAMS lpDTParams
);
指定された長方形に、フォーマットしたテキストを表示します
DrawText() 関数よりも多くのフォーマット情報をサポートします

hdc - 、デバイスコンテキストのハンドルを指定します
lpchText - 描画する文字列へのポインタを指定します
cchText - 文字数を指定します
lprc - 矩形領域を表す RECT 構造体へのポインタを指定します
dwDTFormat - 書式制御フラグを指定します
lpDTParams - 書式制御情報を格納する DRAWTEXTPARAMS 構造体へのポインタを指定します

戻り値 - 成功すれば描画したテキストの高さ。そうでなければ 0

ExtTextOut()

BOOL ExtTextOut(
	HDC hdc,
	int X , int Y ,
	UINT fuOptions , CONST RECT *lprc,
	LPCTSTR lpString , UINT cbCount,
	CONST INT *lpDx
);
指定した文字列を描画します
クリップ領域や文字間隔など TextOut() 関数よりも柔軟な設定を可能としています

hdc - デバイスコンテキストのハンドルを指定します
X - 文字列を描画する原点となる X 座標を指定します
Y - 文字列を描画する原点となる Y 座標を指定します
fuOptions -対象の矩形に対する処理方法を定数で指定します
lprc - 対象矩形を表す RECT 構造体へのポインタを指定します
lpString - 描画する文字列を指定します
cbCount - 文字列に含まれる文字数を指定します
lpDx - 文字列の各文字間隔を格納する配列へのポインタを指定します

戻り値 - 成功すれば TRUE。そうでなければ FALSE

fuOptions には次の定数を組み合わせて指定することができます

定数解説
ETO_CLIPPED 文字列を矩形にクリップします
ETO_GLYPH_INDEX lpString パラメータが、GetCharacterPlacement() で渡される配列を指し
GDI で直接解析されません
グリフのインデックスが適用されるのは
TrueType ふぉんとだけですが
このフラグは、ビットマップフォントとベクトルフォントで
言語処理をバイパスするために使われます
ETO_OPAQUE 矩形を現在の背景色で塗りつぶします
ETO_RTLREADING 現在のフォントがヘブライ語、またはアラビア語の場合に
右から左へと読みます
ETO_GLYPH_INDEX と併用することはできません

SetTextCharacterExtra()

int SetTextCharacterExtra(HDC hdc , int nCharExtra);

文字間隔を設定します

hdc - デバイスコンテキストのハンドルを指定します
nCharExtra - 各文字に追加するスペースの幅をピクセル単位で指定します

戻り値 - 成功すると以前の文字間隔。失敗すると 0x80000000

GetTextCharacterExtra()

int GetTextCharacterExtra(HDC hdc);

現在の文字間隔を取得します

hdc - デバイスコンテキストのハンドルを指定します

戻り値 - 現在の文字間隔。失敗すれば 0x80000000



前のページへ戻る次のページへ