摘录于《Windows程序(第5版,珍藏版).CHarles.Petzold 著》P816

我们已经学会了如何选择和创建逻辑字体,显示是时候来尝试如何进行设置文本格式了。这一过程包括将分别使用以下四个对齐方式在边界范围内进行文本对齐:左对齐、右对齐、居中或两端对齐(即从一端的边界到另一端排列文本,平均分配字符间的空白)。对于前三种情况,可以使用 DrawText 函数的 DT_WORDBREAK 参数来实现,但这种方法有其局限限。例如,你无法确定 DrawText 在输出时,哪一部分文本能够落在矩形框内。DrawText 函数对于完成一些简单的工作十分方便,但对于更复杂的文本格式设置,可能需要借助 TextOut 函数。

17.5.1  设置简单的文本格式

GetTextExtentPoint32 是处理文本时最有用的函数之一(这个函数的名称揭示了从 Windows 的早期版本以来的一些变化)。该函数可以根据当前设备环境中选定的字体,告诉你一个字符串在该字体中的宽度和高度:

GetTextExtentPoint32(hdc, pString, iCount, &size);

SIZE 结构的 cx 和 cy 字段中返回以逻辑单位来表示的该字符串的宽度和高度。这里我们用一个单行文本作为例子,假设你已经在设备环境选择了一个字体,现在想要输出如下文本:

TCHAR * szText[] = TEXT("Hello, how are you?");

文本要从纵坐标 yStart 开始,在 xLeft 和 xRight 坐标确定的边界之间。你的任务就是计算文本开始的横坐标 xStart 的值。

如果显示的文本使用的是等宽字体,那么这个任务将变得非常容易,可惜大多数情况并非如此。你首先需要得到字符串的宽度和高度:

GetTextExtentPoint32(hdc, szText, lstrlen(szText), &size);

如果 size.cx 大于(xRight - xLeft),则表示两个边界间额距离不足以容纳该字符串。先让我们假设它足够长。

如果要将文本对齐到左边界,你只需要设置 xStart 等于 xLeft,然后输出文本:

TextOut(hdc, xStart, yStart, szText, lstrlen(szText));

这十分简单。现在,你将 yStart 增加 size.cy,然后就可以开始写下一行文本了。

如果要将文本对齐到右边界,你可以对 xStart 使用这个公式:

xStart = xRight - size.cx;

如果要把文本对齐到左右边界的中央,则使用这个公式。

xStart = (xLeft + xRight - size.cx) / 2;

现在,来看一下最难的任务——调整文本使其平均分布在平均左右边界之间。两个边界之间的距离是(xRight - xLeft)。如果不做任何调整,该文本的宽度为 size.cx。这两个值之间的差为

xLeft + xRight - size.cx

这个计算出的差值必须平均分布在字符串的三个空格字符上。这听起来像一个可怕的任务,不过还不算太难。为了实现这个目标,你可以调用:

SetTextJustification(hdc, xRight - xLeft - size.cx, 3);

第二个参数就是必须分配到字符串内的空格字符上的空间大小。第三个参数是空格字符的数量,在这个例子中是 3。现在设置 xStart 等于 xLeft,用 TextOut 输出文本:

TextOut(hdc, xStart, yStart, szText, lstrlen(szText));

文本将在 xLeft 和 xRight 边界之间进行调整。

调用 SetTextJustification 时,如果要求的空间无法在空格字符之间平均分配,它将会生成一个误差项。这个误差项将会影响随后的 GetTextExtentPoint32 调用。每当开始新的一行时,需要调用下面的函数来清除这个误差项:

SetTextJustification(hdc, 0, 0);

17.5.2  段落的处理

如果你要处理整个段落,则需要从头开始扫描整个字符串寻找空格字符。每当遇到空格字符(或其他可以用来做间隔的字符),就可以调用 GetTextExtentPoint32 函数来确定左右边界之间的距离是否仍足够容纳文本。如果文本长度超过了该距离允许的范围,就回溯到前一个空格。这样你就确定了该行可以显示的字符串。如果你想调整该行,则调用 SetTextJustification 和 TextOut,清楚误差项后,继续处理下一行。

图 17-9 所示的 JUSTIFY1 程序,对马克●吐温的《哈克贝利●费恩历险记》的第一段进行了排版。你可以从对话框中选择想要的字型,也可以使用菜单选项改变对齐方式(左、右、居中或两端对齐)。图 17-10 显示了使用 JUSTIFY1 程序对某个段落处理的结果。

