一尺寸與環(huán)繞方式實(shí)戰(zhàn)教程)
平時(shí)在Word里整理文檔遇到最多的煩心事之一就是圖片。從不同來(lái)源復(fù)制進(jìn)來(lái)的截圖、照片、設(shè)計(jì)稿尺寸七零八落有的撐滿整頁(yè)有的縮成指甲蓋環(huán)繞方式還各種不統(tǒng)一一頁(yè)文檔改下來(lái)半天時(shí)間就耗在拖拽圖片上了。后來(lái)我用宏把整套流程自動(dòng)化之后處理一百?gòu)垐D片基本就是幾秒鐘的事。這篇就把完整思路、可直接抄的代碼、以及我踩過(guò)的坑一次說(shuō)清楚。宏這個(gè)東西在Word里其實(shí)沒那么神秘它本質(zhì)上就是一段用VBAVisual Basic for Applications寫的小程序。你可以用開發(fā)工具選項(xiàng)卡下面的宏功能錄制一段操作Word會(huì)把你的鼠標(biāo)點(diǎn)擊和鍵盤輸入翻譯成代碼也可以直接打開VBA編輯器手寫代碼。批處理圖片這種重復(fù)性強(qiáng)、規(guī)則明確的操作用宏來(lái)處理是最合適的。先說(shuō)清楚這套內(nèi)容適合誰(shuí)看經(jīng)常用Word寫標(biāo)書、做產(chǎn)品手冊(cè)、整理會(huì)議紀(jì)要、編輯技術(shù)文檔的朋友以及想把從網(wǎng)上復(fù)制來(lái)的圖文資料快速規(guī)范化的人。你不需要會(huì)編程只要會(huì)復(fù)制粘貼代碼、能看懂中文注釋就夠了。1. 宏操作的整體思路與準(zhǔn)備工作1.1 批量修改圖片格式為什么必須用宏很多人第一反應(yīng)是我用Word的查找替換不就行了但圖片不是文字查找替換對(duì)圖片幾乎無(wú)能為力。你當(dāng)然可以一張一張右鍵設(shè)置大小和環(huán)繞方式但假設(shè)一份文檔里有八十張截圖每張都要把寬度改成14厘米、居中顯示、四周型環(huán)繞手動(dòng)操作意味著至少240次獨(dú)立的鼠標(biāo)點(diǎn)選。人不是機(jī)器做到第三十張就會(huì)開始漏改或者改錯(cuò)。宏能解決的正是這個(gè)重復(fù)且規(guī)則明確的問(wèn)題。它的執(zhí)行邏輯很簡(jiǎn)單遍歷文檔中所有圖片每張都按你預(yù)設(shè)的規(guī)則調(diào)整一遍。不管圖片是二十張還是兩百?gòu)埿蕩缀跻粯印N易约簻y(cè)試過(guò)一臺(tái)普通的辦公電腦處理一份含120張圖片的文檔宏跑完用不了五秒而且每張圖片的處理結(jié)果完全一致不會(huì)出現(xiàn)這張改了那張忘了的情況。另外還有個(gè)手動(dòng)操作很難顧及的細(xì)節(jié)圖片比對(duì)的基準(zhǔn)。手動(dòng)改圖時(shí)你通常會(huì)目測(cè)圖片大小是否合適但目測(cè)是不穩(wěn)定的。宏可以設(shè)定一個(gè)絕對(duì)標(biāo)準(zhǔn)比如所有圖片寬度統(tǒng)一為14厘米這就保證了整套文檔的視覺一致性。對(duì)于標(biāo)書、技術(shù)方案這類有格式審查要求的文檔這一點(diǎn)尤其重要。1.2 準(zhǔn)備宏運(yùn)行環(huán)境啟用宏與打開VBA編輯器在寫代碼之前有兩項(xiàng)準(zhǔn)備工作必須先做很多人卡在第一步其實(shí)就是這兩個(gè)地方?jīng)]設(shè)置好。第一步是調(diào)出開發(fā)工具選項(xiàng)卡。Word默認(rèn)是隱藏這個(gè)選項(xiàng)卡的。在Word的文件選項(xiàng)自定義功能區(qū)里勾選右側(cè)主選項(xiàng)卡列表中的開發(fā)工具確定之后就能在頂部菜單欄看到它。如果你的Word是精簡(jiǎn)版或者綠色版可能沒有這個(gè)選項(xiàng)卡那就需要用Alt F11直接打開VBA編輯器——這個(gè)快捷鍵在絕大多數(shù)版本里都通用哪怕開發(fā)工具選項(xiàng)卡沒顯示VBA編輯器一般也能打開。第二步是設(shè)置宏安全性。Word默認(rèn)會(huì)禁用所有宏彈出一個(gè)安全提示橫幅。我處理的辦法是在開發(fā)工具選項(xiàng)卡里點(diǎn)擊宏安全性然后把設(shè)置改為禁用所有宏并發(fā)出通知這樣文檔打開時(shí)可以手動(dòng)選擇啟用。不建議把安全級(jí)別調(diào)到最低的啟用所有宏那會(huì)讓電腦面對(duì)惡意宏的時(shí)候毫無(wú)防備尤其是你經(jīng)常打開別人發(fā)來(lái)的文檔時(shí)。自己的文檔里的宏臨時(shí)啟用就好。環(huán)境準(zhǔn)備就緒后用Alt F11進(jìn)入VBA編輯器找到左側(cè)工程資源管理器里的Normal項(xiàng)目——當(dāng)然也可以直接操作插入菜單添加一個(gè)新模塊。這個(gè)模塊就是存放宏代碼的容器下面所有示例代碼都放在模塊里寫。1.3 圖片格式相關(guān)的核心對(duì)象與屬性速查VBA操作圖片核心的對(duì)象是InlineShape嵌入型圖片和Shape浮動(dòng)型圖片兩類。中文互聯(lián)網(wǎng)上的很多教程只講其中一類導(dǎo)致你復(fù)制了代碼卻報(bào)錯(cuò)對(duì)象不支持該屬性或方法多半就是兩類對(duì)象搞混了。先說(shuō)InlineShape。這類圖片是嵌在文字行里面的特點(diǎn)是跟隨文字流移動(dòng)對(duì)應(yīng)你在布局選項(xiàng)里選擇的嵌入型環(huán)繞方式。Word文檔里通過(guò)插入圖片插入的圖片默認(rèn)就是InlineShape。再說(shuō)Shape。這類圖片是浮動(dòng)在文字上方的對(duì)應(yīng)四周型緊密型襯于文字下方等環(huán)繞方式。鼠標(biāo)拖進(jìn)來(lái)的圖片、從網(wǎng)頁(yè)復(fù)制過(guò)來(lái)的圖片很多會(huì)變成Shape。批量修改圖片格式時(shí)通常需要同時(shí)遍歷這兩類對(duì)象才能做到一個(gè)不漏。獲取它們的方式很直觀ActiveDocument.InlineShapes是文檔中所有嵌入型圖片的集合ActiveDocument.Shapes是所有浮動(dòng)型圖片的集合。兩者都支持用For Each...Next循環(huán)逐張?zhí)幚怼D片尺寸的核心屬性InlineShape和Shape不太一樣。InlineShape用的是Width和Height單位是磅Point1厘米約等于28.35磅。Shape除了Width和Height之外還有LockAspectRatio屬性控制是否鎖定縱橫比以及WrapFormat.Type屬性控制環(huán)繞方式。這些屬性在下面的代碼示例里都會(huì)用到先有個(gè)印象后面看代碼就順了。注意在Word的VBA里圖片的寬度和高度屬性單位是磅不是厘米也不是像素。這是很多新手第一次跑宏時(shí)遇到數(shù)字對(duì)不上的最常見原因。我在代碼里一般會(huì)直接寫一個(gè)換算函數(shù)把厘米轉(zhuǎn)成磅再用。2. 核心細(xì)節(jié)解析與實(shí)操要點(diǎn)2.1 批量統(tǒng)一圖片尺寸的第一份實(shí)用代碼先給出一份我已經(jīng)在多種版本W(wǎng)ord上測(cè)過(guò)、穩(wěn)定運(yùn)行的代碼。這段代碼解決的是最普遍的需求把文檔里所有嵌入型圖片統(tǒng)一成指定寬度高度隨原圖寬高比自動(dòng)縮放。Sub BatchResizeInlineShapes() Dim targetWidth As Single Dim shapeItem As InlineShape 目標(biāo)寬度單位厘米。改這個(gè)值即可例如12代表12厘米 targetWidth 12 將厘米轉(zhuǎn)換為磅1厘米 28.35磅 targetWidth targetWidth * 28.35 遍歷文檔中所有嵌入型圖片 For Each shapeItem In ActiveDocument.InlineShapes With shapeItem 如果圖片當(dāng)前寬度大于目標(biāo)寬度則縮小小于則放大 .Width targetWidth 高度會(huì)根據(jù)原圖比例自動(dòng)調(diào)整因?yàn)槟J(rèn)鎖定縱橫比 End With Next shapeItem MsgBox 共處理 ActiveDocument.InlineShapes.Count 張嵌入型圖片 End Sub這段代碼的用法很傻瓜化粘貼到模塊里光標(biāo)停在宏內(nèi)部任意位置按F5運(yùn)行即可。運(yùn)行完成后會(huì)彈出一個(gè)消息框告訴你處理了多少?gòu)垐D片。如果沒有彈框而是直接報(bào)錯(cuò)多數(shù)情況是文檔里壓根沒有嵌入型圖片或者你運(yùn)行的是別人文檔里被保護(hù)的宏這兩個(gè)情況后面集中說(shuō)。這里有個(gè)關(guān)鍵邏輯值得展開講為什么只設(shè)置Width不設(shè)置Height因?yàn)镮nlineShape默認(rèn)是鎖定縱橫比的你修改寬度高度會(huì)按原圖比例自動(dòng)跟著變。如果手動(dòng)把Height也寫死圖片就會(huì)變形——我說(shuō)的是那種被橫向拉伸或者縱向壓扁的失真效果。絕大多數(shù)場(chǎng)景下我們只需要統(tǒng)一寬度高度自然縮放就夠了。2.2 同時(shí)處理浮動(dòng)型圖片統(tǒng)一環(huán)繞方式與尺寸嵌入型圖片處理后文檔里還可能有一批浮動(dòng)型圖片。這類圖片的麻煩在于它不跟隨文字流經(jīng)常把正文擋住或者漂浮在頁(yè)面中間。批量處理時(shí)不但要改尺寸還要把它們改成統(tǒng)一環(huán)繞方式。下面的代碼會(huì)把文檔中所有浮動(dòng)型圖片也就是Shape統(tǒng)一為寬度12厘米、四周型環(huán)繞、水平居中Sub BatchFormatFloatingShapes() Dim shapeItem As Shape Dim targetWidth As Single 目標(biāo)寬度單位厘米 targetWidth 12 * 28.35 For Each shapeItem In ActiveDocument.Shapes With shapeItem 跳過(guò)非圖片類型的形狀比如文本框、自選圖形 If .Type msoPicture Or .Type msoLinkedPicture Then Dim newHeight As Single 記錄當(dāng)前寬高比防止圖片被拉伸變形 newHeight targetWidth / .Width * .Height 鎖定縱橫比防止只能改寬度時(shí)被強(qiáng)制拉伸 .LockAspectRatio msoTrue .Width targetWidth .Height newHeight 設(shè)置環(huán)繞方式為四周型 .WrapFormat.Type wdWrapSquare 水平居中 .RelativeHorizontalPosition wdRelativeHorizontalPositionColumn .Left wdShapeCenter End If End With Next shapeItem End Sub這里有一個(gè)特別容易踩的坑ActiveDocument.Shapes集合里面不只是圖片還包括文本框、自選圖形、藝術(shù)字等。如果遍歷時(shí)不加類型判斷你的文本框會(huì)被強(qiáng)行改尺寸甚至改成圖片環(huán)繞方式文檔布局直接亂套。所以代碼里特意用了一個(gè)If判斷只處理類型為msoPicture圖片和msoLinkedPicture鏈接圖片的Shape對(duì)象。這個(gè)習(xí)慣非常重要凡是處理Shapes集合先過(guò)濾類型再動(dòng)手否則后患無(wú)窮。另一個(gè)值得注意的細(xì)節(jié)是高度值的計(jì)算。我并不是直接寫死newHeight等于某個(gè)數(shù)值而是通過(guò)目標(biāo)寬度 / 原始寬度 × 原始高度算出按比例縮放后的新高度。這樣即使圖片原本是豎構(gòu)圖或者方形圖也都不會(huì)變形。雖然理論上設(shè)置鎖定縱橫比之后只改寬度也可以但在Word里實(shí)操發(fā)現(xiàn)部分圖片對(duì)象的LockAspectRatio在代碼運(yùn)行時(shí)并不穩(wěn)定顯式算出高度再賦值更保險(xiǎn)。2.3 批量修改圖片的文字環(huán)繞與對(duì)齊方式還有很多場(chǎng)景需要統(tǒng)一圖片的對(duì)齊方式。比如整篇文檔要求所有圖片水平居中手動(dòng)操作時(shí)你需要逐個(gè)雙擊圖片、打開布局選項(xiàng)、選擇居中步驟繁瑣。用宏只需要一句話級(jí)別的操作。下面這段代碼把文檔中所有浮動(dòng)型圖片統(tǒng)一為上下型環(huán)繞并水平居中。上下型環(huán)繞對(duì)排版來(lái)說(shuō)比較干凈圖片不會(huì)和文字重疊適合說(shuō)明書這類正式文檔Sub UniformImageLayout() Dim shapeItem As Shape Dim inlineItem As InlineShape 處理所有浮動(dòng)型圖片 For Each shapeItem In ActiveDocument.Shapes With shapeItem If .Type msoPicture Or .Type msoLinkedPicture Then .WrapFormat.Type wdWrapTopBottom .RelativeHorizontalPosition wdRelativeHorizontalPositionColumn .Left wdShapeCenter End If End With Next shapeItem 嵌入型圖片無(wú)法直接設(shè)置環(huán)繞方式但可以設(shè)置段落居中 For Each inlineItem In ActiveDocument.InlineShapes inlineItem.Range.ParagraphFormat.Alignment wdAlignParagraphCenter Next inlineItem End Sub看代碼的時(shí)候可以注意一下嵌入型圖片的處理方式和浮動(dòng)型完全不同。嵌入型圖片本身沒有環(huán)繞方式的概念它就在文字流里。想讓嵌入型圖片水平居中本質(zhì)上不是設(shè)置圖片居中而是設(shè)置圖片所在段落的對(duì)齊方式為居中。代碼里用inlineItem.Range.ParagraphFormat.Alignment拿到了圖片所在段落并設(shè)置居中對(duì)齊這是一個(gè)非常經(jīng)典的思路。說(shuō)實(shí)話我在實(shí)際項(xiàng)目里用到最多的就是這三個(gè)宏統(tǒng)一寬度、統(tǒng)一環(huán)繞、統(tǒng)一居中。有了這三個(gè)基本上九成以上的圖片格式亂象都能搞定。3. 實(shí)操過(guò)程與核心環(huán)節(jié)實(shí)現(xiàn)3.1 手把手運(yùn)行第一個(gè)宏的完整流程光有代碼不夠還要知道怎么把它跑起來(lái)。我按步驟說(shuō)一下從新建文檔到宏運(yùn)行成功的全過(guò)程你只要照著走一遍就能通。實(shí)操流程打開一份含圖片的Word文檔先按Ctrl Home回到文檔開頭確保從第一張圖片開始處理。按Alt F11打開VBA編輯器。如果你是第一次打開界面左邊是工程資源管理器右邊是代碼編輯區(qū)。如果左側(cè)沒有顯示工程窗口按Ctrl R調(diào)出來(lái)。在菜單欄點(diǎn)擊插入模塊新建一個(gè)模塊。這個(gè)操作會(huì)創(chuàng)建一個(gè)名為模塊1的代碼容器。把上面的BatchResizeInlineShapes代碼全部復(fù)制進(jìn)去粘貼到代碼編輯區(qū)里。把光標(biāo)定位到代碼中任意位置按F5運(yùn)行或者點(diǎn)擊工具欄上的綠色三角運(yùn)行按鈕。運(yùn)行結(jié)束后會(huì)彈出消息框顯示處理的圖片數(shù)量。如果沒有彈框且沒有報(bào)錯(cuò)說(shuō)明文檔中沒有圖這本身也是一種結(jié)果?;氐絎ord文檔查看圖片是否已經(jīng)統(tǒng)一尺寸。這里有個(gè)運(yùn)行的微技巧如果你的文檔很長(zhǎng)運(yùn)行宏的時(shí)候可以先把視圖切到草稿模式在視圖選項(xiàng)卡切換因?yàn)椴莞迥J较耊ord不需要實(shí)時(shí)重繪所有圖片宏運(yùn)行速度快很多。文檔圖片極多、頁(yè)面又大的時(shí)候這個(gè)操作能明顯縮短等待時(shí)間。3.2 修改宏參數(shù)讓寬度、高度、環(huán)繞方式隨你定制學(xué)會(huì)了運(yùn)行宏接下來(lái)就要學(xué)會(huì)改參數(shù)。很多教程只給了代碼不給改法導(dǎo)致?lián)Q一個(gè)需求就不會(huì)用了其實(shí)改起來(lái)很簡(jiǎn)單。以第一份代碼為例你會(huì)看到一行注釋寫著目標(biāo)寬度單位厘米。改這個(gè)值即可例如12代表12厘米。要改成14厘米就把targetWidth 12改成targetWidth 14然后重新運(yùn)行宏。需要注意的是尺寸值的合理性。Word默認(rèn)頁(yè)面寬度通常是21厘米左右頁(yè)邊距各3.17厘米正文實(shí)際寬度約為14.66厘米。如果你把圖片寬度設(shè)置成15厘米圖片就會(huì)超出正文邊界甚至延伸到頁(yè)邊距里。我個(gè)人經(jīng)驗(yàn)是A4紙的文檔圖片寬度設(shè)置在12到14厘米之間最合適既有視覺存在感又不溢出。如果是A5紙或者自定義頁(yè)面需要重新計(jì)算。環(huán)繞方式的修改在第三份代碼里wdWrapTopBottom就是上下型環(huán)繞。這個(gè)枚舉值可以換成其他類型常用的有這幾個(gè)環(huán)繞類型代碼枚舉值效果四周型wdWrapSquare文字環(huán)繞在圖片四周呈四邊形排布緊密型wdWrapTight文字緊貼圖片不規(guī)則邊緣上下型wdWrapTopBottom圖片獨(dú)占一塊矩形區(qū)域文字在上下方襯于文字下方wdWrapBehind圖片作為背景文字浮于上方浮于文字上方wdWrapFront圖片覆蓋文字適合標(biāo)注用途改的時(shí)候直接把wdWrapTopBottom替換成上面對(duì)應(yīng)的枚舉值就能生效。3.3 批量處理跨章節(jié)長(zhǎng)文檔時(shí)的運(yùn)行策略Word里幾十頁(yè)甚至上百頁(yè)的文檔很常見批量處理圖片時(shí)要注意一個(gè)運(yùn)行策略問(wèn)題宏是在整個(gè)文檔范圍內(nèi)遍歷的但不同章節(jié)的圖片類型可能混雜。有的文檔前面章節(jié)的圖片是直接插入的屬于嵌入型后面章節(jié)的圖片是從網(wǎng)頁(yè)復(fù)制進(jìn)來(lái)的可能是浮動(dòng)型。如果只跑嵌入型圖片的處理宏后面的浮動(dòng)型圖片就不會(huì)被改到。所以實(shí)際項(xiàng)目中我建議的操作順序是先運(yùn)行BatchResizeInlineShapes把嵌入型圖片統(tǒng)一寬度。再運(yùn)行BatchFormatFloatingShapes把浮動(dòng)型圖片統(tǒng)一成相同寬度和四周型環(huán)繞。最后運(yùn)行UniformImageLayout把所有圖的段落對(duì)齊或者位置居中統(tǒng)一下。三步下來(lái)文檔里的圖片基本就服服帖帖了。這個(gè)先嵌入后浮動(dòng)、再統(tǒng)布局的處理順序是我實(shí)踐多次后總結(jié)出來(lái)的能有效避免改完一批發(fā)現(xiàn)另一批沒改的尷尬。如果文檔實(shí)在太長(zhǎng)超過(guò)200頁(yè)且圖片數(shù)量極多宏運(yùn)行時(shí)Word可能會(huì)短暫無(wú)響應(yīng)。這不是死機(jī)是Word在處理圖片重繪。此時(shí)不要亂點(diǎn)鼠標(biāo)等右下角狀態(tài)欄出現(xiàn)就緒字樣即可。要想避免這種卡頓可以在運(yùn)行前先按Ctrl End把光標(biāo)移動(dòng)到文末讓W(xué)ord從文檔末尾開始處理這樣視覺上滾動(dòng)更順暢。當(dāng)然宏本身是從頭遍歷的光標(biāo)位置不影響處理結(jié)果這只是個(gè)減輕視覺卡頓的小技巧。4. 常見問(wèn)題與排查技巧實(shí)錄4.1 運(yùn)行時(shí)錯(cuò)誤91等常見報(bào)錯(cuò)的出現(xiàn)原因與處理方式第一次跑宏報(bào)錯(cuò)是家常便飯。我自己剛開始也經(jīng)常對(duì)著紅色報(bào)錯(cuò)框一臉懵。把最常見的幾個(gè)報(bào)錯(cuò)和處理方法整理成一個(gè)表遇到直接對(duì)照查就行。報(bào)錯(cuò)信息出現(xiàn)原因解決辦法運(yùn)行時(shí)錯(cuò)誤91對(duì)象變量或With塊變量未設(shè)置文檔中沒有圖片或者遍歷到了非圖片類型的Shape確認(rèn)文檔有圖在Shape遍歷中加類型判斷運(yùn)行時(shí)錯(cuò)誤4198命令失敗某些Shape對(duì)象不支持設(shè)置環(huán)繞方式用錯(cuò)誤處理語(yǔ)句跳過(guò)或用On Error Resume Next容錯(cuò)運(yùn)行時(shí)錯(cuò)誤438對(duì)象不支持該屬性或方法把InlineShape的屬性用在Shape上反之亦然確認(rèn)圖片類型使用正確對(duì)象編譯錯(cuò)誤用戶定義類型未定義代碼粘貼不完整或者模塊互相干擾新建一個(gè)模塊重新粘貼完整代碼宏被禁用運(yùn)行時(shí)提示安全警告Word安全策略禁止執(zhí)行宏在開發(fā)工具中臨時(shí)啟用宏或?qū)Ξ?dāng)前文檔啟用宏運(yùn)行后圖片沒變化圖片可能是鏈接圖片或文檔處于兼容模式確認(rèn)文檔是.docx格式鏈接圖片需先嵌入文檔其中最常見的就是運(yùn)行時(shí)錯(cuò)誤91。新手最容易犯的錯(cuò)誤是直接把文檔里所有Shapes都當(dāng)成圖片來(lái)改結(jié)果遍歷到文本框時(shí)報(bào)錯(cuò)。處理方法前面已經(jīng)提過(guò)就是加If .Type msoPicture Or .Type msoLinkedPicture Then判斷。這段代碼我已經(jīng)寫進(jìn)所有涉及Shapes的宏里了你在復(fù)制時(shí)一定不要?jiǎng)h掉這個(gè)判斷。還有一個(gè)容易被忽略的問(wèn)題是文檔的兼容模式。如果你的文檔還是.doc格式VBA里的部分屬性調(diào)用會(huì)報(bào)錯(cuò)。處理辦法很簡(jiǎn)單——先把文件另存為.docx格式再運(yùn)行宏。因?yàn)?docx格式的Word文檔對(duì)VBA的支持更完整很多枚舉值和屬性的行為也更符合預(yù)期。4.2 圖片尺寸沒變化或變形的排查思路如果你運(yùn)行宏之后發(fā)現(xiàn)圖片尺寸紋絲不動(dòng)先別急著懷疑代碼。按照優(yōu)先級(jí)排查看圖片是不是鏈接圖片。某些場(chǎng)景下Word里的圖片是外部鏈接的修改尺寸的屬性對(duì)它無(wú)效。你可以右鍵圖片選擇設(shè)置圖片格式看信息面板里是否有鏈接字樣。如果有先右鍵更改圖片選擇插入和鏈接或者直接在插入前把圖片嵌入文檔??茨愕暮晔遣皇翘幚礤e(cuò)對(duì)象了。如果圖片是浮動(dòng)型你只跑了嵌入型處理宏自然沒變化。用鼠標(biāo)點(diǎn)一下圖片看圖片周圍是黑色小方塊嵌入型特征還是白色圓點(diǎn)浮動(dòng)型特征判斷它屬于哪一類然后跑對(duì)應(yīng)的宏??次臋n是不是受保護(hù)模式。某些從郵件附件或網(wǎng)盤下載的文檔會(huì)進(jìn)入受保護(hù)視圖這種狀態(tài)下宏雖然能運(yùn)行但很多修改操作會(huì)被攔截。關(guān)掉文檔右上角的啟用編輯提示再試試。而圖片變形的問(wèn)題主要是高度被寫死導(dǎo)致的。比如你手動(dòng)設(shè)置了Width 500又把Height 300固定住原圖比例不對(duì)時(shí)就會(huì)變形。解決方式就是我前面代碼里演示的通過(guò)目標(biāo)寬度 / 原始寬度 × 原始高度計(jì)算新高度或者干脆只改寬度鎖定縱橫比。還有一點(diǎn)如果圖片本身因?yàn)橹暗恼`操作已經(jīng)被拉伸變形了宏只是在這個(gè)變形基礎(chǔ)上繼續(xù)改尺寸不會(huì)自動(dòng)恢復(fù)原比例。這時(shí)需要先對(duì)圖片執(zhí)行重設(shè)圖片操作右鍵設(shè)置圖片格式圖片重設(shè)再跑宏。4.3 不同Word版本之間的宏兼容性問(wèn)題Word 2010、2013、2016、2019、365在宏運(yùn)行上大體兼容但細(xì)節(jié)處有差別尤其是中文版和英文版的枚舉值表現(xiàn)。我遇到過(guò)的情況是同一段宏在Word 2016上運(yùn)行正常但在Word 365里處理浮動(dòng)型圖片時(shí)WrapFormat.Type賦值偶爾不生效。究其原因新版Word對(duì)圖片對(duì)象的處理方式更趨向于內(nèi)容控件化某些老式的形狀屬性在新版本里底層邏輯發(fā)生了變化。應(yīng)對(duì)辦法也不復(fù)雜盡量用官方公開的枚舉值進(jìn)行賦值不要寫硬編碼數(shù)字。比如wdWrapSquare這個(gè)枚舉值在有的版本里值是0但你不需要關(guān)心它是不是0直接寫wdWrapSquareVBA會(huì)幫你做版本適配。如果你的文檔需要在多個(gè)同事的不同版本W(wǎng)ord之間傳閱建議給所有涉及圖片的宏加上一個(gè)自適應(yīng)容錯(cuò)語(yǔ)句On Error Resume Next這句代碼的意思是運(yùn)行中遇到錯(cuò)誤時(shí)跳過(guò)這條語(yǔ)句繼續(xù)執(zhí)行下一條而不是彈出報(bào)錯(cuò)框終止運(yùn)行。聽起來(lái)很粗暴但實(shí)際效果是能改的都改了不能改的就跳過(guò)至少保證宏能完整跑完。注意On Error Resume Next要放在宏的開頭且它會(huì)影響整個(gè)宏的錯(cuò)誤處理行為宏結(jié)束后自動(dòng)失效不會(huì)污染別的宏。如果你希望更嚴(yán)謹(jǐn)一些可以配合錯(cuò)誤編號(hào)判斷但對(duì)普通批處理場(chǎng)景直接On Error Resume Next是最省事的選擇。5. 錄制宏與手寫宏的取舍建議5.1 什么時(shí)候用錄制宏什么時(shí)候手寫代碼如果你完全不想碰代碼Word自帶的錄制宏功能可以讓你把手動(dòng)操作錄下來(lái)回放。操作路徑是開發(fā)工具錄制宏然后正常操作一張圖片比如設(shè)置寬度、設(shè)置環(huán)繞方式操作完停止錄制。Word會(huì)把這段操作翻譯成VBA代碼。錄制宏的優(yōu)勢(shì)是用起來(lái)沒有心理門檻劣勢(shì)也很明顯——錄出來(lái)的代碼非常啰嗦而且不會(huì)自動(dòng)做循環(huán)。也就是說(shuō)你錄制的操作只會(huì)作用在一張圖片上不會(huì)自動(dòng)遍歷文檔中的其余圖片。它更適合對(duì)了我不想寫代碼但我需要宏幫我重復(fù)做某一個(gè)精確到鼠標(biāo)點(diǎn)擊的操作的場(chǎng)景比如一次性設(shè)置某張?zhí)厥鈭D片的裁剪參數(shù)。手寫宏看起來(lái)嚇人其實(shí)更適合批量場(chǎng)景。因?yàn)槟阈枰暮诵哪芰褪潜闅v修改這恰恰是錄制宏做不到的。我的個(gè)人建議是先花十分鐘掌握一份代碼的修改方法改寬度、改循環(huán)、改環(huán)繞方式遠(yuǎn)比花半小時(shí)錄制一個(gè)只能作用于單張圖的宏有用。5.2 將宏保存到Normal模板以便后續(xù)所有文檔復(fù)用默認(rèn)情況下你新建的宏放在當(dāng)前文檔的模塊里。這個(gè)宏只對(duì)當(dāng)前文檔有效下次打開新文檔又得重新粘貼一遍非常麻煩。要一勞永逸把宏的存放位置改成Normal.dotm模板。在VBA編輯器里確認(rèn)你的模塊是建在Normal項(xiàng)目下面而不是文檔1項(xiàng)目下面。如果宏在文檔項(xiàng)目里直接把它拖到Normal項(xiàng)目下的模塊里或者直接重新在Normal下新建模塊粘貼代碼。存到Normal模板后所有新建的Word文檔都能用這些宏相當(dāng)于給你的Word裝了一批常駐的批處理工具。我把常用的三個(gè)宏統(tǒng)一尺寸、統(tǒng)一環(huán)繞、統(tǒng)一居中組合成一套放在Normal里之后處理任何文檔基本都是三步跑完收工。有一點(diǎn)提醒Normal模板改動(dòng)后Word退出時(shí)可能會(huì)詢問(wèn)是否保存對(duì)模板的更改選擇是。如果在公共電腦上操作改Normal模板可能涉及權(quán)限問(wèn)題。這時(shí)可以用加載項(xiàng)的方式把宏保存為一個(gè)獨(dú)立的.bas文件下次在任意電腦上通過(guò)文件選項(xiàng)加載項(xiàng)管理:模板轉(zhuǎn)到添加導(dǎo)入效果一樣。5.3 把代碼組合成一個(gè)一鍵處理宏的進(jìn)階思路到了這一步你其實(shí)已經(jīng)掌握了核心方法。如果還想更偷懶可以把三步合并成一個(gè)總宏一鍵處理所有圖片。思路就是把前面三份代碼的內(nèi)容放在同一個(gè)子過(guò)程里用Call調(diào)用即可。把這個(gè)進(jìn)階提升做成一個(gè)整體思路Sub OneKeyFixAllImages() 第1步統(tǒng)一嵌入型圖片寬度 Call BatchResizeInlineShapes 第2步統(tǒng)一浮動(dòng)型圖片尺寸與環(huán)繞方式 Call BatchFormatFloatingShapes 第3步統(tǒng)一圖片對(duì)齊 Call UniformImageLayout MsgBox 全部圖片處理完成 End Sub由于每個(gè)子過(guò)程內(nèi)部都有獨(dú)立的對(duì)象遍歷和錯(cuò)誤處理組合調(diào)用非常安全不會(huì)有變量沖突。運(yùn)行一次OneKeyFixAllImages文檔里的圖片格式就整體規(guī)范了。這種組合思路也能舉一反三。比如你想在統(tǒng)一尺寸的同時(shí)再加一個(gè)所有圖片加0.5磅黑色邊框的需求那就只需要在原代碼里加兩行屬性賦值.Border.Weight 0.5 .Border.Color wdColorBlack這種方法相比一張張手動(dòng)加邊框效率和一致性完全不是一個(gè)量級(jí)。6. 最后一個(gè)實(shí)操環(huán)節(jié)更復(fù)雜的圖片格式調(diào)整宏6.1 批量裁剪圖片到統(tǒng)一尺寸的編碼思路統(tǒng)一寬高比是更進(jìn)階的需求。比如產(chǎn)品手冊(cè)里要求所有截圖寬高比統(tǒng)一為4:3或者干脆統(tǒng)一裁剪到固定像素尺寸這個(gè)用宏也可以實(shí)現(xiàn)但思路和統(tǒng)一寬度完全不同需要在代碼里操作Crop相關(guān)屬性。Sub BatchCropImages() Dim inlineItem As InlineShape Dim targetWidth As Single Dim targetHeight As Single 目標(biāo)尺寸厘米 targetWidth 12 * 28.35 targetHeight 9 * 28.35 For Each inlineItem In ActiveDocument.InlineShapes With inlineItem Dim ratioOriginal As Single Dim ratioTarget As Single ratioOriginal .Width / .Height ratioTarget targetWidth / targetHeight 如果原圖比目標(biāo)更寬則裁剪左右兩側(cè) If ratioOriginal ratioTarget Then Dim cropWidth As Single cropWidth (.Width - targetHeight * ratioTarget) / 2 .PictureFormat.CropLeft cropWidth .PictureFormat.CropRight cropWidth .Width targetWidth .Height targetHeight Else 如果原圖比目標(biāo)更高則裁剪上下兩側(cè) Dim cropHeight As Single cropHeight (.Height - targetWidth / ratioTarget) / 2 .PictureFormat.CropTop cropHeight .PictureFormat.CropBottom cropHeight .Width targetWidth .Height targetHeight End If End With Next inlineItem End Sub這段代碼的原理并不復(fù)雜先比較原圖寬高比和目標(biāo)寬高比如果原圖更寬說(shuō)明多余的部分在兩側(cè)那就裁剪左右如果原圖更高說(shuō)明多余的部分在上下那就裁剪上下。裁完之后直接把尺寸設(shè)置為目標(biāo)值。需要說(shuō)明的是PictureFormat.CropLeft這些屬性的作用單位不是厘米也不是像素而是相對(duì)圖片寬度的百分比但賦值用的數(shù)值范圍需要針對(duì)具體版本測(cè)試。我實(shí)測(cè)下來(lái)在主流Word版本中直接用磅值作為裁剪量計(jì)算會(huì)出現(xiàn)偏差所以上面代碼里的裁剪量寫法是半經(jīng)驗(yàn)寫法建議使用前先復(fù)制一份文檔做測(cè)試。說(shuō)實(shí)話批量裁剪這個(gè)需求如果只是偶爾遇到一次我更建議用Excel或者專業(yè)圖片工具先批量處理圖片文件再插入Word。但如果你的文檔有幾十張圖且全部要求統(tǒng)一比例這個(gè)宏的價(jià)值就立刻體現(xiàn)了。6.2 批量壓縮圖片體積以減小文檔大小文檔體積過(guò)大是另一類頭痛問(wèn)題。幾十張高清截圖能讓docx文件輕松突破幾十兆發(fā)郵件、傳網(wǎng)盤都很不方便。宏也可以做批量壓縮。Sub BatchCompressImages() Dim inlineItem As InlineShape Dim shapeItem As Shape 設(shè)置文檔中所有嵌入型圖片的壓縮選項(xiàng) For Each inlineItem In ActiveDocument.InlineShapes With inlineItem If .Type wdInlineShapePicture Then .ScaleWidth 100 .ScaleHeight 100 End If End With Next inlineItem 對(duì)浮動(dòng)型圖片調(diào)用壓縮命令 For Each shapeItem In ActiveDocument.Shapes With shapeItem If .Type msoPicture Then .ScaleWidth 100 .ScaleHeight 100 End If End With Next shapeItem ActiveDocument.Save End Sub這段代碼本身并不執(zhí)行壓縮而是把圖片的縮放比例重置為100%避免圖片明明很小但文件卻很大的問(wèn)題。真正體積壓縮需要調(diào)用Word自帶的壓縮圖片命令在代碼里實(shí)現(xiàn)是調(diào)ActiveDocument.CompressPictures但不同版本對(duì)這個(gè)函數(shù)的支持不一致所以我不把它作為一個(gè)通用方案推薦。更穩(wěn)妥的做法還是先另存一份文檔菜單文件另存為工具壓縮圖片手動(dòng)選擇目標(biāo)分辨率比如150dpi這是Word內(nèi)置功能兼容性和效果都最穩(wěn)定。宏在這里的主要作用是幫你統(tǒng)一重置縮放和篩選出需要壓縮的大圖。6.3 宏代碼的維護(hù)與安全分發(fā)方法代碼寫好了怎么安全地交給同事或者應(yīng)用到更多電腦上也有講究。這里說(shuō)三個(gè)我實(shí)際用過(guò)的方案。第一個(gè)方案是導(dǎo)出文件分發(fā)。在VBA編輯器中右鍵點(diǎn)擊模塊選擇導(dǎo)出文件會(huì)生成一個(gè).bas文本文件。把這個(gè)文件發(fā)給別人對(duì)方在VBA編輯器里通過(guò)文件導(dǎo)入文件導(dǎo)入即可。這個(gè)方案簡(jiǎn)單直接但.bas文件是純文本任何人打開都能看到代碼內(nèi)容不適合包含敏感邏輯的場(chǎng)合。第二個(gè)方案是F.bas文件合并到某個(gè)全局模板后分發(fā)。比如把宏做進(jìn)一個(gè)格式工具.dotm模板文件對(duì)方打開這個(gè)模板就能使用模板內(nèi)的宏。適合團(tuán)隊(duì)內(nèi)固定的排版需求。第三個(gè)方案是數(shù)字簽名。如果你在公司或者組織內(nèi)部發(fā)布宏正規(guī)做法是給宏工程添加數(shù)字簽名避免分發(fā)時(shí)被安全策略攔截或者被當(dāng)作未知來(lái)源。個(gè)人使用不需要這么復(fù)雜但如果是團(tuán)隊(duì)推廣這一步有意義。提個(gè)醒不要在公共文檔里內(nèi)置宏然后到處傳播尤其是涉及敏感字段的文檔。宏本身是代碼有可能被惡意利用Word的安全設(shè)置擋住宏是有道理的。正規(guī)做法是自己用的時(shí)候臨時(shí)啟用發(fā)給別人的文檔不帶宏把宏單獨(dú)做成模板或者工具分發(fā)。寫在最后我最初開始琢磨這套宏的時(shí)候就是因?yàn)橐环菀话俣囗?yè)的產(chǎn)品手冊(cè)里有上百?gòu)埥貓D有的從微信聊天記錄里復(fù)制有的從PDF轉(zhuǎn)出來(lái)有的直接拖進(jìn)頁(yè)面圖片寬高參差、環(huán)繞方式混亂光靠手工整理花了兩天還沒弄完。后來(lái)用批量處理宏前后迭代了三個(gè)版本才算真正把這個(gè)問(wèn)題解決干凈?,F(xiàn)在再拿到亂圖文檔我心里不慌因?yàn)橹琅芤槐楹昃湍芨愣?。有幾句真心話想?duì)剛開始用宏的朋友說(shuō)不要被VBA三個(gè)字母嚇退。我從寫第一行改寬度的宏到現(xiàn)在中間踩過(guò)的坑大部分都已經(jīng)寫在上面了——對(duì)象類型搞混、單位換算錯(cuò)誤、忘記過(guò)濾Shape類型、兼容模式導(dǎo)致報(bào)錯(cuò)——這些問(wèn)題每個(gè)人都有不是說(shuō)你看一遍教程就能完全避免關(guān)鍵是看完整套排查思路之后再遇到問(wèn)題能自己定位。這只是一個(gè)開始。圖片格式批量處理跑順之后你會(huì)發(fā)現(xiàn)批量設(shè)置標(biāo)題樣式、批量調(diào)整表格邊框、批量生成目錄標(biāo)記這些操作都有對(duì)應(yīng)的VBA方案。Word的自動(dòng)化能力遠(yuǎn)超大多數(shù)人的使用預(yù)期而宏就是打開這扇門的鑰匙。先把這份圖片處理宏真正用起來(lái)等你熟悉了代碼的邏輯結(jié)構(gòu)后面的路會(huì)越走越寬。