XỬ LÝ VĂN BẢN TRONG LẬP TRÌNH C TRÊN WINDOWS
Xử lý văn bản là công việc phổ biến nhất trong các thao tác đồ họa. Chúng được sử dụng theo các định dạng và cách thức khác nhau trong các ứng dụng xử lý tài liệu, bảng biểu, cơ sở dữ liệu và hỗ trợ thiết kế bằng máy tính (CAD - Computer Aided Design).
Tập hợp các hàm Win32® API xử lý văn bản được phân thành hai nhóm chính: Nhóm các hàm định dạng– chuẩn bị cho thao tác xuất dữ liệu, và nhóm các hàm thực hiện thao tác hiển thị. Chúng ta sẽ bắt đầu với việc tìm hiểu các hàm hiển thị.
Hiển thị văn bản
Để hiện thị nội dung văn bản trên các thiết bị xuất, dựa vào từng trường hợp thể hiện khác nhau, ta dùng các hàm Win32 API khác nhau. Các hàm này phụ thuộc vào font chữ, thuộc tính của thiết bị ngữ cảnh DC (Device Context ) và khoảng cách ký tự thể hiện.
Hàm phổ biến nhất thực hiện thao tác xuất một chuỗi ký tự văn bản, sử dụng font chữ, màu chữ và màu nền hiện hành là :
BOOL TextOut(HDC hDC, int nXStart, int nYStart, LPCTSTR lpString, int cbString);
Hàm này thực hiện thao tác xuất chuỗi ký tự xác định bởi con trỏ lpString ra DC, với chiều dài được xác định bởi cbString (không phụ thuộc vào ký tự NULL đánh dấu kết thúc chuỗi).
Hai trường nXStart và nYStart là vị trí gốc của chuỗi hiển thị, xác định theo tọa độ logic của vùng làm việc cửa sổ, và thường là điểm gốc trên bên trái của vùng hiển thị chuỗi. Chúng ta sẽ bàn kỹ hơn khi tìm hiểu về canh lề văn bản trong phần 5.2.2.
Nếu thao tác xuất chuỗi thực hiện thành công, hàm trả về giá trị khác 0. Ngược lại, giá trị trả về bằng 0.
Khi cần trình bày văn bản theo tuần tự từng cột, ta dùng hàm TabbedTextOut sau :
LONG TabbedTextOut(HDC hDC, int nX, int nY, LPCTSTR lpString, int nCount, int nNumTabs, LPINT lpnTabStopPositions, intnTabOrigin);
Nếu trong chuỗi ký tự có các ký tự tab (‘\t’ hoặc 0x09), hàm TabbedTextOut sẽ chuyển các ký tự tab vào dãy các vị trí "dừng" tương ứng. Số lượng các tab dừng được xác định bởi nNumTabs, và lpnTabStopPositions là dãy vị trí các tab dừng theo đơn vị tính pixels. Ví dụ, nếu độ rộng trung bình của mỗi ký tự là 8 pixels, và mỗi tab dừng cần đặt cách nhau 5 ký tự, dãy các tab dừng sẽ phải lần lượt có giá trị 40, 80, 120, … . Tuy nhiên, các giá trị này không nhất thiết phải là bội số của nhau.
Nếu biến nNumTabs hoặc lpnTabStopPositions có giá trị là 0 và NULL, các tab dừng được đặt cách nhau từng 8 ký tự. Nếu nNumTabs bằng 1, lpnTabStopPositions trỏ đến giá trị xác định một dãy tăng tuần hoàn là bội số của dãy này. Ví dụ, nếu nNumTabs bằng 1, và lpnTabStopPositions bằng 30, ta sẽ có dãy tab dừng tại vị trí 30, 60, 90, … pixels.
Trường nTabOrigin xác định tọa độ theo trục x của điểm bắt đầu tính khoảng cách tới các tab. Giá trị này không nhất thiết phải là vị trí đầu tiên của chuỗi, có thể chọn trùng hoặc không.
Hàm trả về kích thước chuỗi hiển thị, theo đơn vị logic, nếu thành công. Ngược lại, hàm trả về 0. Trong đó, chiều cao chuỗi là WORDcao của biến kiểu LONG, chiều rộng là WORD thấp.
Một hàm xuất văn bản khác tương tự hàm TextOut là hàm ExtTextOut :
BOOL ExtTextOut(HDC hDC, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCTSTR lpString, UINT cbCount, CONST INT*lpDx);
Biến lprc là một con trỏ đến cấu trúc RECT, trong đó nội dung văn bản hiển thị sẽ bị cắt vào vùng hình chữ nhật tương ứng nếu fuOptions được thiết lập là ETO_CLIPPED, hoặc là vùng nền hình chữ nhật sẽ được tô bởi màu nền nếu fuOptions là ETO_OPAQUE.
Trường lpDx là một dãy số nguyên xác định khoảng cách giữa các ký tự liên tiếp trong chuỗi. Nó cho phép một chương trình tạo khoảng cách rộng hoặc hẹp giữa các ký tự, điều mà đôi lúc cần thiết trong việc điều chỉnh các từ trong văn bản theo độ rộng cột. Giá trị lpDx có thể là NULL tương ứng với chế độ mặc định cho khoảng cách này.
Tương tự hàm TextOut, hàm ExtTextOut trả về giá trị khác 0 nếu thành công. Ngược lại, giá trị trả về bằng 0.
Một hàm mức ở mức cao hơn để xuất văn bản là hàm DrawText :
int DrawText(HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat);
Cũng như các hàm xuất văn bản khác, hàm DrawText xuất chuỗi xác định bởi con trỏ lpString có độ dài nCount. Tuy nhiên, với chuỗi có ký tự kết thúc là NULL, nếu nCount bằng -1, hàm sẽ tự động tính toán chiều dài của chuỗi.
Biến lpRect trỏ đến cấu trúc RECT của hình chữ nhật (theo toạ độ logic) mà trong đó văn bản thể hiện theo định dạng được thiết lập trong uFormat.
Nếu uFormat bằng 0, nội dung văn bản sẽ được hiển thị theo từng dòng từ trên xuống dưới. Mỗi dòng mới được xác định thông qua ký tự về đầu dòng CR (carriage return, bằng ‘\r’ hoặc 0x0D) hoặc ký tự xuống dòng LF (linefeed, bằng ‘\n’ hoặc 0x0A) có trong văn bản. Phần văn bản bên ngoài hình chữ nhật lpRect sẽ bị cắt bỏ.
Giá trị uFormat bằng 0 cũng chính là giá trị cờ canh lề trái (DT_LEFT). Ngoài ra, ta có thể thiết lập các cờ canh lề phải (DT_RIGHT), và canh lề giữa (DT_CENTER) cho văn bản.
Để loại bỏ chức năng điều khiển của các ký tự CR và LF, cần thêm vào cờ DT_SINGLELINE. Nếu thiết lập DT_SINGLELINE, ta cũng có thể chỉ định vị trí của dòng hiển thị ở phía trên (DT_TOP), phía dưới (DT_BOTTOM), hoặc ở chính giữa (DT_VCENTER) trong vùng hình chữ nhật.
Trong trường hợp hiển thị nhiều dòng văn bản, Windows chỉ ngắt dòng khi gặp ký tự CR và LF. Để ngắt dòng dài hơn kích thước hình chữ nhật hiển thị, cần thiết lập cờ DT_WORDBREAK. Nếu không muốn Windows cắt bỏ các phần dư ra khi vẽ chữ vượt quá phạm vi khung chữ nhật, ta thêm cờ DT_NOCLIP. Nếu muốn ký tự tab (‘\t’ hoặc 0x09) được diễn dịch thành ký tự phân cột, cần thêm cờ DT_EXPANDTABS. Giá trị mặc định của tab là 8 khoảng trắng. Cờ DT_TABSTOP được dùng để đặt lại giá trị tab. Trong trường hợp này, byte cao của word thấp (bits 15-8) của uFormat sẽ chứa giá trị tab cần thay thế.
Định dạng văn bản
Dựa vào đặc trưng các thành phần hiển thị, các hàm định dạng văn bản phân làm ba nhóm liên quan đến thuộc tính của DC, độ rộng ký tự và kích thước chuỗi ký tự hiển thị.
Việc thiết lập thuộc tính định dạng văn bản cho DC được thực hiện thông qua các hàm canh lề văn bản, thiết lập khoảng cách ký tự, xác định màu nền và màu văn bản. Cùng với các hàm này, Windows cũng cung cấp các hàm cho biết thuộc tính hiện hành tương ứng cho DC.
Trong các hàm về thuộc tính DC, biến đầu tiên luôn là handle của DC hiện hành. Xét hàm thiết lập màu chữ và màu nền :
COLORREF SetTextColor(HDC hDC, COLOREF crColor);
COLORREF SetBkColor(HDC hDC, COLORREF crColor);
Biến crColor xác định màu cần thiết lập. Nếu thành công, hàm trả về màu chữ (màu nền) trước khi được thiết lập. Nếu không, hàm trả về giá trị cờ CLR_INVALID. Ngoài ra, để xác định màu chữ và màu nền hiện hành, ta dùng hai hàm sau :
COLORREF GetTextColor(HDC hDC);
COLORREF GetBkColor(HDC hDC);
Nếu hàm thực hiện thành công, ta xác định được màu hiện hành. Nếu không, giá trị trả về là CLR_INVALID.
Khi vẽ chữ, Windows sử dụng hai chế độ : chế độ trong suốt (TRANSPARENT) và chế độ mờ (OPAQUE). Ở chế độ trong suốt, màu nền sẽ không được sử dụng đến, chữ vẽ ra đè lên nền hiện hành. Ở chế độ mờ, trước khi vẽ chữ, nền sẽ được xoá đi với màu nền được thiết lập bởi hàm SetBkColor:
int SetBkColor(HDC hDC, int iBkMode);
Với iBkMode là chế độ nền TRANSPARENT hoặc OPAQUE (chế độ mặc định của Windows là OPAQUE). Nếu thành công, hàm trả về chế độ nền trước khi được thiết lập. Ngược lại, giá trị trả về là zero. Để biết chế độ nền hiện tại, ta dùng hàm :
int GetBkMode(HDC hDC);
Hàm trả về giá trị TRANSPARENT hoặc OPAQUE, nếu thành công. Ngược lại, giá trị trả về là zero.
Để xác lập vị trí chuỗi văn bản hiển thị dựa trên điểm gốc nXStart, nYStart (xem phần 5.2.1) ta dùng hàm SetTextAlign :
UINT SetTextAlign(HDC hDC, UINT fMode);
Khi đó, điểm gốc nXStart ở cạnh bên trái khung chữ nhật nếu fMode là TA_LEFT. Ký tự đầu chuỗi sẽ hiển thị từ điểm gốc này. Đây cũng là giá trị mặc định của Windows. Nếu fMode bằng TA_RIGHT, vị trí chuỗi được tính từ bên phải, tức ký tự cuối chuỗi hiển thị tại điểm gốc, và ngược lại cho đến ký tự đầu tiên. Nếu fMode bằng TA_CENTER, vị trí giữa chuỗi chính là điểm gốc.
Tương tự, để thiết lập vị trí hiển thị chuỗi theo phương đứng, các cờ TA_TOP, TA_BOTTOM, và TA_BASELINE được dùng tương ứng điểm gốc nYStart ở trên, giữa và dưới dòng văn bản hiển thị. Đối với Windows thì giá trị mặc định theo phương đứng là TA_TOP.
Nếu gọi hàm SetTextAlign với cờ TA_UPDATE, Windows sẽ không sử dụng điểm gốc nXStart, nYStart trong hàm xuất văn bảnTextOut, thay vào đó là vị trí được thiết lập trước đó bởi hàm MoveToEx hoặc LineTo, hoặc một hàm thay đổi vị trí khác. Cờ TA_UPDATE cũng cập nhật điểm gốc về đầu chuỗi (nếu dùng TA_LEFT) và về cuối chuỗi (nếu dùng TA_RIGHT) cho lần gọi kế tiếp. Điều này cần thiết cho việc hiển thị nhiều dòng văn bản với hàm TextOut. Nếu cờ TA_CENTER được thiết lập, vị trí của nXStart vẫn như cũ sau khi hàm TextOut được gọi.
Để biết chế độ canh lề văn bản hiện tại, ta dùng hàm :
UINT GetTextAlign(HDC hDC);
Nếu thành công, hàm trả về cờ tương ứng của canh lề văn bản hiện hành. Ngược lại, giá trị trả về là GDI_ERROR.
Ví dụ sau đây trình bày cách thức xác định các dạng canh lề theo phương ngang:
switch ( (TA_LEFT | TA_RIGHT | TA_CENTER) & GetTextAlign(hDC) )
{
case TA_LEFT:
case TA_RIGHT:
case TA_CENTER:
}
Ví dụ tiếp theo sử dụng hàm SetTextAlign để cập nhật vị trí hiển thị hiện thời khi hàm TextOut được gọi. Trong ví dụ này, biến cArial là một số nguyên cho biết số font Arial.
UINT uAlignPrev;
char szCount[8];
uAlignPrev = SetTextAlign(hdc, TA_UPDATECP);
MoveToEx(hdc, 10, 50, (LPPOINT) NULL);
TextOut(hdc, 0, 0, "Number of Arial fonts: ", 23);
itoa(cArial, szCount, 10);
TextOut(hdc, 0, 0, (LPSTR) szCount, strlen(szCount));
SetTextAlign(hdc, uAlignPrev);
Một thuộc tính khác của DC ảnh hưởng đến cách vẽ chuỗi là khoảng cách giữa các ký tự trong chuỗi hiển thị. Khoảng cách mặc định của Windows là 0, khi đó các ký tự được hiển thị liên tiếp nhau. Để thay đổi khoảng cách giữa các ký tự, ta dùng hàm :
int SetTextCharacterExtra(HDC hDC, int nCharExtra);
Trong đó, nCharExtra là khoảng cách theo đơn vị logic thiết lập giữa các ký tự. Nếu thành công, hàm trả về khoảng cách trước khi được thiết lập. Ngược lại, giá trị trả về là 0x80000000. Để biết khoảng cách hiện tại, ta dùng hàm :
int GetTextCharacterExtra(HDC hDC);
Nếu thành công, giá trị trả về cho biết khoảng cách hiện tại. Ngược lại, giá trị trả về là 0x80000000.
Ngoài ra, Windows còn hỗ trợ các hàm cho biết độ rộng ký tự và kích thước chuỗi hiển thị. Đây là các hàm cấp cao, sử dụng trong việc trình bày văn bản với các kiểu font khá phức tạp. Trong chương này, chúng ta chỉ đề cập đến một số hàm như GetTextMetrics(phần 5.3.2) và GetTextExtentPoint32 (phần 5.3.5).
0 Comment:
Đăng nhận xét
Thank you for your comments!