PDA

View Full Version : مقاله: طريقه نصب فونت و اطلاعاتي در مورد فونت هاي نصب شده روي سيستم



SMRAH1
دوشنبه 20 آبان 1387, 19:21 عصر
قطعا تاکنون در برنامه هايتان،از فونت خاصي استفاده کرده ايد که از نصب بودن آن روي سيسم هدف،اطمينان نداشته ايد.چه راهکاري را در پيش گرفته ايد؟آيا با ساختن يک Installer اقدام به نصب فونت کرده ايد!آيا از کاربر در خواسته کرده ايد که فونت مورد نياز را نصب کند! يا ...

در اين نوشتار به دنبال آن هستيم که اطلاعاتي راجع به موارد زير بدست آوريم:
1) آيا فونت هاي مورد نظر در سيستم موجود است؟
2) چگونه از فونت هاي سفارشي خود استفاده کنيم؟


آيا فونت هاي مورد نظر در سيستم موجود است؟
همانگونه که اطلاع داريد،به محض بارگذاري ويندوز،سيستم عامل فهرستي از فونت هايي که آماده به کار هستند در اختيار کاربر قرار مي دهد.براي تشخيص وجود يا عدم وجود فونت مورد نظر ما (که نام آن را مي دانيم)،يکي از دو راه کار زير توصيه مي شود:

1) در System.Drawing.Text کلاس InstalledFontCollection مي تواند فهرستي از فونت هاي موجود روي سيستم (البته فقط فونت هاي TrueType) بر گرداند.براي استفاده از اين ساختار ،ابتدا يک نمونه از اين کلاس ايجاد کرده و سيس خصوصيت Families آن که يک آرايه از FontFamily ها را ارائه مي کند،مورد استفاده قرار دهيد.هر عضو اين آرايه (FontFamily) داراي خصوصياتي براي دريافت اطلاعات فونت از جمله خصوصيت Name که نام فونت را بر مي گرداند، مي باشد.در نتيجه با متدي شبيه متد زير مي توان از وجود فونت مورد نظر خود (البته با داشتن نام آن) اطمينان حاصل کرد.

public bool FontIsAvailable(string strFontName)
{
InstalledFontCollection AllFonts = new InstalledFontCollection();
foreach (FontFamily ff in AllFonts.Families)
if (ff.Name == strFontName)
return true;
return false;
}2) راه ديگر تست وجود فونت مورد نظر (تکرار مي شود که فقط فونت هاي TrueType)،ساختن يک نمونه از آن نوع فونت است.يعني خط زير

Font f = new Font("Name", Size);که در آن Name ،نام فونت بوده (مثلا Tahama) و size نيز اندازه مورد نظر به point (يک نوع واحد اندازه گيري) مي باشد.براي تست، بهتر است مقدار يک داده شود، چون فقط مي خواهيم تست وجود انجام دهيم و اندازه آن مهم نيست.البته به size نبايد مقدار صفر يا منفي اختصاص داد چون باعث ايجاد يک استثناي ArgumentException مي شود.با رعايت اين نکات،يکي از سه اتفاق زير رخ خواهد داد:
الف) يک استثنا رخ مي دهد (در اين رابطه مطلبي نديده ام و فقط تجربه است) : اين در حالتي است که فونت مورد نظر موجود است ولي نمي تواند يک نمونه از آن بسازد! مثلا فونت Aharoni باعث ايجاد يک چنين استثنايي مي شود.اين استثنا از آنجا ناشي مي شود که اين فونت داراي Regular style نيست و چون ساخت يک فونت توسط خط بالا،در نظر دارد که يک فونت با حالت Regular بسازد،اين استثنا رخ مي دهد.در نتيجه اين فونت موجود است!
ب) فونتي مي سازد که داراي نامي هماهنگ با نام درخواستي ماست : در اين حالت ساخت يک نمونه با موفقيت همراه بوده و قطعا فونت مورد نظر ما موجود است.
ج) فونتي مخالف فونت مورد نظر ما ساخته مي شود : پس از ساخت ،چنانچه Name فونت ساخته شده مخالف نام درخواستي ما باشد،به اين معني است که فونت مود نظر ما موجود نيست.به عبارت ديگر هنگامي که درخواست ساخت فونتي با نام دلخواه (مثلا MyFont) داشته باشيم و سيستم نتواند آن را توليد کند،فونت پيشفرض (معمولا Microsift Sans sherif) را توليد مي کند.در نتيجه فونت مورد نظر ما موجود نيست!


