iT邦幫忙

1

ASP.NET MVC C# itextsharp List問題

各位前輩好,想請教一個問題,因公司專案跟政府部門配合

而公文列印出來的編號清單都需要用中文顯示

目前功能是在網頁上輸入文字,再列印成pdf檔
https://ithelp.ithome.com.tw/upload/images/20180531/20110125G0l8LHYbQD.png
上圖是使用Telerik的html編輯器產生的,list-style-type是使用css提供的
cjk-ideographic中文字形,而列印成pdf檔用的是itextsharp

比如說 下圖是目前list的呈現方式
https://ithelp.ithome.com.tw/upload/images/20180531/20110125YWXKgJr5FX.png

在itext官方文件上面,只有看到GreekList, RomanList, ZapfDingbatsList, ZapfDingbatsNumberList 四種特殊List

想請問itextsharp生成PDF有沒有中文list的呈現方式? 謝謝各位


問題解決 謝謝 fysh711426 大大

附上相關程式碼

namespace iTextSharp.Extensions
{
    public class ChineseList : List
    {
        public ChineseList(bool brackets, bool IsoldNumber)
        {
            this.numbered = IsoldNumber; //true= 甲乙丙丁,false=一二三四
            this.lowercase = brackets;  //true = 、 ,false = () ;
        }

        public override bool Add(IElement o)
        {
            string postSymbol1 = "、";
            string postSymbol2 = "(";
            string postSymbol3 = ")";
            if (o is ListItem)
            {
                ListItem item = (ListItem)o;
                Chunk chunk = new Chunk(preSymbol, symbol.Font);

                if (lowercase)  //加頓號
                {
                    chunk.Attributes = symbol.Attributes;
                    chunk.Append(ChineseNumberFactory.GetString(first + list.Count, numbered));
                    chunk.Append(postSymbol1);
                }
                else //加括號
                {
                    chunk.Attributes = symbol.Attributes;
                    chunk.Append(postSymbol2);
                    chunk.Append(ChineseNumberFactory.GetString(first + list.Count, numbered));
                    chunk.Append(postSymbol3);
                }
                item.ListSymbol = chunk;
                item.SetIndentationLeft(symbolIndent, autoindent);
                item.IndentationRight = 0;
                list.Add(item);
                return true;
            }
            else if (o is List)
            {
                List nested = (List)o;
                nested.IndentationLeft = nested.IndentationLeft + symbolIndent;
                first--;
                list.Add(nested);
                return true;
            }
            return false;
        }
    }
    public class ChineseNumberFactory      
    {
        public static string GetString(int index, bool IsOldNumber)
        {
            return FormatChineseNumber(index, IsOldNumber);
        }

        private static string FormatChineseNumber(decimal n, bool IsOldNumber) //轉換為中文數字
        {
            string t =
                EastAsiaNumericFormatter.FormatWithCulture(
                "Ln", n,
                null, new CultureInfo("zh-TW"));
            if (IsOldNumber)
            {
                string o = GetChineseOldNumber(n);
                return o;
            }
            string pattern = "[^一二三四五六七八九]十";
            string one = "一";
            string res = Regex.Replace(t, pattern, m =>
            {
                return m.Value.Substring(0, 1) + one + m.Value.Substring(1);
            });
            return res;
        }

        private static string GetChineseOldNumber(decimal n)  //轉換為中國數字
        {
            string[] strChineseOldNumber = { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" };
            n = n - 1;
            string NewStr = n.ToString();

            int len = NewStr.Length;
            string tmpstr, rstr;
            rstr = "";

            for (int i = 1; i <= len; i++)
            {
                tmpstr = NewStr.Substring(len - i, 1);
                rstr = rstr + strChineseOldNumber[Int32.Parse(tmpstr)];
            }
            return rstr;
        }
    }

    public class NumberList : List    //數字加上頓號、括號
    {
        public NumberList(bool lowercase)
            : base(true)
        {
            this.lowercase = lowercase;
        }