/*------------------------------------------------JUSTIFY1.C -- Justified Type Program #1(c) Charles Petzold, 1998
------------------------------------------------*/#include <Windows.h>
#include "resource.h"LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);TCHAR szAppName[] = TEXT("Justify1");int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{HWND        hwnd;MSG            msg;WNDCLASS    wndclass;wndclass.style = CS_HREDRAW | CS_VREDRAW;wndclass.lpfnWndProc = WndProc;wndclass.cbClsExtra = 0;wndclass.cbWndExtra = 0;wndclass.hInstance = hInstance;wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);wndclass.lpszMenuName = szAppName;wndclass.lpszClassName = szAppName;if (!RegisterClass(&wndclass)){MessageBox(NULL, TEXT("This program requires Windows NT!"),szAppName, MB_ICONERROR);return 0;}hwnd = CreateWindow(szAppName, TEXT("Justified Type #1"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL);ShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd);while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;
}void DrawRuler(HDC hdc, RECT * prc)
{static int iRuleSize [16] = { 360, 72, 144, 72, 216, 72, 144, 72,288, 72, 144, 72, 216, 72, 144, 72 } ;int        i, j ;POINT      ptClient ;SaveDC (hdc) ;// Set Logical Twips mapping modeSetMapMode (hdc, MM_ANISOTROPIC) ;SetWindowExtEx (hdc, 1440, 1440, NULL) ;SetViewportExtEx (hdc, GetDeviceCaps (hdc, LOGPIXELSX),GetDeviceCaps (hdc, LOGPIXELSY), NULL) ;// Move the origin to a half inch from upper leftSetWindowOrgEx (hdc, -720, -720, NULL) ;// Find the right margin (quarter inch from right)ptClient.x = prc->right ;ptClient.y = prc->bottom ;DPtoLP (hdc, &ptClient, 1) ;ptClient.x -= 360 ;// Draw the rulersMoveToEx (hdc, 0,          -360, NULL) ;LineTo   (hdc, ptClient.x, -360) ;MoveToEx (hdc, -360,          0, NULL) ;LineTo   (hdc, -360, ptClient.y) ;for (i = 0, j = 0 ; i <= ptClient.x ; i += 1440 / 16, j++){MoveToEx (hdc, i, -360, NULL) ;LineTo   (hdc, i, -360 - iRuleSize [j % 16]) ;}for (i = 0, j = 0 ; i <= ptClient.y ; i += 1440 / 16, j++){MoveToEx (hdc, -360, i, NULL) ;LineTo   (hdc, -360 - iRuleSize [j % 16], i) ;}RestoreDC (hdc, -1) ;
}void Justify(HDC hdc, PTSTR pText, RECT * prc, int iAlign)
{int        xStart, yStart, cSpaceChars;PTSTR   pBegin, pEnd;SIZE   size;yStart = prc->top;do                           // for each text line{cSpaceChars = 0;     // initialize number of spaces in linewhile (*pText == ' ') // skip over leading spacespText++;pBegin = pText;           // set pointer to char at beginning of linedo                       // until the line is known{pEnd = pText;       // set pointer to char at end ot line// skip to next spacewhile (*pText != '\0' && *pText++ != ' ');if (*pText == '\0')break;// after each space encountered, calculate extentscSpaceChars++;GetTextExtentPoint32(hdc, pBegin, pText - pBegin - 1, &size);} while (size.cx < (prc->right - prc->left));cSpaceChars--;                // discount last space at end of linewhile (*(pEnd - 1) == ' ') // eliminate trailing spaces{pEnd--;cSpaceChars--;}// if end of text and no space characters, set pEnd to endif (*pText == '\0' || cSpaceChars <= 0)pEnd = pText;GetTextExtentPoint32(hdc, pBegin, pEnd - pBegin, &size);switch (iAlign)                   // use alignment for xStart{case IDM_ALIGN_LEFT:xStart = prc->left;break;case IDM_ALIGN_RIGHT:xStart = prc->right - size.cx;break;case IDM_ALIGN_CENTER:xStart = (prc->right + prc->left - size.cx) / 2;break;case IDM_ALIGN_JUSTIFIED:if (*pText != '\0' && cSpaceChars > 0)SetTextJustification(hdc,prc->right - prc->left - size.cx, cSpaceChars);xStart = prc->left;break;}// display the textTextOut(hdc, xStart, yStart, pBegin, pEnd - pBegin);// prepare for next lineSetTextJustification(hdc, 0, 0);yStart += size.cy;pText = pEnd;} while (*pText && yStart < prc->bottom - size.cy);}LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static CHOOSEFONT cf;static DOCINFO    di = { sizeof(DOCINFO), TEXT("Justify1: Printing") };static int        iAlign = IDM_ALIGN_LEFT;static LOGFONT    lf;static PRINTDLG   pd;static TCHAR      szText[] = {TEXT("You don't know about me, without you ")TEXT("have read a book by the name of \"The ")TEXT("Adventures of Tom Sawyer,\" but that ")TEXT("ain't no matter. That book was made by ")TEXT("Mr. Mark Twain, and he told the truth, ")TEXT("mainly. There was things which he ")TEXT("stretched, but mainly he told the truth. ")TEXT("That is nothing. I never seen anybody ")TEXT("but lied, one time or another, without ")TEXT("it was Aunt Polly, or the widow, or ")TEXT("maybe Mary. Aunt Polly -- Tom's Aunt ")TEXT("Polly, she is -- and Mary, and the Widow ")TEXT("Douglas, is all told about in that book ")TEXT("-- which is mostly a true book; with ")TEXT("some stretchers, as I said before.") };BOOL              fSuccess;HDC               hdc, hdcPrn;HMENU             hMenu;int               iSavePointSize;PAINTSTRUCT       ps;RECT              rect;switch (message){case WM_CREATE:// Initialize the CHOOSEFONT structureGetObject(GetStockObject(SYSTEM_FONT), sizeof(lf), &lf);cf.lStructSize = sizeof(CHOOSEFONT);cf.hwndOwner = hwnd;cf.hDC = NULL;cf.lpLogFont = &lf;cf.iPointSize = 0;cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS |CF_EFFECTS;cf.rgbColors = 0;cf.lCustData = 0;cf.lpfnHook = NULL;cf.lpTemplateName = NULL;cf.hInstance = NULL;cf.lpszStyle = NULL;cf.nFontType = 0;cf.nSizeMin = 0;cf.nSizeMax = 0;return 0;case WM_COMMAND:hMenu = GetMenu(hwnd);switch (LOWORD(wParam)){case IDM_FILE_PRINT:// Get printer DCpd.lStructSize = sizeof(PRINTDLG);pd.hwndOwner = hwnd;pd.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION;if (!PrintDlg(&pd))return 0;if (NULL == (hdcPrn = pd.hDC)){MessageBox(hwnd, TEXT("Cannot obtain Printer DC"),szAppName, MB_ICONEXCLAMATION | MB_OK);return 0;}// Set margins of 1 inchrect.left = GetDeviceCaps(hdcPrn, LOGPIXELSX) -GetDeviceCaps(hdcPrn, PHYSICALOFFSETX);rect.top = GetDeviceCaps(hdcPrn, LOGPIXELSY) -GetDeviceCaps(hdcPrn, PHYSICALOFFSETY);rect.right = GetDeviceCaps(hdcPrn, PHYSICALWIDTH) -GetDeviceCaps(hdcPrn, LOGPIXELSX) -GetDeviceCaps(hdcPrn, PHYSICALOFFSETX);rect.bottom = GetDeviceCaps(hdcPrn, PHYSICALHEIGHT) -GetDeviceCaps(hdcPrn, LOGPIXELSY) -GetDeviceCaps(hdcPrn, PHYSICALOFFSETY);// Display text on printerSetCursor(LoadCursor(NULL, IDC_WAIT));ShowCursor(TRUE);fSuccess = FALSE;if ((StartDoc(hdcPrn, &di) > 0) && (StartPage(hdcPrn) > 0)){// Select font using adjusted lfHeightiSavePointSize = lf.lfHeight;lf.lfHeight = -(GetDeviceCaps(hdcPrn, LOGPIXELSY) *cf.iPointSize) / 720;SelectObject(hdcPrn, CreateFontIndirect(&lf));lf.lfHeight = iSavePointSize;// Set text colorSetTextColor(hdcPrn, cf.rgbColors);// Display textJustify(hdcPrn, szText, &rect, iAlign);if (EndPage(hdcPrn) > 0){fSuccess = TRUE;EndDoc(hdcPrn);}}ShowCursor(FALSE);SetCursor(LoadCursor(NULL, IDC_ARROW));DeleteDC(hdcPrn);if (!fSuccess)MessageBox(hwnd, TEXT("Could not print text"),szAppName, MB_ICONEXCLAMATION | MB_OK);return 0;case IDM_FONT:if (ChooseFont(&cf))InvalidateRect(hwnd, NULL, TRUE);return 0;case IDM_ALIGN_LEFT:case IDM_ALIGN_RIGHT:case IDM_ALIGN_CENTER:case IDM_ALIGN_JUSTIFIED:CheckMenuItem(hMenu, iAlign, MF_UNCHECKED);iAlign = LOWORD(wParam);CheckMenuItem(hMenu, iAlign, MF_CHECKED);InvalidateRect(hwnd, NULL, TRUE);return 0;}return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);GetClientRect(hwnd, &rect);DrawRuler(hdc, &rect);rect.left += GetDeviceCaps(hdc, LOGPIXELSX) / 2;rect.top += GetDeviceCaps(hdc, LOGPIXELSY) / 2;rect.right -= GetDeviceCaps(hdc, LOGPIXELSX) / 4;SelectObject(hdc, CreateFontIndirect(&lf));SetTextColor(hdc, cf.rgbColors);Justify(hdc, szText, &rect, iAlign);DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));EndPaint(hwnd, &ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProc(hwnd, message, wParam, lParam);
}
JUSTIFY1.RC
// Microsoft Visual C++ 生成的资源脚本。
//
#include "resource.h"/
//
// Menu
//JUSTIFY1 MENU DISCARDABLE
BEGINPOPUP "&File"BEGINMENUITEM "&Print", IDM_FILE_PRINTENDPOPUP "&Font"BEGINMENUITEM "&Font...", IDM_FONTENDPOPUP "&Align"BEGINMENUITEM "&Left", IDM_ALIGN_LEFT, CHECKEDMENUITEM "&Right", IDM_ALIGN_RIGHTMENUITEM "&Centered", IDM_ALIGN_CENTERMENUITEM "&Justified", IDM_ALIGN_JUSTIFIEDEND
EN
RESOURCE.H
// Microsoft Visual C++ generated include file.
// Used by Justify1.rc
#define IDM_FILE_PRINT                  40001
#define IDM_FONT                        40002
#define IDM_ALIGN_LEFT                  40003
#define IDM_ALIGN_RIGHT                 40004
#define IDM_ALIGN_CENTER                40005
#define IDM_ALIGN_JUSTIFIED             40006