چگونه از فونت هاي سفارشي خود استفاده کنيم؟
براي استفاده از فونت مورد نظر خود در برنامه، دو راهکار وجود دارد:

1) نصب فونت در سيستم: در اين راهکار پس از اجراي برنامه،و تست وجود يا عدم وجود فونت مورد نظر،پس از اطمينان از عدم وجود فونت مورد نظر،مي توان آن را درسيستم هدف نصب کرد! در اين روش بايد از تابع API ويندوز به نام AddFontResource که در gdi32.dll موجود است،استفاده کنيم.همانطور که مي دانيد براي استفاده از توابع API ويندوز در برنامه هاي NET. ،ابتدا بايد آنها را Import کنيم،چيزي شبيه به اين:

[DllImport("gdi32")]
private static extern int AddFontResource(string strFontFilePath);که در آن تابع AddFontResource که يک رشته حاوي مسير فايل فونت مورد نظر است،دريافت و مقدار int بر ميگرداند را معرفي کرده ايم.عبارت الزامي extern ،تاکيد بر اين موضوع است که متد مذبور از خارج از برنامه (يک API ويندوز يا يک اسمبلي دات نت) فراخواني مي شود.همچنين دسترسي به اين متد بايد حتما static نيز باشد (چون موجود است و از خارج فراخواني مي شود) و در هنگام بارگذاري برنامه يک نمونه دسترسي به آن به حالت استاتيک،ايجاد مي شود.براي Import متد،نيازي نيست که حتما Private باشند بلکه مي توانند public يا .. هم باشند.مقدار بازگشتي AddFontResource نيز ،در صورت موفقيت غير صفر بوده و در صورت عدم موفقيت صفر است.
در رابطه با اين تابع، پنج نکته وجود دارد که عبارتند از :
الف) اطلاعات هر فونت با توجه به مسير آن ذخيره مي شود به عبارت ديگر اگر فونتي با نام MY_Font که در مسير C:\MyFontPath\MyFont.ttf موجود است را به وسيله AddFontResource در سيستم ثبت کنيم،و سپس فايل C:\MyFontPath\MyFont.ttf را حذف نماييم،با آنکه سيستم عامل نام فونت MY_Font را در فهرست فونت ها دارد،اما به دليل حذف فايل حاوي آن،نمي تواند آن را ايجاد نمايد! براي رفع اين مشکل پيشنهاد مي شود که فونت مورد نظر را ابتدا در پوشه فونتها در مسير windir\Fonts کپي کرده و سپس اقدام به نصب آن با توجه به همان مسير windir\Fonts نماييد.
ب) نصب فونت با استفاده از AddFontResource ،باعث ثبت فونت و نصب آن در ويندوز فعال جاري شده و با Restart ويندوز،در دسترس نخواهد بود.براي ثبت فونت به شکلي که در Restart ويندوز نيز در دسترسي باشد،بايد آن را در رجيستري،به شکل دستي،ثبت نمود.اين اطلاعات در مسير

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fontsدر رجيستري ثبت مي شود.در اين مسير،بايد ابتدا يک مقدار از نوع REG_SZ ايجاد کنيم.Name يا نام اين مقدار مهم نيست (هرچه مي تواند باشد) ولي Data يا اطلاعات آن براي يافتن فونت هاي در دسترس، مهم است.Data هر کليد شامل نام فايل فونت مورد نظر است.چنانچه اين فايل در windir\Fonts،موجود باشد،کافيست نام فايل آن را وارد کنيد (TAHOMA.TTF) و اگر در مسير ديگري قرار دارد،بايد حتما مسير کامل آن را درج کنيد (C:\MyFontPath\MyFont.ttf).تاکيد مي شود که اطلاعات اين کليد رجيستري فقط در هر بار Restart بازخواني شده تا اطلاعات فونت در درسترس ،بازيابي شود و پس از بارگذاري کامل ويندوز،براي افزودن يک فونت به فهرست فونت هاي جاري سيستم حتما بايد AddFontResource را فراخواني کرد.
ج) چنانچه بخواهيم فونت نصب شده را Uninsatll کنيم،بايد از تابع RemoveFontResource استفاده کنيم.براي اطلاعات بيشتر مي توان به MSDN مراجعه کرد.توجه به اين موضوع که حتما رد پاي فونت را از رجيستري نيز پاک شود،ضروريست (به قسمت ب مراجعه شود).
د) تابع AddFontResource ،فقط فونت را در سيستم نصب مي کند،ولي بايد به نرم افزارهاي باز ديگر اطلاع دهيد که فونت جديد نصب شده و خود را به روز کنند!براي اينکار بايد توسط متد SendMessage (از API هاي ويندوز) پيغام WM_FONTCHANGE را به تمام پنجره هاي سطح بالا ارسال کند.براي پارامتر دست انداز پنجره (HWND) مي توان از HWND_BROADCAST استفاده کرد.
ه‍) مطابق نکته د ،با آنکه تمام پنجره هاي ويندوز،از اين تغيير مطلع مي شوند،ولي برنامه هاي دات نت نمي توانند از اين تغيير اطلاع يابند.دليل آن وجود يک باگ در +GDI است که مطابق يک جستجو که انجام دادم،از سال 2004 مطرح شده و تا 2006 (http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework.windowsforms/topic42273.aspx)به عنوان يک باگ ،به شکل غير رسمي،منتشر شده است.البته از وجود آن در vista بي خبرم (هر چند که از عدم وجود آن نيز بي اطلاع هستم!).اثر آن در دات نت به اين شکل است که با وجود ثبت فونت در سيستم،شي InstalledFontCollection ،فقط اطلاعات فونت هايي را در دسترس دارد که قبل از اجراي برنامه در سيستم ثبت شده بودند.در نتيجه چنانچه در برنامه دات نتي خود،اقدام به نصب فونت کرديم،حتما بايد برنامه خود را Resatrt کنيم.همچنين براي اطلاع تمام برنامه هاي دات نتي ديگر آنها هم بايد دوباره اجرا شوند ،در غير اينصورت از وجود فونت جديد بي اطلاع خواند بود.
با توجه به نکات مطرح شده قطعه برنامه زير براي نصب قونت در سيستم پيشنهاد مي شود :

