PdfSharp對中文字體支持及排版格式的調整

  最近用PdfSharp生成pdf文檔發現使用微軟雅黑畫中文字體顯示亂碼,查了官方文檔說是不支持中日韓等語言(FAQ鏈接),但是嘗試更換字體爲 “黑體” 後中文是可以正常顯示的,然後萌生出一個想法就是能不能根據輸入的文字來動態識別一個字體是否被支持,而用字體去判斷是否支持指定的文字是行不通的,因爲單從字體來說,就微軟雅黑而言是支持中/英多種文字的。 通過閱讀源碼,最終在CMapInfo的AddChars方法裏找到了解決方案,AddChars方法內部在添加字符的時候會調用OpenTypeDescriptor.CharCodeToGlyphIndex方法將unicode字符映射到相應glyph的索引上,如果映射的索引爲0,則說明該字符使用的字體在pdfSharp是不支持的,那麼我們就可以通過遍歷映射結果字典CharacterToGlyphIndex,只要字典值裏有任何一個0值就說明提供的文本在該字體上有不被支持的字符,一旦被打印就會有亂碼出現,這時就需要換一種字體來顯示了。

 1       //CMapInfo.AddChars方法源碼        
 2      public void AddChars(string text)
 3         {
 4             if (text != null)
 5             {
 6                 bool symbol = _descriptor.FontFace.cmap.symbol;
 7                 int length = text.Length;
 8                 for (int idx = 0; idx < length; idx++)
 9                 {
10                     char ch = text[idx];
11                     if (!CharacterToGlyphIndex.ContainsKey(ch))
12                     {
13                         char ch2 = ch;
14                         if (symbol)
15                         {
16                             // Remap ch for symbol fonts.
17                             ch2 = (char)(ch | (_descriptor.FontFace.os2.usFirstCharIndex & 0xFF00));  // @@@ refactor
18                         }
19                         int glyphIndex = _descriptor.CharCodeToGlyphIndex(ch2); //glyphIndex=0時說明該字符不被PdfSharp支持
20                         CharacterToGlyphIndex.Add(ch, glyphIndex);
21                         GlyphIndices[glyphIndex] = null;
22                         MinChar = (char)Math.Min(MinChar, ch);
23                         MaxChar = (char)Math.Max(MaxChar, ch);
24                     }
25                 }
26             }
27         }

  然而CMapInfo類被設計成internal了,外部是訪問不到裏面方法的,然後就只能把源碼down下來魔改一波了 ^_^ 。我的方案是在XFont上寫一個擴展方法,內部去構造CMapInfo,然後調用AddChars方法,擴展方法所在的類應該和PdfSharp在同一項目,代碼如下:

 1         /// <summary>
 2         /// 該字體是否支持字符串的繪製
 3         /// </summary>
 4         /// <param name="font"></param>
 5         /// <param name="text"></param>
 6         /// <returns></returns>
 7         public static bool IsSupport(this XFont font, string text)
 8         {
 9             OpenTypeDescriptor descriptor = FontDescriptorCache.GetOrCreateDescriptorFor(font) as OpenTypeDescriptor;
10             if (descriptor != null)
11             {
12                 var mapInfo = new CMapInfo(descriptor);
13                 mapInfo.AddChars(text);
14                 var maps = mapInfo.CharacterToGlyphIndex; //CharacterToGlyphIndex是AddChars方法映射結果字典
15                 return !maps.Values.Any(x => x == 0);
16             }
17             return false;
18         }

IsSupport方法的使用:

       public XFont BuildFont(string text, double size)
        {
            var fontFamilies = new string[] { "微軟雅黑", "黑體" }; //根據需要字體列表自己控制數量
            XFont font = null;
            foreach (var name in fontFamilies)
            {
                font = new XFont(name, size, FontStyle.Regular);
                if (font.IsSupport(text)) return font;   
            }
            return font;
        }

  字體亂碼解決之後,又有遇到一個問題,XTextFormatter是pdfsharp內置實現字符串自動換行功能的,對於英文文本自動換行是沒問題的,但是它的換行單元拆分是基於空格的,中文句子中間如果沒有空格就死翹翹了,整個文本就畫在一行了,等於啥都沒幹。修改思路是在原來基礎上加一個分支,判斷當前字符串的寬度加上下一個字符的寬度,如果超過區域的寬度時,拆分一塊。

//..... 省略上面代碼邏輯               
         else
          {
//-------------------以下爲新加入的代碼邏輯--------------
                    if (startIndex + blockLength < _text.Length - 1)
                    {
                        string token = _text.Substring(startIndex, blockLength);
                        var width = _gfx.MeasureString(token, _font).Width;
                        var nextCharWidth = _gfx.MeasureString(_text[startIndex + blockLength + 1].ToString(), _font).Width;
                        if (width + nextCharWidth >= LayoutRectangle.Width - 2) //加上下一個字符超過塊寬度時,強制拆分換行
                        {
                            _blocks.Add(new Block(token, BlockType.Text, _gfx.MeasureString(token, _font).Width));
                            startIndex = idx + 1;
                            blockLength = 0;
                            continue;
                        }
                    }
//-------------------以上 爲新加入的代碼邏輯------------------------
                    inNonWhiteSpace = true;
                    blockLength++;
                }  
//..... 省略下文代碼                                      

  和自動換行需求類似的功能是,在一個區域內打印一行文本,如果文本超出區域寬度時不是換行,而直接截斷後面的內容顯示省略號...代替,方法很簡單,也是基於XFont類的擴展方法,直接上代碼:

 1         /// <summary>
 2         /// 獲取縮略信息,文本寬度超過指定的寬度時顯示省略號...
 3         /// </summary>
 4         /// <param name="font"></param>
 5         /// <param name="text"></param>
 6         /// <param name="width"></param>
 7         /// <returns></returns>
 8         public static string GetOmitText(this XFont font, string text, double width)
 9         {
10             var maxWidth = FontHelper.MeasureString(text, font, XStringFormats.Default);
11             if (maxWidth.Width <= width) return text;
12             var omitWidth = FontHelper.MeasureString("...", font, XStringFormats.Default);
13 
14             int index = 1;
15             while (index < text.Length)
16             {
17                 var part = text.Substring(0, index + 1);
18                 var currentWidth = FontHelper.MeasureString(part, font, XStringFormats.Default);
19                 var nextCharWidth = FontHelper.MeasureString(text.Substring(index + 1, 1), font, XStringFormats.Default);
20                 if (currentWidth.Width + omitWidth.Width + nextCharWidth.Width > width) return part + "...";
21                 index++;
22             }
23             return text;
24         }

   以上就是近來使用PdfSharp遇到的坑,後面遇到新的再補充。

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章