JUSTIFY1 在程序工作区的顶端和左侧各县市一个标尺(单位当然是逻辑英寸)。DrawRuler 函数绘制了这个标尺。一个矩形结构确定了文本可以分布的区域范围。

格式化文本的大多数工作式在 Justify 函数中完成的。该函数从文本起始处开始搜索空格,并调用 GetTextExtentPoint32 函数来度量每一行。一旦该行的长度超过显示区域的宽度时,JUSTIFY1 就返回上一个空格, 然后使用当时形成的行。根据 iAlign 长数值的不同,该行可以为左对齐、右对齐、居中对齐或两端对齐。

JUSTIFY1 并不完美。比如它没有任何支持“连字符”(hyphen)的逻辑。此外,该算法在一行中单词数量少于两个的情况下完全失效。即使我们解决了这个并不是特别难的问题。如果有一个单词比左右边界间的距离更长时,该程序仍然无法正常工作。当然,当你开始编写一个可以在同一行使用多种字体的程序(比如 Windows 自带的 Wordpad 程序)时,事情会变得更加复杂。

当然,从没有人号称这些很容易办到。使用这些函数只是比你自己做从头开始完成所有的工作要容易些。

图 17-10  使用 JUSTIFY1 程序对某个段落处理的结果

17.5.3  打印预览

