CODE-製作注音文字圖檔
| | 4 | | ![]() |
之前幫小木頭寫的英文單字測驗,有個小問題。
原本網頁介面上,每個單字只有圖片加英文,配合單字的圖片都是去網路上找的,很難做到100%精準搭配,有時甚至找不到明確符合的(例如: 星期三要用什麼圖片來象徵?),不小心就會變成聯想力考驗。例如,當初女王挑了一張可愛的老鼠卡通圖片(下圖左)當成Rats的插圖,結果發現小木頭竟把這個學校還沒教的單字誤解成"溜滑板",暈倒~~~
決定出個改良版,為每張圖片加註附含注音的中文翻譯,小木頭讀注音國字已不是問題,這樣子看到生字才不用看圖亂猜一通。
為了小木頭的功課,我有了新功課 ==> 研發網頁注音字型產生模組!
注音字型的來源,我選擇使用中原大學數學系王漢宗教授所研發的GPL授權中文字型(Microsoft Word也有產生注音字的實力,不過直接用字型比較單純),理論上中文字只要選用注音字型就會有注音標示,但我採行的策略是為預先產生中文翻譯並存成圖檔,網頁執行時直接顯示圖檔。如此,瀏覽器端不需要安裝任何字型都能正確顯示,才算做到Windows、iPad或Android通吃。(以前寫網頁要跨瀏覽器就夠煩了,現在起還要考慮跨平台,這行飯愈來愈不容易吃囉!)
在IIS Server的機器上安裝好王漢宗注音字型,我寫了以下的ChWordImage.ashx,可接受不同文字內容,利用Graphics.DrawString使用注音字型繪製指定文字內容並轉為圖檔。程式還接收寬度、高度、前景/背景色、字型大小等參數。另外,由於中文字有破音字(同一字有多種讀音),故實際上注音字型共有四組,平時用第一組注音字型,遇破音字時需切換其他組,所以我再多接收一個破音字選用參數,文字內容的每個字元可用0,1,2,3指定不同的破音字型。而DrawString也因為如此必須改寫成以字元為繪製單位,才能做到每個字元獨立切換不同字型。
<%@ WebHandler Language="C#" Class="ChWordImage" %>
using System;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Drawing.Text;
public class ChWordImage : IHttpHandler {
static string[] PhonFonts = new string[]
{
"王漢宗中楷體注音", "王漢宗中楷體破音一",
"王漢宗中楷體破音二", "王漢宗中楷體破音三"
};
public void ProcessRequest (HttpContext context) {
//忽略參數檢查
int w = int.Parse(context.Request["w"] ?? "256");
int h = int.Parse(context.Request["h"] ?? "64");
float fs = float.Parse(context.Request["fs"] ?? "20");
Color bc = ColorTranslator.FromHtml("0x" +
(context.Request["bc"] ?? "dddddd"));
Color fc = ColorTranslator.FromHtml("0x" +
(context.Request["fc"] ?? "000000"));
string txt = context.Request["t"] ?? "黑暗執行緒";
//允許不同的字指定破音字,如四個字第三個字要用破音字一 af=0010
string af = context.Request["af"] ?? new string('0', txt.Length);
//建立畫布
Bitmap bmp = new Bitmap(w, h);
//取得字數,測量並計算寬度,決定置中用的位移
Graphics g = Graphics.FromImage(bmp);
//塗上背景色
Brush p = new SolidBrush(bc);
g.FillRectangle(p, 0, 0, bmp.Width, bmp.Height);
//使用"王漢宗中楷體"
Font fnt = new Font(PhonFonts[0], fs);
var sz = g.MeasureString(txt, fnt);
//設定文字反鋸齒
g.TextRenderingHint = TextRenderingHint.AntiAlias;
//取得每個字的寬度
float widthPerChar = sz.Width / txt.Length;
//計算置中用的位移
float offsetX = (bmp.Width - sz.Width) / 2;
float offsetY = (bmp.Height - sz.Height) / 2;
//考量破音字要換字型,每個字元可用不同字型
//用迴圈一次畫一個字元
for (int i = 0; i < txt.Length; i++)
{
//查第i個字元的破音字指定
int fntIdx = (byte)af[i] - 0x30;
//以前景色寫上文件
g.DrawString(
txt[i].ToString(),
new Font(PhonFonts[fntIdx], fs),
new SolidBrush(fc),
new PointF(
offsetX + widthPerChar * i,
offsetY));
}
if (context.Request["m"] == "save")
{
//將結果存為檔案
bmp.Save(context.Server.MapPath(
string.Format("{0}", context.Request["f"])));
}
else
{
//將結果以PNG格式傳回
context.Response.ContentType = "image/png";
bmp.Save(context.Response.OutputStream, ImageFormat.Png);
}
}
public bool IsReusable {
get {
return false;
}
}
}
另外,我寫了一個測試執行效果的網頁:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>注音文字測試</title>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.1.js"
type="text/javascript"></script>
<script type="text/javascript">
$(function () {
$("#t").change(function () {
//取得文字長度, select數要相等
var len = $(this).val().length;
//移除多餘的select
var $altBlock = $("#spnAlt");
$altBlock.find("select:gt(" + (len-1) + ")").remove();
//取現有select數
var curLen = $altBlock.find("select").length;
//補足字元數個select
for (var i = curLen; i < len; i++)
$altBlock.append(
"<select><option value='0'>0</option>" +
"<option value='1'>1</option>" +
"<option value='2'>2</option>" +
"<option value='3'>3</option></select>");
}).change();
$("#bc,#fc").change(function () {
$(this).parent().find(".c").css("background-color", "#" + this.value);
}).change();
//任何欄位變動後就重新顯示
$("input,select").live("change", function () {
//組成URL
var url = "ChWordImage.ashx?t=" + escape($("#t").val()) +
"&w=" + $("#w").val() + "&h=" + $("#h").val() +
"&bc=" + $("#bc").val() + "&fc=" + $("#fc").val() +
"&af=" + getAltFont() + "&fs=" + $("#fs").val();
//設定圖檔產生參數
$("#preview").attr("src", url);
//取得破音字型切換
function getAltFont() {
var v = [];
$("#spnAlt").find("option:selected").each(function () {
v.push(this.value);
});
return v.join('');
}
});
//觸發初始change
$("input:first").change();
});
</script>
<style type="text/css">
body { font-size: 11pt; background-color: #444444; color: yellow; }
#bc,#fc { width: 50px; }
#w,#h,#fs { width: 50px; }
input.c { border: 1px dotted black; width: 40px; }
span { margin: 3px; }
div { padding: 4px; }
</style>
</head>
<body>
<div>
<div><span>寬 度:</span><input type="text" id="w" value="256"/></div>
<div><span>高 度:</span><input type="text" id="h" value="50"/></div>
<div><span>文 字:</span><input type="text" id="t" value="黑暗執行緒" /></div>
<div><span>破音字:</span><span id="spnAlt"></span></div>
<div><span>大 小:</span><input type="text" id="fs" value="20" /></div>
<div><span>顏 色:</span><input type="text" id="fc" value="ffffff" />
<input class='c' readonly /></div>
<div><span>底 色:</span><input type="text" id="bc" value="af2f00" />
<input class='c' readonly /></div>
<div style="padding: 5px;"><img id="preview" /></div>
</div>
</body>
</html>
執行範例:
破音字示範:
如上圖,"差"字有很多讀音,切換0/1/2/3就可以得到不同的注音標示。
PS: 若要更方便指定破音字,還可以定義特殊的標註語法,例如[$差1$]代表差字的破音字組1,[$差2$]代表差字的破音字組2,但為求範例單純,這裡僅用額外參數的做法。
From Monday to Friday, coding for the company.
At weekends, coding for the family.
嗯! 驗證本人為程式魔人無誤~~
Comments
# by 毛豆
讚,為家人付出最帥
# by 米斯特‧載卡多
魔人是不需要休息的 XD
# by KKBruce
突然覺得,好像可以拿來當驗證程式碼使用。
# by 資訊一哥
如果搭配線上出題考試 就太帥了!!!