Persian Type Tutor را که خاطرتان هست، قرار بود که در مورد بخشهای مختلف آن توضیحاتی را ارائه کنم.
یکی از مواردی که در این نرمافزار استفاده شده، نمایش متنی است که کاربر قراراست آن را تایپ کند. به این صورت که این متن به رنگ مشکی در صفحه نمایش داده میشود و با تایپ کردن هر حرف از متن آن حرف به رنگ زرد رنگ در خواهد آمد و همچنین یک نشانگر به صورت چشمکزن در زیر حرف بعدی که قراراست تایپ شود نمایش داده میشود.
مسئلهای که وجود دارد نمایش حروف یک کلمه فارسی به صورت رنگی میباشد.
حتماً میپرسید چه مسألهای؟
در ادامه یک توضیح در مورد حروف فارسی:
همانطور که میدانید بعضی از حروف فارسی با توجه به موقعیت آن در کلمه به شکلهای مختلف نمایش داده میشود. به عنوان مثال حرف «س» را در کلمات زیر در نظر بگیرید:
این حرف با توجه به اینکه در اول، وسط و یا آخر کلمه باشد شکلهای مختلفی به خود گرفته است.
حال قطعه کد زیر را در نظر بگیرید:
const
Str = 'تست';
var
I: Integer;
XPos: Integer;
begin
XPos := 400;
for I := 0 to Length(str) - 1 do
begin
TextOut(Canvas.Handle, XPos, 10, @Str[I+1], 1);
XPos := XPos - Canvas.TextWidth(Str[I+1]);
end;
end;
میخواهیم کلمهی «تست» را بصورت حرف به حرف در صفحه ترسیم کنیم. خروجی ای قطعه کد چیزی شبیه به این خواهد بود:
همانطور که میبینید حروف تماماً بصورت غیر متصل در صفحه ترسیم شدهاند. داستان از چه قرار است؟
وقتی که شما توسط یک دستور ترسیم، رشتهای را در صفحه ترسیم میکنید سیستم عامل در پس پرده با توجه به فونت انتخابی صفحه ترسیم (Canvas) و با توجه به موقعیت هر حرف در کلمه شکل مخصوص به آن حرف (گلایف) را از جدول فونت مربوطه استخراج کرده و در صفحه ترسیم مینماید.
من چطور این ترسیم را انجام دهم؟
کل کار با استفاده از دو تابع API ویندوز انجام میشود:
GetCharacterPlacementW:
جهت استخراج خصوصیات کاراکترهای رشته.
ExtTextOutW: جهت ترسیم رشته در صفحه.
و اما نحوه استفاده:
ابتدا رکورد زیر را تعریف مینماییم:
type
PIntArray = array of Integer;
PwcArray = array of widechar;
WideCharInfo = Record
lpDx: PIntArray;
lpGlyphs: PwcArray;
lpOrder: PIntArray;
End;
lpDx: آرایهایست که در آن عرض گلایف مربوط به کاراکترهای رشته قرار میگیرد که از آن برای تعیین موقعیت ترسیم هر گلایف در صفحه (و یا نمایش نشانگر چشمکزن زیر کاراکترها) استفاده خواهیم کرد.
lpGlyphs: در این آرایه کد یونیکد هر گلایف قرار داده میشود.
lpOrder: در این آرایه هم شماره ترتیب هر گلایف در رشته قرار داده میشود. (این حالت برای زبانهای راست به چپ استفاده دارد)
سپس توسط تابع زیر اطلاعات مربوط به رشتهای را که قصد ترسیم آن را داریم را استخراج مینمائیم:
function GetCharacterPlace(DC: HDC;
sText: WideString): WideCharInfo;
var
GCP : TGCPResults;
TextLength: Integer;
begin
TextLength := Length(sText);
SetLength(Result.lpOrder, TextLength);
SetLength(Result.lpDx, TextLength);
SetLength(Result.lpGlyphs, TextLength);
GCP.lStructSize := sizeof(TGCPResults);
GCP.lpOutString := nil;
GCP.lpOrder := Pointer(Result.lpOrder);
GCP.lpDx := Pointer(Result.lpDx);
GCP.lpGlyphs := Pointer(Result.lpGlyphs);
GCP.lpCaretPos := nil;
GCP.lpClass := nil;
GCP.nGlyphs := TextLength;
GCP.nMaxFit := 0;
GetCharacterPlacementW(DC, Pointer(sText), TextLength, 0,
GCP, GetFontLanguageInfo(DC))
end;
و در آخر نحوه ترسیم رشته در صفحه با استفاده از تابع بالا:
procedure DrawPersianText(Canvas: TCanvas; Str: WideString);
var
aRect : TRect;
CharInfo : WideCharInfo;
Options : LongInt;
wChar : widechar;
I : Integer;
xPos : Integer;
begin
aRect := Canvas.ClipRect;
CharInfo := GetCharacterPlace(Canvas.Handle, Str);
xPos := aRect.Right - 100;
Options := ETO_GLYPH_INDEX;
I := 0;
while I < Length(Str) do
begin
if (I mod 2) = 0 then
Canvas.Font.Color := clBlack
else
Canvas.Font.Color := clRed;
wChar := CharInfo.lpGlyphs[CharInfo.lpOrder[I]];
xPos := xPos - CharInfo.lpDx[CharInfo.lpOrder[I]];
ExtTextOutW(Canvas.Handle, xPos, 10, Options, @aRect, @wChar, 1, nil);
Inc(I);
end;
end;
procedure TfrmMain.FormPaint(Sender: TObject);
Const
Str = 'تست';
begin
DrawPersianText(Canvas, Str);
end;
که خروجی آن به صورت زیر خواهد بود:
دریافت سورس کد مثال:
برای مشاهده مثال کامل به سورس کد
Persian Type Tutor مراجعه فرمائید.
همچنین اطلاعات بیشتر در مورد تابع
GetCharacterPlacementW