某些文本并不仅仅需要显示在屏幕上,它们还需要被打印出来。往往在这种情况下,屏幕上文本大打印预览必须精确符合打印机输出的格式。仅显示相同的字体、字号和文本格式是不够的。但是自从有了 TrueType 字体,这一切都变得容易了。剩下的还需要做的事情就是确保在不同设备下,段落的每一行在同一个地方换行。这才是实现“所见即所得”功能里面比较困难的部分。

JUSTIFY1 程序包括一个 Print (打印)选项,但它所做的仅仅是设置页面的上、左、右边距为 1 英寸。因此,格式和屏幕显示是完全独立的。这里你可以做一个有趣的尝试:修改 JUSTIFY1 中的几行代码,使得屏幕和打印机的逻辑都是基于 6 英寸格式的矩形。要达到此目的,改变 WM_PAINT 和 Print(打印)命令逻辑中的 rect.right 的定义即可。在 WM_PAINT 逻辑中,如下修改语句:

rect.right = rect.left + 6 * GetDeviceCaps(hdc, LOGPIXELSX);

在 Print(打印)命令逻辑中,如下修改语句:

rect.right = rect.left + 6 * GetDeviceCaps(hdcPrn, LOGPIXELSX);

如果你选择了一个 TrueType 字体,那么在屏幕上的断行应该和打印机输出时的断行是相同的。

实际上却不是。即使两个设备使用相同的字体、相同的字号并将文本显示在相同的格式矩形里,不同的显示分辨率和四舍五入引入的误差仍会导致断行出现在不同的地方。显然,需要更完善的方法来实现打印预览功能。

图 17-11 所示的 JUSTIFY2 程序试图解决这个问题。JUSTIFY2 的代码是基于 Microsoft 的 David Weise 编写的一个名为 TTJUST(TrueType Justify,TrueType 排版)的程序,而这个程序又是基于本书较早版本中的 JUSTIFY1 程序的。为了表现增长后的程序复杂度,我们将马克●吐温的摘录片段换为 Herman Melville 的《白鲸》(Movy-Dick)的第一段。

