第17章 文本和字体_17.5 设置段落格式

17.5 设置段落格式

17.5.1 设置简单的文本格式

(1)对齐方式及起始坐标:设字符串的长度为size.cx


对齐方式


文本输出的起始位置(设输出框左右边界分别为xLeft、XRight)


左对齐


xStart =xLeft;


右对齐


xStart = xRight – size.cx


中间对齐


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


两端对齐


xStart = xLeft,但要调用SetTextJustification进行调整

(2)两个重要函数

  ①GetTextExtentPoint32(hdc,pString,iCount,&size);——计算字符串长度

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

    //参数2表示要分配到字符串内空格字符上的空间大小;即输出框中剩余的空间大小。

    //参数3表示该范围内(xRight -xLeft)的字符串中空格的数量。

(3)每次调用SetTextJustification后,如果要求的空间无法在空格字符间平均分配,将产生一个误差项。这个误差会影响下一次的GetTextExtentPoint32。所以当开始新的一行时,要清除这个误差项,即SetTextJusitifaction(hdc,0,0);

17.5.2 段落的处理——图解段落调整(代码部分见示例中的Justify函数)

【Justify1程序】
效果图

/*------------------------------------------------------------
JUSTIFY1.C -- Justified Type Program #1
(c)Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawRuler(HDC hdc, RECT *pRc);
void Justify(HDC hdc, PTSTR pText, RECT *pRc, int iAlign);
TCHAR szAppName[] = TEXT("Justify1");
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
    HWND         hwnd;
    MSG          msg;
    WNDCLASSEX     wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.cbSize = sizeof(WNDCLASSEX);
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(hInstance, szAppName);
    wndclass.hIconSm = LoadIcon(hInstance, szAppName);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = szAppName;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClassEx(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
                   szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
                        TEXT("Justified Type #1"), // window caption
                        WS_OVERLAPPEDWINDOW,        // window style
                        CW_USEDEFAULT,              // initial x position
                        CW_USEDEFAULT,              // initial y position
                        CW_USEDEFAULT,              // initial x size
                        CW_USEDEFAULT,              // initial y size
                        NULL,                       // parent window handle
                        NULL,                       // window menu handle
                        hInstance,                  // program instance handle
                        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    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.") };
    static LOGFONT     lf;
    static CHOOSEFONT  cf;
    static int iAlign = IDM_ALIGN_LEFT;
    HDC         hdc, hdcPrn;
    static PRINTDLG    pd;
    static DOCINFO     di = { sizeof(DOCINFO), TEXT("Justify1:Printing") };
    PAINTSTRUCT ps;
    RECT        rect;
    HMENU       hMenu;
    BOOL        fSuccess;
    int         iSavePointSize;

    switch (message)
    {
    case WM_CREATE:
        //初始化CHOOSEFONT结构体
        memset(&cf, 0, sizeof(CHOOSEFONT));
        cf.lStructSize = sizeof(CHOOSEFONT);
        cf.hwndOwner = hwnd;
        cf.lpLogFont = &lf;
        cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_EFFECTS;
        return 0;
    case WM_COMMAND:
        hMenu = GetMenu(hwnd);
        switch (LOWORD(wParam))
        {
        case IDM_FILE_PRINT:
            //获取打印机DC
            pd.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;
            }
            //设置1英寸的页边距
            rect.left = GetDeviceCaps(hdcPrn, LOGPIXELSX) -
                GetDeviceCaps(hdcPrn, PHYSICALOFFSETX);
            rect.top = GetDeviceCaps(hdcPrn, LOGPIXELSY) -
                GetDeviceCaps(hdcPrn, PHYSICALOFFSETY);
            rect.right = GetDeviceCaps(hdcPrn, PHYSICALWIDTH) -
                GetDeviceCaps(hdcPrn, LOGPIXELSX);
            rect.bottom = GetDeviceCaps(hdcPrn, PHYSICALHEIGHT) -
                GetDeviceCaps(hdcPrn, LOGPIXELSY);
            //在打印内容
            SetCursor(LoadCursor(NULL, IDC_WAIT));
            ShowCursor(TRUE);
            fSuccess = FALSE;
            iSavePointSize = 5;
            if (0 < iSavePointSize < 10);
            if ((StartDoc(hdcPrn, &di) > 0) && (StartPage(hdcPrn) > 0))
            {
                //根据所选的字体来调整高度
                iSavePointSize = lf.lfHeight;
                lf.lfHeight = -(GetDeviceCaps(hdcPrn, LOGPIXELSY)*cf.iPointSize) / 720;  //因为iPointSize以1/10磅为单位
                SelectObject(hdcPrn, CreateFontIndirect(&lf));
                lf.lfHeight = iSavePointSize;
                //设置颜色
                SetTextColor(hdcPrn, cf.rgbColors);
                //显示文本
                Justify(hdcPrn, szText, &rect, iAlign);
                if (EndPage(hdcPrn) > 0)
                {
                    fSuccess = TRUE;
                    EndDoc(hdcPrn);
                }
            }
            ShowCursor(FALSE);
            SetCursor(LoadCursor(NULL, IDC_ARROW));
            DeleteObject(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;
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        GetClientRect(hwnd, &rect);
        DrawRuler(hdc, &rect);
        //计算文本输出的区域(跳过两个标尺的客户区范围 )
        rect.left += GetDeviceCaps(hdc, LOGPIXELSX) / 2; //0.5英寸
        rect.top += GetDeviceCaps(hdc, LOGPIXELSY) / 2; //0.5英寸
        rect.right -= GetDeviceCaps(hdc, LOGPIXELSX) / 4; //0.25英寸
        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);
}
//画标尺
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);
    //设置为逻辑Twips映射模式——这是一种自定义的模式
    SetMapMode(hdc, MM_ANISOTROPIC);
    SetWindowExtEx(hdc, 1440, 1440, NULL); //逻辑单位为1/1440英寸
    SetViewportExtEx(hdc, GetDeviceCaps(hdc, LOGPIXELSX),
                     GetDeviceCaps(hdc, LOGPIXELSY), NULL);
    //将窗口原点移动左上角半英寸的地方,也就是逻辑坐标系的原点设在(0.5,0.5)英寸的地方
    SetWindowOrgEx(hdc, -720, -720, NULL);
    //计算右边距(距离最右边1/4英寸)
    ptClient.x = pRc->right;   //设备坐标
    ptClient.y = pRc->bottom;  //设备坐标
    DPtoLP(hdc, &ptClient, 1); //转为逻辑坐标
    ptClient.x -= 360;  // 右边缩进1/4英寸
    //画标尺
    MoveToEx(hdc, 0, -360, NULL); //
    LineTo(hdc, ptClient.x, -360);  //画水平标尺横线
    MoveToEx(hdc, -360, 0, NULL);
    LineTo(hdc, -360, ptClient.y);
    //水平标尺各刻度线,1/16英寸的间隔画刻度线(1英寸是1440个逻辑单位)
    for (i = 0, j = 0; i <= ptClient.x; i += 1440 / 16, j++)
    {
        MoveToEx(hdc, i, -360, NULL);
        LineTo(hdc, i, -360 - iRuleSize[j % 16]);
    }
    //垂直标尺各刻度线,1/16英寸的间隔画刻度线(1英寸是1440个逻辑单位)
    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                              //处理每一行文本
    {
        cSpaceChars = 0;            //每行空格的个数,先初始化为0
        while (*pText == ‘ ‘)       //跳过每行开始处的空格
            pText++;
        pBegin = pText;             //pBegin指向该行的第一个字母
        do
        {
            pEnd = pText;          //将pEnd指针定位在行尾(注意,最终指向行尾)
            //但每次循环,先将上次的pText的位置保存在pEnd中。
            //跳到下一个空格的地方
            while (*pText != ‘\0‘ && *pText++ != ‘ ‘); //找到下一个空格后,pText++,指向
            //该空格后的下一个位置
            if (*pText == ‘\0‘)
                break;
            //当每次遇到空格时,都要统计一下该空格前面的字符串的长度
            cSpaceChars++;
            GetTextExtentPoint32(hdc, pBegin, pText - pBegin - 1, &size);
        } while (size.cx < (pRc->right - pRc->left));
        cSpaceChars--;            //文本超出范围后,减去每行最后的空格
        while (*(pEnd - 1) == ‘ ‘)  //删除行尾那些连续的空格
        {
            pEnd--;
            cSpaceChars--;
        }
        //如果到了文本的最后或没有空格,将pEnd指针设到最末的位置
        if (*pText == ‘\0‘ || cSpaceChars <= 0)
            pEnd = pText;
        GetTextExtentPoint32(hdc, pBegin, pEnd - pBegin, &size);
        switch (iAlign)
        {
        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;
        }
        //显示文本
        TextOut(hdc, xStart, yStart, pBegin, pEnd - pBegin);
        //准备下一行的输出,并消除误差
        SetTextJustification(hdc, 0, 0);
        yStart += size.cy;
        pText = pEnd;
    } while (*pText && yStart < pRc->bottom - size.cy);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 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 ID_ALIGN_JUSTIFIED              40006
#define IDM_ALIGN_JUSTIFIED             40007
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40008
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//Justify1.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif    // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
JUSTIFY1 MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Print", IDM_FILE_PRINT
END
POPUP "&Font"
BEGIN
MENUITEM "&Font...", IDM_FONT
END
POPUP "&Align"
BEGIN
MENUITEM "&Left", IDM_ALIGN_LEFT, CHECKED
MENUITEM "&Right", IDM_ALIGN_RIGHT
MENUITEM "&Center", IDM_ALIGN_CENTER
MENUITEM "&Justified", IDM_ALIGN_JUSTIFIED
END
END
#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

(1)己知的几个问题

①不支持“连字符”的逻辑
   ②当一行中单词数量少于两个时,算法完全失效。
   ③如果有个单词比左右边界间的距离更长时,该程序无法工作。
   ④屏幕上的显示与打印机上的显示并非完全一致,即不是“所见即所得”

17.5.3 打印预览

(1)Justify2仅对TrueType字体有效。克服了上述非“所见即所得”的问题。
(2)GetOutlineTextMetrics可以获得TrueType字体原始的设计尺寸信息。
(3)本例中创建的字体,其lfHeight使用otmEMSquare,得到的宽度与设备无关。

【Justify2程序】——在Justify1的基础上修改。

/*------------------------------------------------------------
JUSTIFY2.C -- Justified Type Program #2
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#include "resource.h"
#define OUTWIDTH 6     //6英寸
#define LASTCHAR 127   //在文本中使用的最后一个字符编码
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawRuler(HDC hdc, RECT *pRc);
void Justify(HDC hdc, PTSTR pText, RECT *pRc, int iAlign);
UINT GetCharDesignWidths(HDC hdc, UINT uFirst, UINT uLast, int* piWidths);
void GetScaleWidths(HDC hdc, double* pdWidths);
double GetTextExtentFloat(double* pdWidths, PTSTR psText, int iCount);
TCHAR szAppName[] = TEXT("Justify2");
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
    HWND         hwnd;
    MSG          msg;
    WNDCLASSEX     wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.cbSize = sizeof(WNDCLASSEX);
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(hInstance, szAppName);
    wndclass.hIconSm = LoadIcon(hInstance, szAppName);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = szAppName;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClassEx(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
                   szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
                        TEXT("Justified Type #2"), // window caption
                        WS_OVERLAPPEDWINDOW,        // window style
                        CW_USEDEFAULT,              // initial x position
                        CW_USEDEFAULT,              // initial y position
                        CW_USEDEFAULT,              // initial x size
                        CW_USEDEFAULT,              // initial y size
                        NULL,                       // parent window handle
                        NULL,                       // window menu handle
                        hInstance,                  // program instance handle
                        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    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.") };
    static LOGFONT     lf;
    static CHOOSEFONT  cf;
    static int iAlign = IDM_ALIGN_LEFT;
    HDC         hdc, hdcPrn;
    static PRINTDLG    pd;
    static DOCINFO     di = { sizeof(DOCINFO), TEXT("Justify1:Printing") };
    PAINTSTRUCT ps;
    RECT        rect;
    HMENU       hMenu;
    BOOL        fSuccess;
    int         iSavePointSize;

    switch (message)
    {
    case WM_CREATE:
        hdc = GetDC(hwnd);
        lf.lfHeight = -GetDeviceCaps(hdc, LOGPIXELSY) / 6;  //1/6英寸的高度
        lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;             //只使用TrueType字体
        lstrcpy(lf.lfFaceName, TEXT("Times New Roman"));
        ReleaseDC(hwnd, hdc);
        //初始化CHOOSEFONT结构体
        memset(&cf, 0, sizeof(CHOOSEFONT));
        cf.lStructSize = sizeof(CHOOSEFONT);
        cf.hwndOwner = hwnd;
        cf.lpLogFont = &lf;
        cf.iPointSize = 120;  //初始值为12磅
        cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS | CF_EFFECTS;
        return 0;
    case WM_COMMAND:
        hMenu = GetMenu(hwnd);
        switch (LOWORD(wParam))
        {
        case IDM_FILE_PRINT:
            //获取打印机DC
            pd.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;
            }
            //设置1英寸的页边距
            rect.left = (GetDeviceCaps(hdcPrn, PHYSICALWIDTH) -
                         GetDeviceCaps(hdcPrn, LOGPIXELSX)*OUTWIDTH) / 2 -
                         GetDeviceCaps(hdcPrn, PHYSICALOFFSETX);
            rect.top = GetDeviceCaps(hdcPrn, LOGPIXELSY) -
                GetDeviceCaps(hdcPrn, PHYSICALOFFSETY);
            rect.right = rect.left + GetDeviceCaps(hdcPrn, LOGPIXELSX)*OUTWIDTH;
            rect.bottom = GetDeviceCaps(hdcPrn, PHYSICALHEIGHT) -
                GetDeviceCaps(hdcPrn, LOGPIXELSY) -
                GetDeviceCaps(hdcPrn, PHYSICALOFFSETY);
            //在打印内容
            SetCursor(LoadCursor(NULL, IDC_WAIT));
            ShowCursor(TRUE);
            fSuccess = FALSE;
            iSavePointSize = 5;
            if (0 < iSavePointSize < 10);
            if ((StartDoc(hdcPrn, &di)>0) && (StartPage(hdcPrn)>0))
            {
                //根据所选的字体来调整高度
                iSavePointSize = lf.lfHeight;
                lf.lfHeight = -(GetDeviceCaps(hdcPrn, LOGPIXELSY)*cf.iPointSize) / 720;  //因为iPointSize以1/10磅为单位
                SelectObject(hdcPrn, CreateFontIndirect(&lf));
                lf.lfHeight = iSavePointSize;
                //设置颜色
                SetTextColor(hdcPrn, cf.rgbColors);
                //显示文本
                Justify(hdcPrn, szText, &rect, iAlign);
                if (EndPage(hdcPrn)>0)
                {
                    fSuccess = TRUE;
                    EndDoc(hdcPrn);
                }
            }
            ShowCursor(FALSE);
            SetCursor(LoadCursor(NULL, IDC_ARROW));
            DeleteObject(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;
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        GetClientRect(hwnd, &rect);
        DrawRuler(hdc, &rect);
        //计算文本输出的区域(跳过两个标尺的客户区范围 )
        rect.left += GetDeviceCaps(hdc, LOGPIXELSX) / 2; //0.5英寸
        rect.top += GetDeviceCaps(hdc, LOGPIXELSY) / 2; //0.5英寸
        rect.right = rect.left + OUTWIDTH*GetDeviceCaps(hdc, LOGPIXELSX); //3英寸
        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);
}
//画标尺
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);
    //设置为逻辑Twips映射模式——这是一种自定义的模式
    SetMapMode(hdc, MM_ANISOTROPIC);
    SetWindowExtEx(hdc, 1440, 1440, NULL); //逻辑单位为1/1440英寸
    SetViewportExtEx(hdc, GetDeviceCaps(hdc, LOGPIXELSX),
                     GetDeviceCaps(hdc, LOGPIXELSY), NULL);
    //将窗口原点移动左上角半英寸的地方,也就是逻辑坐标系的原点设在(0.5,0.5)英寸的地方
    SetWindowOrgEx(hdc, -720, -720, NULL);
    //计算右边距(距离最右边1/4英寸)
    ptClient.x = pRc->right;   //设备坐标
    ptClient.y = pRc->bottom;  //设备坐标
    DPtoLP(hdc, &ptClient, 1); //转为逻辑坐标
    ptClient.x -= 360;  // 右边缩进1/4英寸
    //画标尺
    MoveToEx(hdc, 0, -360, NULL); //
    LineTo(hdc, OUTWIDTH * 1440, -360);  //画水平标尺横线
    MoveToEx(hdc, -360, 0, NULL);
    LineTo(hdc, -360, ptClient.y);
    //水平标尺各刻度线,1/16英寸的间隔画刻度线(1英寸是1440个逻辑单位)
    for (i = 0, j = 0; i <= ptClient.x && i <= OUTWIDTH * 1440; i += 1440 / 16, j++)
    {
        MoveToEx(hdc, i, -360, NULL);
        LineTo(hdc, i, -360 - iRuleSize[j % 16]);
    }
    //垂直标尺各刻度线,1/16英寸的间隔画刻度线(1英寸是1440个逻辑单位)
    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);
}
/*-------------------------------------------------------------------------------------------------
Justify:基于屏幕或打印机设计
_________________________________________________________________________________________________*/
void Justify(HDC hdc, PTSTR pText, RECT *pRc, int iAlign)
{
    int xStart, yStart, cSpaceChars;
    PTSTR pBegin, pEnd;
    SIZE  size;
    double dWidth, adWidths[LASTCHAR + 1];
    //利用字符的浮点宽度填充adWidths数组
    GetScaleWidths(hdc, adWidths);
    yStart = pRc->top;
    do                              //处理每一行文本
    {
        cSpaceChars = 0;            //每行空格的个数,先初始化为0
        while (*pText == ‘ ‘)       //跳过每行开始处的空格
            pText++;
        pBegin = pText;             //pBegin指向该行的第一个字母
        do
        {
            pEnd = pText;          //将pEnd指针定位在行尾(注意,最终指向行尾)
            //但每次循环,先将上次的pText的位置保存在pEnd中。
            //跳到下一个空格的地方
            while (*pText != ‘\0‘ && *pText++ != ‘ ‘); //找到下一个空格后,pText++,指向
            //该空格后的下一个位置
            if (*pText == ‘\0‘)
                break;
            //当每次遇到空格时,都要统计一下该空格前面的字符串的长度
            cSpaceChars++;
            dWidth = GetTextExtentFloat(adWidths, pBegin, pText - pBegin - 1);
        } while (dWidth<(double)(pRc->right - pRc->left));
        cSpaceChars--;            //文本超出范围后,减去每行最后的空格
        while (*(pEnd - 1) == ‘ ‘)  //删除行尾那些连续的空格
        {
            pEnd--;
            cSpaceChars--;
        }
        //如果到了文本的最后或没有空格,将pEnd指针设到最末的位置
        if (*pText == ‘\0‘ || cSpaceChars <= 0)
            pEnd = pText;
        //获得整型的长度,因为SetTextJustification要使用这样的长度。
        GetTextExtentPoint32(hdc, pBegin, pEnd - pBegin, &size);
        switch (iAlign)
        {
        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;
        }
        //显示文本
        TextOut(hdc, xStart, yStart, pBegin, pEnd - pBegin);
        //准备下一行的输出,并消除误差
        SetTextJustification(hdc, 0, 0);
        yStart += size.cy;
        pText = pEnd;
    } while (*pText && yStart < pRc->bottom - size.cy);
}
/*-------------------------------------------------------------------------------------------------
GetCharDesignWidths:获得指定字体的字符,其原始的设计尺寸
_________________________________________________________________________________________________*/
UINT GetCharDesignWidths(HDC hdc, UINT uFirst, UINT uLast, int* piWidths)
{
    HFONT hFont, hFontDesign;
    LOGFONT lf;
    OUTLINETEXTMETRIC otm;  //
    hFont = GetCurrentObject(hdc, OBJ_FONT); //获得当前DC中的字体
    GetObject(hFont, sizeof(LOGFONT), &lf);
    //获得Outline文本的尺寸信息
    //设计TrueType字体时,使用了“EM正方形”网格,字符宽度不同,但网格大小是一样的。
    otm.otmSize = sizeof(OUTLINETEXTMETRIC);
    GetOutlineTextMetrics(hdc, sizeof(OUTLINETEXTMETRIC), &otm);
    //创建基于设计尺寸的字体
    lf.lfHeight = -(int)otm.otmEMSquare;
    lf.lfWidth = 0;
    hFontDesign = CreateFontIndirect(&lf);
    //选入设备环境,并获得字体的宽度
    SaveDC(hdc);
    SetMapMode(hdc, MM_TEXT);
    SelectObject(hdc, hFontDesign);
    GetCharWidth(hdc, uFirst, uLast, piWidths);  //GetCharWidth是系统API
    //本例中获得所有ASCII码字符的设计宽度。
    SelectObject(hdc, hFont);
    RestoreDC(hdc, -1);
    //清除
    DeleteObject(hFontDesign);
    return otm.otmEMSquare;
}
/*-------------------------------------------------------------------------------------------------
GetScalWidths:获得指定字体经缩放后的尺寸
_________________________________________________________________________________________________*/
void GetScaleWidths(HDC hdc, double* pdWidths)
{
    double dScale;
    HFONT hFont;
    int  aiDesignWidths[LASTCHAR + 1]; //所有ASCII码,共128个
    int i;
    LOGFONT lf;
    UINT uEMSquare;
    //调用以上的函数
    uEMSquare = GetCharDesignWidths(hdc, 0, LASTCHAR, aiDesignWidths);
    //获得当前DC中的字体
    hFont = GetCurrentObject(hdc, OBJ_FONT);
    GetObject(hFont, sizeof(LOGFONT), &lf);
    //缩放各宽度,并存储成浮点数数据
    dScale = (double)-lf.lfHeight / (double)uEMSquare;
    for (i = 0; i <= LASTCHAR; i++)
    {
        pdWidths[i] = dScale*aiDesignWidths[i];
    }
}
/*-------------------------------------------------------------------------------------------------
GetTextExtentFloat:计算文本字符串的浮点宽度。
功能与GetTextExtentPoint32类似
_________________________________________________________________________________________________*/
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;
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 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 ID_ALIGN_JUSTIFIED              40006
#define IDM_ALIGN_JUSTIFIED             40007
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40008
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//Justify2.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif    // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
JUSTIFY2 MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Print", IDM_FILE_PRINT
END
POPUP "&Font"
BEGIN
MENUITEM "&Font...", IDM_FONT
END
POPUP "&Align"
BEGIN
MENUITEM "&Left", IDM_ALIGN_LEFT, CHECKED
MENUITEM "&Right", IDM_ALIGN_RIGHT
MENUITEM "&Center", IDM_ALIGN_CENTER
MENUITEM "&Justified", IDM_ALIGN_JUSTIFIED
END
END
#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
时间: 2024-10-28 06:31:27