        public override bool Add(IElement o)
        {
            string postSymbol1 = "、";
            string postSymbol2 = "(";
            string postSymbol3 = ")";
            if (o is ListItem)
            {
                ListItem item = (ListItem)o;
                Chunk chunk = new Chunk(preSymbol, symbol.Font);
                int index =first + list.Count;
                if (lowercase)  //加頓號
                {
                    chunk.Attributes = symbol.Attributes;
                    chunk.Append(index.ToString());
                    chunk.Append(postSymbol1);
                }
                else //加括號
                {
                    chunk.Attributes = symbol.Attributes;
                    chunk.Append(postSymbol2);
                    chunk.Append(index.ToString());
                    chunk.Append(postSymbol3);
                }
                item.ListSymbol = chunk;
                item.SetIndentationLeft(symbolIndent, autoindent);
                item.IndentationRight = 0;
                list.Add(item);
                return true;
            }
            else if (o is List)
            {
                List nested = (List)o;
                nested.IndentationLeft = nested.IndentationLeft + symbolIndent;
                first--;
                list.Add(nested);
                return true;
            }
            return false;
        }
    }
}

測試結果:
https://ithelp.ithome.com.tw/upload/images/20180605/20110125GbjyEQhsOS.png

w4560000 iT邦新手 5 級 ‧ 2018-06-01 18:12:23 檢舉
謝謝大大回覆,測試大大的程式可以執行,不過又遇到個問題
中文字無法顯示 

1 個回答

2
fysh711426
iT邦研究生 4 級 ‧ 2018-06-01 00:14:25
最佳解答

研究了一下,給大大參考看看。
剛剛跑去 GitHub 偷看了 RomanList.cs 的程式碼,
感覺可以改成中文的,所以試了一下。

namespace iTextSharp.Extensions
{
    public class ChineseList : List
    {
        public ChineseList() : base(true)
        {
            this.lowercase = true;
        }
        
        public ChineseList(int symbolIndent) : base(true, symbolIndent)
        {
            this.lowercase = true;
        }

        public ChineseList(bool lowercase) : base(true)
        {
            this.lowercase = lowercase;
        }

        public ChineseList(bool lowercase, int symbolIndent) : base(true, symbolIndent) {
            this.lowercase = lowercase;
        }

        public override bool Add(IElement o)
        {
            if (o is ListItem)
            {
                ListItem item = (ListItem)o;
                Chunk chunk = new Chunk(preSymbol, symbol.Font);
                chunk.Attributes = symbol.Attributes;
                chunk.Append(ChineseNumberFactory.GetString(first + list.Count, lowercase));
                chunk.Append(postSymbol);
                item.ListSymbol = chunk;
                item.SetIndentationLeft(symbolIndent, autoindent);
                item.IndentationRight = 0;
                list.Add(item);
                return true;
            }
            else if (o is List)
            {
                List nested = (List)o;
                nested.IndentationLeft = nested.IndentationLeft + symbolIndent;
                first--;
                list.Add(nested);
                return true;
            }
            return false;
        }
    }

    public class ChineseNumberFactory
    {
        public static string GetString(int index, bool lowercase)
        {
            return FormatChineseNumber(index, !lowercase);
        }