/*------------------------------------------------JUSTIFY2.C -- Justified Type Program #2(c) Charles Petzold, 1998
------------------------------------------------*/#include <Windows.h>
#include "resource.h"#define OUTWIDTH 6           // Width of formatted output in inches
#define LASTCHAR 127        // Last character code used in textLRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);TCHAR szAppName[] = TEXT("Justify2");int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{HWND        hwnd;MSG            msg;WNDCLASS    wndclass;wndclass.style = CS_HREDRAW | CS_VREDRAW;wndclass.lpfnWndProc = WndProc;wndclass.cbClsExtra = 0;wndclass.cbWndExtra = 0;wndclass.hInstance = hInstance;wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);wndclass.lpszMenuName = szAppName;wndclass.lpszClassName = szAppName;if (!RegisterClass(&wndclass)){MessageBox(NULL, TEXT("This program requires Windows NT!"),szAppName, MB_ICONERROR);return 0;}hwnd = CreateWindow(szAppName, TEXT("Justified Type #2"),WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,NULL, NULL, hInstance, NULL);ShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd);while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;
}void DrawRuler(HDC hdc, RECT * prc)
{static int iRuleSize[16] = { 360, 72, 144, 72, 216, 72, 144, 72,288, 72, 144, 72, 216, 72, 144, 72 };int        i, j;POINT      ptClient;SaveDC(hdc);// Set Logical Twips mapping modeSetMapMode(hdc, MM_ANISOTROPIC);SetWindowExtEx(hdc, 1440, 1440, NULL);SetViewportExtEx(hdc, GetDeviceCaps(hdc, LOGPIXELSX),GetDeviceCaps(hdc, LOGPIXELSY), NULL);// Move the origin to a half inch from upper leftSetWindowOrgEx(hdc, -720, -720, NULL);// Find the right margin (quarter inch from right)ptClient.x = prc->right;ptClient.y = prc->bottom;DPtoLP(hdc, &ptClient, 1);ptClient.x -= 360;// Draw the rulersMoveToEx(hdc, 0, -360, NULL);LineTo(hdc, ptClient.x, -360);MoveToEx(hdc, -360, 0, NULL);LineTo(hdc, -360, ptClient.y);for (i = 0, j = 0; i <= ptClient.x; i += 1440 / 16, j++){MoveToEx(hdc, i, -360, NULL);LineTo(hdc, i, -360 - iRuleSize[j % 16]);}for (i = 0, j = 0; i <= ptClient.y; i += 1440 / 16, j++){MoveToEx(hdc, -360, i, NULL);LineTo(hdc, -360 - iRuleSize[j % 16], i);}RestoreDC(hdc, -1);
}/*-----------------------------------------------------------------------GetCharDesignWidths: Gets character widths for font as large as theoriginal design size
-----------------------------------------------------------------------*/
UINT GetCharDesingWidths(HDC hdc, UINT uFirst, UINT uLast, int * piWidths)
{HFONT              hFont, hFontDesign;LOGFONT              lf;OUTLINETEXTMETRIC    otm;hFont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);GetObject(hFont, sizeof(LOGFONT), &lf);// Get outline text metrics (we'll only be using a field that is//  independent of the DC the font is selected into)otm.otmSize = sizeof(OUTLINETEXTMETRIC);GetOutlineTextMetrics(hdc, sizeof(OUTLINETEXTMETRIC), &otm);// Create a new font based on the design sizelf.lfHeight = -(int)otm.otmEMSquare;lf.lfWidth = 0;hFontDesign = CreateFontIndirect(&lf);// Select the font into the DC and get the character widthsSaveDC(hdc);SetMapMode(hdc, MM_TEXT);SelectObject(hdc, hFontDesign);GetCharWidth(hdc, uFirst, uLast, piWidths);SelectObject(hdc, hFont);RestoreDC(hdc, -1);// Clean upDeleteObject(hFontDesign);return otm.otmEMSquare;
}/*---------------------------------------------------------------------GetScaledWidths: Gets floating point character widths for selectedfont size
----------------------------------------------------------------------*/
void GetScaledWidths(HDC hdc, double * pdWidths)
{double dScale;HFONT    hFont;int       aiDesignWidths[LASTCHAR + 1];int       i;LOGFONT lf;UINT   uEMSquare;// Call function aboveuEMSquare = GetCharDesingWidths(hdc, 0, LASTCHAR, aiDesignWidths);// Get LOGFONT for current font in device contexthFont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);GetObject(hFont, sizeof(LOGFONT), &lf);// Scale the widths and store as floating point valuesdScale = (double)-lf.lfHeight / (double)uEMSquare;for(i = 0; i <= LASTCHAR; i++)pdWidths[i] = dScale * aiDesignWidths[i];
}/*---------------------------------------------------------------------GetTextExtentFloat: Calculates text width in floating point
----------------------------------------------------------------------*/
double GetTextExtentFloat(double * pdWidths, PTSTR psText, int iCount)
{double dWidth = 0;int     i;for (i = 0; i < iCount; i++)dWidth += pdWidths[psText[i]];return dWidth;
}/*---------------------------------------------------------------------Justify: Based on design units for screen/printer compatibility
----------------------------------------------------------------------*/
void Justify(HDC hdc, PTSTR pText, RECT * prc, int iAlign)
{double dWidth, adWidths[LASTCHAR + 1];int     xStart, yStart, cSpaceChars;PTSTR   pBegin, pEnd;SIZE   size;// Fill the adWidths array with floating point character widthsGetScaledWidths(hdc, adWidths);yStart = prc->top;do                         // for each text line{cSpaceChars = 0;     // initialize number of spaces in linewhile (*pText == ' ') // skip over leading spacespText++;pBegin = pText;           // set pointer to char at beginning of linedo                       // until the line is known{pEnd = pText;       // set pointer to char at end ot line// skip to next spacewhile (*pText != '\0' && *pText++ != ' ');if (*pText == '\0')break;// after each space encountered, calculate extentscSpaceChars++;dWidth = GetTextExtentFloat(adWidths, pBegin,pText - pBegin - 1);} while (dWidth < (prc->right - prc->left));cSpaceChars--;                // discount last space at end of linewhile (*(pEnd - 1) == ' ') // eliminate trailing spaces{pEnd--;cSpaceChars--;}// if end of text and no space characters, set pEnd to endif (*pText == '\0' || cSpaceChars <= 0)pEnd = pText;GetTextExtentPoint32(hdc, pBegin, pEnd - pBegin, &size);switch (iAlign)                   // use alignment for xStart{case IDM_ALIGN_LEFT:xStart = prc->left;break;case IDM_ALIGN_RIGHT:xStart = prc->right - size.cx;break;case IDM_ALIGN_CENTER:xStart = (prc->right + prc->left - size.cx) / 2;break;case IDM_ALIGN_JUSTIFIED:if (*pText != '\0' && cSpaceChars > 0)SetTextJustification(hdc,prc->right - prc->left - size.cx,cSpaceChars);xStart = prc->left;break;}// display the textTextOut(hdc, xStart, yStart, pBegin, pEnd - pBegin);// prepare for next lineSetTextJustification(hdc, 0, 0);yStart += size.cy;pText = pEnd;} while (*pText && yStart < prc->bottom - size.cy);}LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{static CHOOSEFONT cf;static DOCINFO    di = { sizeof(DOCINFO), TEXT("Justify2: Printing") };static int        iAlign = IDM_ALIGN_LEFT;static LOGFONT    lf;static PRINTDLG   pd;static TCHAR      szText[] = {TEXT("Call me Ishmael. Some years ago -- never ")TEXT("mind how long precisely -- having little ")TEXT("or no money in my purse, and nothing ")TEXT("particular to interest me on shore, I ")TEXT("thought I would sail about a little and ")TEXT("see the watery part of the world. It is ")TEXT("a way I have of driving off the spleen, ")TEXT("and regulating the circulation. Whenever ")TEXT("I find myself growing grim about the ")TEXT("mouth; whenever it is a damp, drizzly ")TEXT("November in my soul; whenever I find ")TEXT("myself involuntarily pausing before ")TEXT("coffin warehouses, and bringing up the ")TEXT("rear of every funeral I meet; and ")TEXT("especially whenever my hypos get such an ")TEXT("upper hand of me, that it requires a ")TEXT("strong moral principle to prevent me ")TEXT("from deliberately stepping into the ")TEXT("street, and methodically knocking ")TEXT("people's hats off -- then, I account it ")TEXT("high time to get to sea as soon as I ")TEXT("can. This is my substitute for pistol ")TEXT("and ball. With a philosophical flourish ")TEXT("Cato throws himself upon his sword; I ")TEXT("quietly take to the ship. There is ")TEXT("nothing surprising in this. If they but ")TEXT("knew it, almost all men in their degree, ")TEXT("some time or other, cherish very nearly ")TEXT("the same feelings towards the ocean with ")TEXT("me.") };BOOL              fSuccess;HDC               hdc, hdcPrn;HMENU             hMenu;int               iSavePointSize;PAINTSTRUCT       ps;RECT              rect;switch (message){case WM_CREATE:// Initialize the CHOOSEFONT structurehdc = GetDC(hwnd);lf.lfHeight = -GetDeviceCaps(hdc, LOGPIXELSX) / 6;lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;lstrcpy(lf.lfFaceName, TEXT("Times New Roman"));ReleaseDC(hwnd, hdc);cf.lStructSize = sizeof(CHOOSEFONT);cf.hwndOwner = hwnd;cf.hDC = NULL;cf.lpLogFont = &lf;cf.iPointSize = 120;// Set flags for TrueType only!cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS |CF_TTONLY | CF_EFFECTS;cf.rgbColors = 0;cf.lCustData = 0;cf.lpfnHook = NULL;cf.lpTemplateName = NULL;cf.hInstance = NULL;cf.lpszStyle = NULL;cf.nFontType = 0;cf.nSizeMin = 0;cf.nSizeMax = 0;return 0;case WM_COMMAND:hMenu = GetMenu(hwnd);switch (LOWORD(wParam)){case IDM_FILE_PRINT:// Get printer DCpd.lStructSize = sizeof(PRINTDLG);pd.hwndOwner = hwnd;pd.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION;if (!PrintDlg(&pd))return 0;if (NULL == (hdcPrn = pd.hDC)){MessageBox(hwnd, TEXT("Cannot obtain Printer DC"),szAppName, MB_ICONEXCLAMATION | MB_OK);return 0;}// Set margins of OUTWIDTH inchs widerect.left = (GetDeviceCaps(hdcPrn, LOGPIXELSX) -GetDeviceCaps(hdcPrn, PHYSICALOFFSETX) * OUTWIDTH) / 2- GetDeviceCaps(hdcPrn, PHYSICALOFFSETX);rect.right = rect.left +GetDeviceCaps(hdcPrn, LOGPIXELSX) * OUTWIDTH;// Set margins of 1 inch at top and bottomrect.top = GetDeviceCaps(hdcPrn, LOGPIXELSY) -GetDeviceCaps(hdcPrn, PHYSICALOFFSETY);rect.bottom = GetDeviceCaps(hdcPrn, PHYSICALHEIGHT) -GetDeviceCaps(hdcPrn, LOGPIXELSY) -GetDeviceCaps(hdcPrn, PHYSICALOFFSETY);// Display text on printerSetCursor(LoadCursor(NULL, IDC_WAIT));ShowCursor(TRUE);fSuccess = FALSE;if ((StartDoc(hdcPrn, &di) > 0) && (StartPage(hdcPrn) > 0)){// Select font using adjusted lfHeightiSavePointSize = lf.lfHeight;lf.lfHeight = -(GetDeviceCaps(hdcPrn, LOGPIXELSY) *cf.iPointSize) / 720;SelectObject(hdcPrn, CreateFontIndirect(&lf));lf.lfHeight = iSavePointSize;// Set text colorSetTextColor(hdcPrn, cf.rgbColors);// Display textJustify(hdcPrn, szText, &rect, iAlign);if (EndPage(hdcPrn) > 0){fSuccess = TRUE;EndDoc(hdcPrn);}}ShowCursor(FALSE);SetCursor(LoadCursor(NULL, IDC_ARROW));DeleteDC(hdcPrn);if (!fSuccess)MessageBox(hwnd, TEXT("Could not print text"),szAppName, MB_ICONEXCLAMATION | MB_OK);return 0;case IDM_FONT:if (ChooseFont(&cf))InvalidateRect(hwnd, NULL, TRUE);return 0;case IDM_ALIGN_LEFT:case IDM_ALIGN_RIGHT:case IDM_ALIGN_CENTER:case IDM_ALIGN_JUSTIFIED:CheckMenuItem(hMenu, iAlign, MF_UNCHECKED);iAlign = LOWORD(wParam);CheckMenuItem(hMenu, iAlign, MF_CHECKED);InvalidateRect(hwnd, NULL, TRUE);return 0;}return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);GetClientRect(hwnd, &rect);DrawRuler(hdc, &rect);rect.left += GetDeviceCaps(hdc, LOGPIXELSX) / 2;rect.top += GetDeviceCaps(hdc, LOGPIXELSY) / 2;rect.right = rect.left + OUTWIDTH * GetDeviceCaps(hdc, LOGPIXELSX);SelectObject(hdc, CreateFontIndirect(&lf));SetTextColor(hdc, cf.rgbColors);Justify(hdc, szText, &rect, iAlign);DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));EndPaint(hwnd, &ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProc(hwnd, message, wParam, lParam);
}
JUSTIFY2.RC
// Microsoft Visual C++ 生成的资源脚本。
//
#include "resource.h"/
//
// Menu
//JUSTIFY2 MENU DISCARDABLE
BEGINPOPUP "&File"BEGINMENUITEM "&Print", IDM_FILE_PRINTENDPOPUP "&Font"BEGINMENUITEM "&Font...", IDM_FONTENDPOPUP "&Align"BEGINMENUITEM "&Left", IDM_ALIGN_LEFT, CHECKEDMENUITEM "&Right", IDM_ALIGN_RIGHTMENUITEM "&Centered", IDM_ALIGN_CENTERMENUITEM "&Justified", IDM_ALIGN_JUSTIFIEDEND
EN
RESOURCE.H
// Microsoft Visual C++ generated include file.
// Used by Justify2.rc
#define IDM_FILE_PRINT                  40001
#define IDM_FONT                        40002
#define IDM_ALIGN_LEFT                  40003
#define IDM_ALIGN_RIGHT                 40004
#define IDM_ALIGN_CENTER                40005
#define IDM_ALIGN_JUSTIFIED             40006