public static bool InstallFontInWindows(string strFontFilePath)
{
string strFileName = Path.GetFileName(strFontFilePath);

try
{
//1)Step 1 : Copy font file to Fonts directory
string strFontsPathFile = Environment.GetEnvironmentVariable("windir")
+ @"\Fonts\" + strFileName;
File.Copy(strFontFilePath, strFontsPathFile, true);

//2)Step 2 : Install font in resource of MS-Windows
if (AddFontResource(strFontsPathFile) == 0)
return false;

//3)Step 3 : Set registry information of new installed font for use
RegistryKey reg = Registry.LocalMachine.OpenSubKey(
@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", true);
reg.SetValue(strFileName.Split('.')[0] + " (TrueType)", strFileName);
reg.Close();
SendMessage(HWND_BROADCAST, WM_FONTCHANGE, 0, 0);
}
catch (Exception exc)
{
return false;
}

return true;
}البته تاکيد مي شود که بعد از نصب فونت،حتما برنامه خود را (و نه ويندوز را) Restart کنيد (يا از کاربر بخواهيد که اين کار را انجام دهد).توجه به اين نکته ضروريست که کدهاي بالا داراي خصوصياتي است که نياز به دسترسي هاي سطح Admin دارد (مثلا باز کردن کليد رجيستري در LocalMechine براي نوشتن!).در اين صورت (عدم اجازه کاربر جاري براي اجراي اين فرآيند ها)،سيستم يک استثنا از نوع SecurityException ايجاد مي کند.

2) شما مي توانيد بدون نصب يک فونت در سيستم آن را استفاده کنيد!در واقع +GDI و NET. به شما اين اجازه را مي دهند.در دات نت کلاسي به نام PrivateFontCollection به همين منظور تدارک ديده شده است.ابتدا يک نمونه از اين کلاس ايجاد کنيد.حالا متد AddFontFile اين نمونه را به همراه نام فايل فونت مورد نظر فراخواني کنيد.در اين صورت فونت مورد نظر براي اين نمونه از PrivateFontCollection بار گذاري مي شود.به هر دليل،PrivateFontCollection نتواند فونت را بارگذاري کند يک استثنا از نوع FileNotFoundException روي خواهد داد(حتي اگر فرمت فايل صحيح نباشد!).تا هنگامي که اين نمونه موجود است،مي توان از فونت بار گذاري شده در آن استفاده کرد.براي اين منظور از خصوصيتي Families آن استفاده کنيد.توجه کنيد که در اين حالت فقط فونت براي برنامه،آن هم تا هنگامي که نمونه اي از PrivateFontCollection که در آن فونت را بارگذاري نموده ايد در دسترس باشد،قابل دسترسي است و درسيستم اين فونت در دسترس نخواهد بود.در ضمن تاکيد مي شود که اين کلاس فقط براي فونتهاي TrueType کاربر دارد.