第17章 文本和字体_17.5 设置段落格式的相关文章

第17章 文本和字体_17.1-17.2 简单文本输出、 字体的背景知识

17.1 简单文本输出 17.1.1 文本输出函数 (1)TextOut(hdc,xStart,yStart,pString,iCount) ①xStart和yStart使用的是逻辑坐标,TextOut并不以NULL来做字符串的结束,需指定字符的个数iCount的值 ②SetTextAlign会改变xStart和yStart的含义 SetTextAlign 坐标值的含义 TA_LEFT xStart:第一个字符的左侧坐标 TA_RIGHT xStart:最后一个字符的右侧坐标 TA_CENTER

第17章 文本和字体_17.3 逻辑字体

17.3 逻辑字体 17.3.1 逻辑字体的创建和选择 (1)创建逻辑字体 hFont= CreateFontIndirect(&lf); //其中LOGFONT为结构体 (2)SelectObject(hdc,hFont); //选入并匹配字体或使用从ChooseFont中选择的字体 (3)返回选入设备环境的字体信息: ①GetTextFace(hdc,sizeof(szFaceName)/sizeof(TCHAR),szFaceName);//返回字样名称 ②GetTextMetrics(h

第17章 文本和字体_17.6 一些有趣和新奇的内容

17.6 一些有趣和新奇的内容 17.6.1 GDI路径 (1)路径的创建 BeginPath(hdc); //1.使用任何绘制线的函数在DC上绘制,被存在GDI内部,但不显示出来. //2.可以在当前路径中创建一个新的子路径,其中每个子路径都是一系列互相连接的线. //3.每个子路径可以是闭合的,也可以是开放的.要闭合子路径时,可以用CloseFigure将子路径闭合,必要时会自动添加一条直线以达到些目的.其后用任何画线函数创建的都是新的子路径. EndPath(hdc); (2)五个与路径有