JUSTIFY2 仅对 TrueType 字体有效。该程序在 GetCharDesignWidths 函数中,曾调用 GetOutlineTextMetrics 函数来得到一些看似不起眼的信息,即OUTLINETEXTMETRIC 结构中的 otmEMSquare 字段。

在设计一个 TrueType 字体时,使用了“EM 正方形”(EM Square)网格。一个特定的 TrueType 字体的所有字符虽然一般来说宽度都不相同,但都在同样的网格中被设计出来。OUTLINETEXTMETRIC 结构的 otmEMSquare 字段给出了该字体的网格尺寸。对于大多数的 TrueType 字体,你会发现 otmEMSquare 字段都等于 2048,这意味着这些字体是在 2048 * 2048 的网格中设计的。

这里的关键是:你可以使用某个你需要的特定的 TrueType 字体的字样名字来建立一个 LOGFONT 结构,但要将 lfHeight 字段赋值为 otmEMSquare 值的相反数。在创建好字体并将其选择到设备环境中后,你可以调用 GetCharWidth 函数。这个函数会告诉你该字体中的每个字符的逻辑单位宽度。通常这些字符宽度并不准确,因为它们的字体大小被缩放过了。但是,如果该字体的 lfHeight 使用了 otmEMSquare,得到的宽度总是准确的整数,与设备环境无关。