پي نوشت :
* فونت TrueType : در سيستم عامل هاي امروزي ،فونت ها را بر اساس ساختار ترسيم و همچنين فايل محتويات آن، تقسيم بندي مي شوند.بر اساس ساختار، فونت هاي به سه دسته MonoSpace و Serif و Sans Serif تقسيم بندي مي شوند.فونت هاي بر اساس فايل هايشان نيز به دسته هاي متعددي تقسيم مي گردند که با پسوند هاي فايل محتويات آنها شناسايي مي گردند که از جمله آنها مي توان به fon ، fnt ، ttf ، ttc ، fot ، pfb ، pfm و ... اشاره کرد.برخي از فونت هاي ساختاري با يک پسوند مشخص مي شوند.مثلا پسوند fon براي فونتهاي MonoSpace (اينگونه فونت ها، نظير فونت هاي تحت داس ،داراي فضاي کاراکتري يکساني براي تمام کاراکتر ها هستند در نتيجه زيبايي کمتري دارند - نمونه اين فونت،فونت Terminal مي باشد).اما برخي ساختارها داراي پسوند هاي متعدد هستند.مثلا فونت TrueType که بهترين زيبايي را در فرايند رسم خود دارد،مي تواند داراي پسوند هاي ttf (مشهورترين پسوند) يا ttc يا ... باشد.
* نام فونت در داخل فايل حاوي فونت معرفي و معمولا با نام فايل محتوي فونت متفاوت است.
* مي توان به جاي AddFontResource از AddFontResourceEx هم استفاده کرد که البته تنظيمات پيشرفته تري در اختيار برنامه نويس خواهد گذارد.
* windir عبارت است از آدرس پوشه ويندوز،مثلا C:\WINDOWS .
* لطفا اگر دوستان از وجود يا عدم وجود باگ مطرح شده در قسمت ه‍ بخش 'نصب فونت در سيستم' در ويندوز هاي XP SP3 يا Vista اطلاع دارند ما را هم بي خبر نگذارند.
* دوستاني که در WPF کار مي کنند حتما کلاس System.Windows.Media.Fonts را ملاحظه کنند.متد GetFontFamilies اين کلاس مي تواند فهرستي از فونت هاي موجود در يک پوشه را بار گذاري کند.
*اگر ايرادي در اين نوشتار ملاحظه مي کنيد،حتما اينجانب را باخبر سازيد.

موفق و پيروز باشيد

HAMID484
شنبه 05 آذر 1390, 01:28 صبح
دوست عزیز خیلی ممنون از نمونه برنامه ای که قرار دادین.
میدونم که تاپیک قدیمیه. اما موضوعش قدیمی نمیشه :لبخند:
من خودم همیشه برای ویندوز XP , فونت های مورد نظر رو در پوشه فونت کپی میکردم و هیچ مشکلی هم رخ نمیده. آیا فقط اجرای دستور کپی فونت در پوشه فونت ویندوز کافی نیست؟ چون من این روش رو برای XP اجرا کردم و جواب داد. یعنی برنامم با یک فونت خاص Tornado Tahoma سروکار داره و وقتی که این فونت رو در پوشه فونت ویندوز کپی می کنم و برنامه رو مجددا اجرا میکنم همیشه درسته و اصلا نیازی نمیبینم که سایر دستورات برای نصب فونت اجرا بشه.
البته میگم این ها در ویندوز xp هست و الان ویندوز 7 دم دستم نیست که امتحان کنم.
اگر دوستان هم اطلاعی دارن لطفا کمک کنند