第17章 文本和字体_17.4 字体枚举

17.4 字体枚举 17.4.1 枚举函数 (1)EnumFontFamiliesEx函数 参数 含义 HDC hdc handle to DC LPLOGFONT lpLogfont 传入LOGFONT结构的指针 注意:如果lfCharset=DEFAULT_CHARSET: lf.lfFaceName[0]=NULL,则枚举所有字体 FONTENUMPROC lpEnumFontFamExProc 枚举回调函数 LPARAM lParam 可以指定附加数据,会传到枚举的回调函数中 DWORD

CSS3秘笈第三版涵盖HTML5学习笔记13~17章

第13章,构建基于浮动的布局 使用的是float(浮动)属性 注:float:none值将取消所有浮动,通常只用来取消元素中已经应用的浮动. 切记:不需要给正文的div设计宽度,即使设计成固定宽度也不用 用浮动进行布局 LayoutGala网站(http://blog.html.it/layoutgala/)上提供了40种不同的CSS设计,但大多只是基本框架,里面只有<div>标签及其定位用的CSS代码 布局生成器,Cridinator(http://gridinator.com)提供了简单的

JavaScript高级程序设计(第三版)学习笔记11、12、17章

第11章, DOM扩展 选择符 API Selector API Level1核心方法querySelector .querySelectorAll,兼容的浏览器可以使用 Document,Element 实例调用它们,支持浏览器:IE8+,Firefox3.5+,Safari3.1+,chrome,Opera10+ querySelector方法 接收一个 CSS选择符,返回与该模式匹配的第一个元素 通过 Document类型调用该函数,会在文档范围查找匹配元素,通过 Element类型调用该

第15章 文本样式(下)

第 15章  CSS文本样式[下] 学习要点: 1.文本总汇 2.文本样式 3.文本控制 本章主要探讨 HTML5中  CSS文本样式,通过文本样式的设置,更改字体的大小.样式以及文本的方位. 一.文本总汇 我们重点了解一下 CSS文本样式中文本内容的一些设置方法,样式表如下 属性名 说明 CSS版本text-decoration 装饰文本出现各种划线. 1text-transform 将英文文本转换大小写. 1text-shadow 给文本添加阴影 3text-align 设置文本对齐方式 1

第十五章 文本样式(上)

第 15章 CSS文本样式[上]学习要点:1.字体总汇2.字体设置3.Web字体 本章主要探讨 HTML5中 CSS文本样式,通过文本样式的设置,更改字体的大小.样式以及文本的方位.一.字体总汇本节课,我们重点了解一下 CSS文本样式中字体的一些设置方法,样式表如下:属性名 说明font-size 设置字体的大小font-variant 设置英文字体是否转换为小型大写font-style 设置字体是否倾斜font-weight 设置字体是否加粗font-family 设置 font字体font

Linux 第一章文本安装 red hat

享受生活  热爱挑战 明远分享 Linux 第一章文本安装 red hat 每章一段话 有些人似荷,只能远观:有些人似茶,可以细细品味:有些人似风,不必在意:有些人是树,值得依靠.人生就像是一场修行,修的就是一颗心.心顺,一切就会完美:心静,出境就会美好:心乐,人生就幸福了.多点踏实,少点浮躁,活得真实才能自在. 目标:   学会用文本方式安装red hat (建议先练习图形化安装,对比着学习文本安装) 理论部分: 一丶了解linux的系统内核 对于操作系统来说,内核就好像是人的"心脏"