        private static string FormatChineseNumber(decimal n, bool moneyChar)
        {
            string t =
                EastAsiaNumericFormatter.FormatWithCulture(
                moneyChar ? "L" : "Ln", n,
                null, new CultureInfo("zh-TW"));
            string pattern = moneyChar ?
                    "[^壹貳參肆伍陸柒捌玖]拾" :
                    "[^一二三四五六七八九]十";
            string one = moneyChar ? "壹" : "一";
            string res = Regex.Replace(t, pattern, m =>
            {
                return m.Value.Substring(0, 1) + one + m.Value.Substring(1);
            });
            
            if (moneyChar && res.StartsWith("拾"))
            {
                res = "壹" + res;
            }
            return res;
        }
    }
}

數字轉換參考了黑暗大大的這篇文章,可以支援一般一二三和金額壹貳參
CODE-將地址中的阿拉伯數字轉為中文大寫

安裝 InternationalNumericFormatter.dll 參考了小歐大大的這篇文章。
[C#]Visual Studio International Feature Pack 2.0 與阿拉伯數字轉繁中格式範例

最後附上測試程式:

var basePath = AppDomain.CurrentDomain.BaseDirectory;

var baseFont = BaseFont.CreateFont($"{basePath}fonts/TW-Kai-98_1.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
var NORMAL = new Font(baseFont, 12, Font.NORMAL);

var document = new Document();
var writer = PdfWriter.GetInstance(document, new FileStream(string.Format("{0}helloworld.pdf", AppDomain.CurrentDomain.BaseDirectory), FileMode.OpenOrCreate));
writer.InitialLeading = 30;

document.Open();

document.Add(new Chunk("測試列表:", NORMAL));
document.Add(Chunk.NEWLINE);

var list1 = new iTextSharp.Extensions.ChineseList();
for (var i = 0; i < 10; i++)
{
    list1.Add(new iTextSharp.text.ListItem((i + 1).ToString(), NORMAL));
}

document.Add(list1);
document.Add(Chunk.NEWLINE);

var list2 = new iTextSharp.Extensions.ChineseList(false);
for (var i = 0; i < 10; i++)
{
    list2.Add(new iTextSharp.text.ListItem((i + 1).ToString(), NORMAL));
}

document.Add(list2);
document.Add(Chunk.NEWLINE);

document.Close();

結果:
https://ithelp.ithome.com.tw/upload/images/20180601/201068652XNN0CwSLq.jpg

看更多先前的回應...收起先前的回應...
小康 iT邦新手 5 級 ‧ 2018-06-01 09:17:19 檢舉

經過看到這篇文章 不好意思另外問個問題
請問大大們有使用itextsharp轉PDF print特殊字元嗎??
例如 "®" 目前我是使用特殊字元的unicode value /u00ae來做轉換
參考這篇
https://stackoverflow.com/questions/31268867/itext-unable-to-print-mathematical-characters-like-%E2%88%88-%E2%88%A9-%E2%88%91-%E2%88%AB-%E2%88%86-%E2%88%9A-%E2%88%A0
但是如果從Database撈出一串文字 例如"ABCD®(EFGH®)"這樣該怎麼做呢??

fysh711426 iT邦研究生 4 級 ‧ 2018-06-01 12:46:33 檢舉

大大應該是沒有掛字型或掛的字型沒有 ® 這個字。
我用微軟正黑體做了範例,給您參考看看。

var basePath = AppDomain.CurrentDomain.BaseDirectory;

//微軟正黑體字型
var baseFont = BaseFont.CreateFont($"{basePath}fonts/msjh.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
var NORMAL = new Font(baseFont, 12, Font.NORMAL);

var document = new Document();
var writer = PdfWriter.GetInstance(document, new FileStream(string.Format("{0}helloworld.pdf", AppDomain.CurrentDomain.BaseDirectory), FileMode.OpenOrCreate));
writer.InitialLeading = 30;

document.Open();

document.Add(new Chunk("ABCD®(EFGH®)", NORMAL));
document.Add(Chunk.NEWLINE);

document.Close();

結果
https://ithelp.ithome.com.tw/upload/images/20180601/20106865VIjNSyOvuQ.jpg

如果對難字有興趣,可以參考 [C#] 難字處理心得 這篇文章。

w4560000 iT邦新手 5 級 ‧ 2018-06-01 18:22:54 檢舉

這是目前編輯器畫面https://ithelp.ithome.com.tw/upload/images/20180601/20110125xZBDWENuBV.png

這是pdf畫面https://ithelp.ithome.com.tw/upload/images/20180601/201101259u11feUAyu.png

確實是有抓到https://ithelp.ithome.com.tw/upload/images/20180601/20110125RpVWNPz4eW.png

目前我是猜是字型問題,目前程式是用
public ListItem(Chunk chunk);
https://ithelp.ithome.com.tw/upload/images/20180601/20110125mlXSdvIMCb.png

那我在想辦法帶入font參數就可以了嗎? 謝謝

w4560000 iT邦新手 5 級 ‧ 2018-06-01 18:24:43 檢舉

https://ithelp.ithome.com.tw/upload/images/20180601/20110125VsbJos8WY2.png

fysh711426 iT邦研究生 4 級 ‧ 2018-06-01 19:18:31 檢舉

恩少了字型,可以參考程式 NORMAL 的地方。
字型可以到 C:\Windows\Fonts 資料夾找,選一個大大喜歡的複製出來就可以了。
/images/emoticon/emoticon01.gif

w4560000 iT邦新手 5 級 ‧ 2018-06-05 08:49:42 檢舉

謝謝大大 測試成功~
https://ithelp.ithome.com.tw/upload/images/20180605/20110125CgRczLEA3h.png
/images/emoticon/emoticon41.gif

fysh711426 iT邦研究生 4 級 ‧ 2018-06-05 12:58:38 檢舉

很高興幫助到您。/images/emoticon/emoticon42.gif
您修改後的版本可以支援,甲乙丙、括號、頓號,太厲害了,趕快筆記下來。

我要發表回答

立即登入回答