hojjatshariffam
یک شنبه 06 آذر 1390, 18:48 عصر
ب) نصب فونت با استفاده از AddFontResource ،باعث ثبت فونت و نصب آن در ويندوز فعال جاري شده و با Restart ويندوز،در دسترس نخواهد بود.براي ثبت فونت به شکلي که در Restart ويندوز نيز در دسترسي باشد،بايد آن را در رجيستري،به شکل دستي،ثبت نمود.اين اطلاعات در مسير

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fontsدر رجيستري ثبت مي شود.در اين مسير،بايد ابتدا يک مقدار از نوع REG_SZ ايجاد کنيم.Name يا نام اين مقدار مهم نيست (هرچه مي تواند باشد) ولي Data يا اطلاعات آن براي يافتن فونت هاي در دسترس، مهم است.Data هر کليد شامل نام فايل فونت مورد نظر است.چنانچه اين فايل در windir\Fonts،موجود باشد،کافيست نام فايل آن را وارد کنيد (TAHOMA.TTF) و اگر در مسير ديگري قرار دارد،بايد حتما مسير کامل آن را درج کنيد (C:\MyFontPath\MyFont.ttf).تاکيد مي شود که اطلاعات اين کليد رجيستري فقط در هر بار Restart بازخواني شده تا اطلاعات فونت در درسترس ،بازيابي شود و پس از بارگذاري کامل ويندوز،براي افزودن يک فونت به فهرست فونت هاي جاري سيستم حتما بايد AddFontResource را فراخواني کرد.
ج) چنانچه بخواهيم فونت نصب شده را Uninsatll کنيم،بايد از تابع RemoveFontResource استفاده کنيم.براي اطلاعات بيشتر مي توان به MSDN مراجعه کرد.توجه به اين موضوع که حتما رد پاي فونت را از رجيستري نيز پاک شود،ضروريست (به قسمت ب مراجعه شود).
د) تابع AddFontResource ،فقط فونت را در سيستم نصب مي کند،ولي بايد به نرم افزارهاي باز ديگر اطلاع دهيد که فونت جديد نصب شده و خود را به روز کنند!براي اينکار بايد توسط متد SendMessage (از API هاي ويندوز) پيغام WM_FONTCHANGE را به تمام پنجره هاي سطح بالا ارسال کند.براي پارامتر دست انداز پنجره (HWND) مي توان از HWND_BROADCAST استفاده کرد.
ه‍) مطابق نکته د ،با آنکه تمام پنجره هاي ويندوز،از اين تغيير مطلع مي شوند،ولي برنامه هاي دات نت نمي توانند از اين تغيير اطلاع يابند.دليل آن وجود يک باگ در +GDI است که مطابق يک جستجو که انجام دادم،از سال 2004 مطرح شده و تا 2006 (http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework.windowsforms/topic42273.aspx)به عنوان يک باگ ،به شکل غير رسمي،منتشر شده است.البته از وجود آن در vista بي خبرم (هر چند که از عدم وجود آن نيز بي اطلاع هستم!).اثر آن در دات نت به اين شکل است که با وجود ثبت فونت در سيستم،شي InstalledFontCollection ،فقط اطلاعات فونت هايي را در دسترس دارد که قبل از اجراي برنامه در سيستم ثبت شده بودند.در نتيجه چنانچه در برنامه دات نتي خود،اقدام به نصب فونت کرديم،حتما بايد برنامه خود را Resatrt کنيم.همچنين براي اطلاع تمام برنامه هاي دات نتي ديگر آنها هم بايد دوباره اجرا شوند ،در غير اينصورت از وجود فونت جديد بي اطلاع خواند بود.
خیلی ممنون از کد زیباتون
ولی من توی ویندوز XP SP3 تست کردم، و به این نتیجه رسیدم که فقط کپی در مسیر فونت های سیستم، کافی می باشد و نیازی به ثبت در رجیستری و نیز ارسال پیغام نیست ، ویندوز خودش همه این کارها را انجام داد، یعنی بعد از کپی هم تو مسیر مورد نظر در رجیستری ثبت شد و هم اینکه نرم افزار هایی مثل آفیس بلافاصبه بعد از کپی فونت آن را وارد لیست خود کردند.
ولی برای احتیاط هر سه استیپ رو انجام دادم چون مطمئن نیستم که تو همه سیستم ها این اتفاق بیافته
ارسال یک مسیج هم هیچ زیانی در بر نداره پس احتیاج شرط واجبه.
در مورد Restart کردن اپلیکیشن هم امتحان کردم: نتیجه این شد که باید حتما این کار را کرد و بدون بستن نرم افزار فونت ها شناسائی نمی شودو همچنان در دات نت 3.5 این باگ وجود داره.
بازم ممنون از بابت کد

