如何在Word文档中批量添加汉字注音

如何在Word文档中批量添加汉字注音

所谓的汉字注音,就是给汉字上方加注拼音。

汉字注音

在Office里面,这个功能叫做 “拼音指南”(Phonetic Guide)

拼音指南

拼音指南一次只能够处理最多30个字,一篇文章不可能只有30个字,上百个字是很正常的,人工处理就会很累。所以,需要做到自动化,做到自动化有两种方式可以做到:

  1. 调用Office的功能;
  2. 直接修改docx文档。

调用Office的功能

调用Office的功能又有两个途径:

  1. VBA;
  2. .net。

其实,这两种途径最终都是调用的Office提供的API。

VBA

我查过了VBA的资料,总共有3个API可用:

  1. FormatPhoneticGuide
  2. Range.PhoneticGuide method (Word)
  3. Application.GetPhonetic method (Excel)

网上最多的用是第一种,使用FormatPhoneticGuide宏,我试过是能用的,但是存在着一个很大的问题:它不能够定制拼音的样式。而且,相对来说不够稳定。

'Word批量使用默认样式加注拼音
Sub BatchAddPinYinByDefaultStyle()
    On Error Resume Next
    Selection.WholeStory
    TextLength = Selection.Characters.Count
    Selection.EndKey
    For i = TextLength To 0 Step -30
        If i < 30 Then
            Selection.MoveLeft Unit:=wdCharacter, Count:=i
            Selection.MoveRight(Unit:=wdCharacter, Count:=i,Extend:=wdExtend)
        Else
            Selection.MoveLeft Unit:=wdCharacter, Count:=30
            Selection.MoveRight(Unit:=wdCharacter, Count:=30,Extend:=wdExtend)
        End If
        SendKeys "{Enter}"
        Application.Run "FormatPhoneticGuide"
    Next
    Selection.WholeStory
End Sub

另外还有一个清除注音的方法,用到了第二个API:

'Word批量清除拼音
Sub CleanPinYin()
    Application.ScreenUpdating = False
    Selection.WholeStory
    TextLength = Selection.Characters.Count
    Selection.GoTo What:=wdGoToHeading, Which:=wdGoToAbsolute, Count:=1
    For i = 0 To TextLength
        With Selection
             .Range.PhoneticGuide Text:=""
        End With
        Selection.MoveRight Unit:=wdCharacter, Count:=1
    Next
    Selection.WholeStory
    Application.ScreenUpdating = True
End Sub

这一个API既可以清除注音,也可以标明注音。只需要给Text赋值拼音即可。这个API好在可以定制拼音的样式,麻烦的是需要自己去计算出拼音,本来是找到了一个计算拼音的内置方法:GetPhonetic,但是,它只存在于Excel里面,在Word里边无法进行调用。

要实现内置的GetPhonetic,我在网上看到有两种实现方法:

  1. 自行实现的VBA,但是实现不够完整:https://github.com/StinkCat/CH_TO_PY
  2. 利用golang写了一个RestFull服务器提供服务,然后提供给VBA调用:https://github.com/yangjianhua/go-pinyin

我们来讨论第二种方法,比较灵活。

首先是golang的拼音计算服务:

package main

import (
    "flag"
    "fmt"
    "strconv"

    "github.com/gin-gonic/gin"
    "github.com/mozillazg/go-pinyin"
)

var a pinyin.Args

func initPinyinArgs(arg int) { // arg should be pinyin.Tone, pinyin.Tone1, pinyin.Tone2, pinyin.Tone3, see go-pinyin doc
    a = pinyin.NewArgs()
    a.Style = arg
}

func getPinyin(c *gin.Context) {
    han := c.DefaultQuery("han", "")
    p := pinyin.Pinyin(han, a)

    c.JSON(200, gin.H{"code": 0, "data": p})
}

func getPinyinOne(c *gin.Context) {
    han := c.DefaultQuery("han", "")
    p := pinyin.Pinyin(han, a)
    s := ""

    if len(p) > 0 {
        s = p[0][0]
    }

    c.JSON(200, gin.H{"code": 0, "data": s})
}

func allowCors() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
        c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
        c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
        c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(204)
            return
        }
        c.Next()
    }
}

func main() {
    // init pinyin output format
    initPinyinArgs(pinyin.Tone)

    fmt.Print("\n\nDEFAULT PORT: 8080, USING '-port portnum' TO START ANOTHER PORT.\n\n")

    port := flag.Int("port", 8080, "Port Number, default 8080")
    flag.Parse()
    sPort := ":" + strconv.Itoa(*port)

    // using gin as a web output
    r := gin.Default()
    r.Use(allowCors())
    r.GET("/pinyin", getPinyin) // Call like GET http://localhost:8080/pinyin?han=我来了
    r.GET("/pinyin1", getPinyinOne)
    r.Run(sPort)
}

接着,我们来封装自己的GetPhonetic

'从Json字符串中提取data字段的数据
Function getDataFromJSON(s As String) As String
    With CreateObject("VBScript.Regexp")
        .Pattern = """data"":""(.*)"""
        getDataFromJSON = .Execute(s)(0).SubMatches(0)
    End With
End Function

'使用http组件调用拼音转换服务获取拼音字符
Function GetPhonetic(strWord As String) As String
    Dim myURL As String
    Dim winHttpReq As Object

    Set winHttpReq = CreateObject("WinHttp.WinHttpRequest.5.1")

    myURL = "http://localhost:8080/pinyin1"
    myURL = myURL & "?han=" & strWord

    winHttpReq.Open "GET", myURL, False
    winHttpReq.Send

    GetPhonetic = getDataFromJSON(winHttpReq.responseText)
End Function

'测试GetPhonetic方法
Sub testGetPhonetic()
    ret = GetPhonetic("汗")
    MsgBox ret
End Sub

判定字符是否中文的方法:

'判断传入的Unicode是否为中文字符
Function isChinese(uniChar As Integer) As Boolean
    isChinese = uniChar >= 19968 Or uniChar < 0
End Function

最后组装生成拼音注音的VBA脚本:

' Word批量拼音注音
' Alignment 对齐方式, see: https://learn.microsoft.com/en-us/office/vba/api/word.wdphoneticguidealignmenttype
' Raise 偏移量(磅)
' FontSize 字号(磅)
' FontName 字体
Sub BatchAddPinYin()
    Application.ScreenUpdating = False
    Dim SelectText As String
    Dim PinYinText As String
    Selection.WholeStory
    TextLength = Selection.Characters.Count
    Selection.GoTo What:=wdGoToHeading, Which:=wdGoToAbsolute, Count:=1
    For i = 0 To TextLength
        Selection.MoveRight Unit:=wdCharacter, Count:=1
        Selection.MoveLeft Unit:=wdCharacter, Count:=1, Extend:=wdExtend
        With Selection
            SelectText = .Text '基准文字
            If isChinese(AscW(SelectText)) Then '判断是否为中文字符
                PinYinText = GetPhonetic(SelectText) '基准文字 转换为 拼音文字
                If PinYinText <> "" Then
                    .Range.PhoneticGuide Text:=PinYinText, Alignment:=wdPhoneticGuideAlignmentCenter, _
                    Raise:=0, _
                    FontSize:=10, _
                    FontName:="等线"
                End If
            End If
        End With
        Selection.MoveRight Unit:=wdCharacter, Count:=1
    Next
    Selection.WholeStory
    Application.ScreenUpdating = True
End Sub

根据golang服务代码的提供者所说,它比较明显的缺点是对多音字的处理不如Word原来的拼音指南,所以需要后期进行手工校正。

后期校正肯定是必须的,就好比古文里边还有一些通假字,发音是不一样的,这个,我想哪怕是拼音指南也做不好的吧。

完整的BAS文件如下:

'判断传入的Unicode是否为中文字符
Function isChinese(uniChar As Integer) As Boolean
    isChinese = uniChar >= 19968 Or uniChar < 0
End Function

'从Json字符串中提取data字段的数据
Function getDataFromJSON(s As String) As String
    With CreateObject("VBScript.Regexp")
        .Pattern = """data"":""(.*)"""
        getDataFromJSON = .Execute(s)(0).SubMatches(0)
    End With
End Function

'使用http组件调用拼音转换服务获取拼音字符
Function GetPhonetic(strWord As String) As String
    Dim myURL As String
    Dim winHttpReq As Object

    Set winHttpReq = CreateObject("WinHttp.WinHttpRequest.5.1")

    myURL = "http://localhost:8080/pinyin1"
    myURL = myURL & "?han=" & strWord

    winHttpReq.Open "GET", myURL, False
    winHttpReq.Send

    GetPhonetic = getDataFromJSON(winHttpReq.responseText)
End Function

'测试GetPhonetic方法
Sub testGetPhonetic()
    ret = GetPhonetic("汗")
    MsgBox ret
End Sub

' Word批量拼音注音
' Alignment 对齐方式, see: https://learn.microsoft.com/en-us/office/vba/api/word.wdphoneticguidealignmenttype
' Raise 偏移量(磅)
' FontSize 字号(磅)
' FontName 字体
Sub BatchAddPinYin()
    Application.ScreenUpdating = False
    Dim SelectText As String
    Dim PinYinText As String
    Selection.WholeStory
    TextLength = Selection.Characters.Count
    Selection.GoTo What:=wdGoToHeading, Which:=wdGoToAbsolute, Count:=1
    For i = 0 To TextLength
        Selection.MoveRight Unit:=wdCharacter, Count:=1
        Selection.MoveLeft Unit:=wdCharacter, Count:=1, Extend:=wdExtend
        With Selection
            SelectText = .Text '基准文字
            If isChinese(AscW(SelectText)) Then '判断是否为中文字符
                PinYinText = GetPhonetic(SelectText) '基准文字 转换为 拼音文字
                If PinYinText <> "" Then
                    .Range.PhoneticGuide Text:=PinYinText, Alignment:=wdPhoneticGuideAlignmentCenter, _
                    Raise:=0, _
                    FontSize:=10, _
                    FontName:="等线"
                End If
            End If
        End With
        Selection.MoveRight Unit:=wdCharacter, Count:=1
    Next
    Selection.WholeStory
    Application.ScreenUpdating = True
End Sub

'Word批量使用默认样式加注拼音
Sub BatchAddPinYinByDefaultStyle()
    Application.ScreenUpdating = False
    On Error Resume Next
    Selection.WholeStory
    TextLength = Selection.Characters.Count
    Selection.EndKey
    For i = TextLength To 0 Step -30
        If i <= 30 Then
            Selection.MoveLeft Unit:=wdCharacter, Count:=i
            SelectText = Selection.MoveRight(Unit:=wdCharacter, Count:=i,Extend:=wdExtend)
        Else
            Selection.MoveLeft Unit:=wdCharacter, Count:=30
            SelectText = Selection.MoveRight(Unit:=wdCharacter, Count:=30,Extend:=wdExtend)
        End If
        SendKeys "{Enter}"
        Application.Run "FormatPhoneticGuide"
    Next
    Selection.WholeStory
    Application.ScreenUpdating = True
End Sub

'Word批量清除拼音注音
Sub CleanPinYin()
    Application.ScreenUpdating = False
    Selection.WholeStory
    TextLength = Selection.Characters.Count
    Selection.GoTo What:=wdGoToHeading, Which:=wdGoToAbsolute, Count:=1
    For i = 0 To TextLength
        With Selection
             .Range.PhoneticGuide Text:=""
        End With
        Selection.MoveRight Unit:=wdCharacter, Count:=1
    Next
    Selection.WholeStory
    Application.ScreenUpdating = True
End Sub

.net

它其实也是调用的Office的API,这个跟VBA调用API没有本质上的区别,是一样的。

VS2022需要安装:Visual Studio Tools for Office(VSTO)

然后,在项目当中引用程序集:Microsoft.Office.Interop.Word ,VS2022有14和15版本。

我本机的是Office16,而vs2022并没有提供相关的程序集,所以我没有办法使用,也就没有做进一步的探索了。

我查文档在Microsoft.Office.Interop.Word命名空间下,有一个Range.PhoneticGuide方法,接口看起来跟VBA调用的差不多,使用上应该也是差不太多的。

直接修改docx文档

docx的文档本质上是一个经过了zip压缩的OpenXML文档。

基本上,主流的办公软件都支持这样一个标准:微软Office、苹果iWork、WPS Office、Google Docs。

拼音指南在Office Open XML中的类型名是:CT_Ruby

Ruby,Wiki百科中解释为:注音,或称注音标识、加注音、标拼音、拼音指南。

文档可见于:

我稍微研究了下,拼音指南的节点:<w:ruby>

其下面有若干个子节点:

  1. <w:rubyPr>是拼音指南的样式,
  2. <w:rt>是拼音指南的拼音文字,
  3. <w:rubyBase>是拼音指南的基准文字。

一个比较完整的拼音指南的XML是这样的:

<w:ruby>
    <w:rubyPr>
        <w:rubyAlign w:val="center"/>
        <w:hps w:val="26"/>
        <w:hpsRaise w:val="50"/>
        <w:hpsBaseText w:val="52"/>
        <w:lid w:val="zh-CN"/>
    </w:rubyPr>
    <w:rt>
        <w:r w:rsidR="00002ED0" w:rsidRPr="00002ED0">
            <w:rPr>
                <w:rFonts w:ascii="等线" w:eastAsia="等线" w:hAnsi="等线"/>
                <w:color w:val="333333"/>
                <w:sz w:val="26"/>
                <w:shd w:val="clear" w:color="auto" w:fill="FFFFFF"/>
            </w:rPr>
            <w:t>diǎn</w:t>
        </w:r>
    </w:rt>
    <w:rubyBase>
        <w:r w:rsidR="00002ED0">
            <w:rPr>
                <w:rFonts w:ascii="华文楷体" w:eastAsia="华文楷体" w:hAnsi="华文楷体"/>
                <w:color w:val="333333"/>
                <w:sz w:val="52"/>
                <w:shd w:val="clear" w:color="auto" w:fill="FFFFFF"/>
            </w:rPr>
            <w:t>点</w:t>
        </w:r>
    </w:rubyBase>
</w:ruby>

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容