GetCharDesignWidths 函数通过这个方法获得字符的原始设计宽度,并存储在一个整数数组里,JUSTIFY2 程序知道它的文本只使用 ASCII 字符,因此这个数组不必很大。GetScaledWidths 函数根据字体在设备逻辑坐标中的实际点值大小,将这些整数宽度转换为浮点宽度。GetTextExtentFloat 函数使用这些浮点宽度计算整个字符串的宽度。这就是新的 Justify 函数使用的计算文本行宽度的方法。

17.5 设置段落格式相关推荐

  1. 如何设置段落格式html,dreamweaver设置段落格式的方法

    dreamweaver cs6官方电脑版下载(32位64位) 软件大小:278MB授权方式:免费软件 立即下载 Dreamweaver 是第一套针对专业网页设计师特别发展的视觉化网页开发工具,也是目前 ...

  2. Mac 版本 word 如何设置 '段落' 格式

    刚用 Mac 版本的 Word 时, 一直找不到如何设置 '段落' 格式, 这里记录一下 在 Home 目录下找到 Styles Pane, 如下图 点击后可以选择 New Style 来新建一个 S ...

  3. word后续段落格式变掉_如何在Word中设置段落格式

    word后续段落格式变掉 Word provides default font formatting and paragraph formatting that is applied to conte ...

  4. dw中css怎么设置段落格式,Dreamweaver文本段落缩进的五种方法

    一.用自Dreaweave身所带的功能 1.在Dreamweaver中执行下面操作4次就可以插入两个汉字大小的空格. 2.用同背景色相同颜色的字符来完成插入空格,这种方法操作比较繁琐.不易修改,而且在 ...

  5. HTML设置文字与段落格式

    目录 一.插入其他标记 二.设置文字格式 三.设置段落格式 四.水平线标记 五.设置滚动文字 一.插入其他标记 在HTNL网页中,不仅可以输入汉字.英文和其他文字,还可以输入一些空格和特殊字符(¥.$ ...

  6. Python-docx 模块读写 Word 文档基础(一):创建文档、段落格式、字体格式设置方法

    Python-docx 模块读写 Word 文档基础(一):创建文档.段落格式.字体格式设置方法 前言: 1.创建 Word 文档及基础用法: 2.段落格式设置: 3.字体格式设置: 结尾: [Pyt ...

  7. Python控制Word文件中段落格式与文本格式

    封面图片:<Python程序设计实验指导书>(ISBN:9787302525790),董付国,清华大学出版社 图书详情(京东): ================ 本文主要介绍扩展库pyt ...

  8. python word排版_Python控制Word文件中段落格式与文本格式

    本文主要介绍扩展库python-docx中关于Word文件中文本格式控制的接口和用法,可以使用命令pip install python-docx安装,然后通过名字docx来使用其中提供的功能. 1.设 ...

  9. 《版式设计——日本平面设计师参考手册》—第1章段落格式的设置

    本节书摘来自异步社区<版式设计--日本平面设计师参考手册>一书中的第1章段落格式的设置,作者+Designing编辑部,更多章节内容可以访问云栖社区"异步社区"公众号查 ...

最新文章

  1. python3 多进程共享变量实现方法
  2. IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token
  3. 记录 之 一个小bug:Unsupported syntax ‘Starred‘
  4. 使用sqlserver日期函数获取当前日期
  5. python中forward_符合python风格的对象
  6. My Opportunity应用点击Edit后出现time out的错误分析
  7. 【IfICan】脚步很乱!
  8. 为什么我们要使用Async、Await关键字
  9. 一步步编写操作系统 45 linux的elf可执行文件中的段和节
  10. webservice采用ssl/https传输
  11. Unity脚本各种[XXX]的用法
  12. TensorFlow 教程 --进阶指南--3.4数据读取
  13. javaWeb(入门基础详解)
  14. node下使用jquery
  15. JavaScript 霸榜、TypeScript 爆发、开源吞噬世界,GitHub 年度报告正式发布!
  16. Razor语法(四)
  17. 开源ext2read代码走读之-扩展分区与逻辑分区说明及如何读取扩展分区的分区表(EBR)
  18. 雪球产品,场外雪球结构介绍
  19. 确定sw1开关信号输入端口_三菱PLC入门 | FX2N系列PLC的信号输入端子接线(图文详解)...
  20. C++图常用库boost graph library

热门文章

  1. 论如何在DEV C++中修改窗口的图标(包括任务栏图标)
  2. ug许可证错误未连接服务器10004,ug12许可错误,服务器未连接-10004ug打开报错 | 老伙计...
  3. Android 写一个可以横向滑动条目的列表
  4. 2015 国际程序员节
  5. 超级计算机怎样计算台风走势,2020年台风来得晚?超级计算机:五一有“苗头”,模拟可能达15级...
  6. 单片机双机通信c语言实验心得,单片机双机通信实验报告.doc
  7. 将jar包发布到本地maven仓库
  8. 基于IC卡技术的城市信息平台的建设
  9. 2021全球网站流量最高的网站,Python 带你看一看
  10. 模糊理论在机器人传感器中的应用_超声波传感器和激光雷达传感器在机器人避障中的应用...