hooloo24
دوشنبه 22 آبان 1391, 12:49 عصر
سلام دوستان . من به کمکتون نیاز دارم. استادمون گفته راجع به تابع addfontresource تحقیق کنید که چه پارامترهایی داره و هر پارامتر چکار میکنه و اینکه برنامه ای که در اون از این تابع استفاده شده باشه . راستش من با سی شارپ آشنایی ندارم . میخواستم بدونم این توضیحاتی که اینجا داده شده و نمونه برنامه ای که زحمتشو کشیدید و گذاشتید اینجا واسه من کفایت میکنه . اگه ممکنه کمکم کنید . هفته دیگه باید ارائه بدم سر کلاس . مرسی

HAMID484
پنج شنبه 02 آذر 1391, 20:22 عصر
سلام . با تشکر از کد خوبتون
بعد تقریبا یک سال بر حسب اتفاق کد شما رو در ویندوز 7 اجرا کردم اما هنگام کپی کردن فونت در پوشه font خطای access deny میده
File.Copy(strFontFilePath, strFontsPathFile, true);
این مشکل رو چطوری حل کنم. زیاد هم در نت گشتم اما واقعا چیزی پیدا نکردم هنوز

it.dadkhah
پنج شنبه 09 آذر 1391, 16:17 عصر
سلام.
برای من تابع AddFontResource اصلا کار نمیده.

amirmms
جمعه 18 مرداد 1392, 00:40 صبح
سلام . با تشکر از کد خوبتون
بعد تقریبا یک سال بر حسب اتفاق کد شما رو در ویندوز 7 اجرا کردم اما هنگام کپی کردن فونت در پوشه font خطای access deny میده
File.Copy(strFontFilePath, strFontsPathFile, true);
این مشکل رو چطوری حل کنم. زیاد هم در نت گشتم اما واقعا چیزی پیدا نکردم هنوز

دوست عزیز شما باید نرم افزار رو .run as admin کنید چون نیاز به سطح دسترسی مدیر دارد.

mahdi.ahmadian2010
شنبه 02 آذر 1392, 08:03 صبح
دوست عزیز شما باید نرم افزار رو .run as admin کنید چون نیاز به سطح دسترسی مدیر دارد.

من هم این مشکل رو داشتم اما بدون این که Run as کنید هم میتونید مشکل رو حل کنید. به این لینک نگاه کنید:
http://stackoverflow.com/questions/3007805/embedding-deploying-custom-font-in-net-app/3008351

jan_kocholo
چهارشنبه 13 آذر 1392, 14:59 عصر
سلام
وقت بخیر

میخواسم یه فونت رو در پوشه bin\Debug بزارم و برنامم ازش استفاده کنه. میشه یه نمونه برام بزارید؟

یا اینکه میشه فونت رو به صورت باینری توی برنامه ذخیره کرد و توی برنام ازش استفاده کنه؟

نمخام از برنامه setup بسازم. فقط میخام کنار فایل .exe باشه یا به صورت باینری توی برنامه ذخیره کرد و توی برنام ازش استفاده کنه

ممنون میشم کمکم کنید :افسرده:

jan_kocholo
پنج شنبه 14 آذر 1392, 14:14 عصر
مرسی از همگی
خودم حلش کردم :لبخندساده: