也许你要用Word写一本24页的宣传册,但最后数来数去只有21页。当然,你不能让最后3页全部空白,而是应该适当调整文字,使它刚好有24页。
这些问题你经常会遇到。本文的目标就是设计一个插件,实现Word自动扩展和收缩文档,使文档符合指定的打印页数。以前人们常用的WordPerfect软件有这个功能,但Word却从来没有完全实现这个功能。之所以说“没有完全实现”,是因为Word也有一个简单的“减少一页”功能,可以在打印预览工具条上找到,如图一所示,但Word的这个功能无法与WordPerfect的相比。
图一:打印预览工具栏
点击“减少一页”按钮,Word会尝试减小文档中每种字体的大小。它的主要作用是避免将少量内容单独放入一页。按照微软的建议,这个操作最好只用于最后一页包含少量内容的短文档。如果第一次操作没有达到预期的目标,你可以重复执行“减少一页”命令,但最后可能得到一个字体小得难以接受的文档。
相比之下,WordPerfect的这个功能灵活得多。首先,它既可以收缩文档,也可以扩展文档,只要目标页数和当前页数的差异不超过50%即可。第二,你可以选择通过哪些途径使文档符合指定的页数要求,除了改变字体之外,你还可以调整行距以及页的上、下、左、右边距。WordPerfect会根据用户指定的选项分析当前文档,自动修改文档格式,使文档符合指定的页数要求。
图二:自动调整文档页数
本文的目标是模仿WordPerfect,在Word中实现文档页数自动调整功能。调整页数的界面如图二所示,用户可以选择调整哪些项目、调整的范围,然后点击“确定”按钮开始自动调整。当然,实现这种功能需要用到Word宏,也就是要用VBA编程。不过本文的代码并不是特别复杂,只要了解一些基础知识,就很容易理解本文的代码。本文的插件适用于Word 2000和XP,文章中凡是涉及菜单名字、对话框的地方,都以Word XP为标准。
一、调整文档占用空间的基本手段
Word提供了七个改变字体大小和调整行距的内建命令,所有这些命令都可以用等价的VBA代码表示。在图三中,我把这些命令和“减少一页”集中到了一个自定义的工具栏上。
图三:调整文档占用空间的内建命令
如果你也想得到这个工具栏,可按如下步骤设置:选择菜单“工具->自定义”,选择“工具栏”选项卡,新建一个工具栏,将它命名为“扩展与缩减文档页数”,然后选择“自定义”对话框的“命令”页,在“类别”列表中选择“所有命令”,接着从“命令”列表把各个命令拖到新建的工具栏上就可以了。
1.1 调整字体
首先来看第一个命令“减少一页”。在Word宏中,该命令对应的VBA指令是ActiveDocument.FitToPages。需要注意的是,调用该指令的代码必须有健全的错误处理机制,因为如果 Word 无法将文档的页数减少一页,则该方法会导致出错。Word的帮助只提到了编号为5538的错误“Word无法按一页缩小文档,因为此文档只有一页”。但在实际应用中,出现另一个编号为5539的错误可能性更大。5539错误的意思是“经过数次尝试,Word无法按一页来缩小文档”。据测试,只要文档的字体被缩小到6磅,Word会提示这个错误,也许Word认为这是可以保证文档清晰可读的最小字体,所以不再继续缩小字体。
“缩小字体”和“增大字体”两个命令分别对应Selection.Font.Shrink和Selection.Font.Grow,这两个方法可用来调整选中区域的字体大小。Shrink方法把字体缩小一级,如果用户选中的内容字体大小不一,则每种字体大小分别被缩减一级。Grow方法的作用恰好相反。“一级”这个概念的含义必须说明一下。如果文字的当前大小是20,则比它大一级不是21,而是22;比20小一级的不是19,而是18。也就是说,Word对文字大小级别的解释与“格式”工具栏上列表框的值对应,如图四所示。
图四:Word的字体分级
仔细观察图四的列表,每一级之间的距离没有任何规律,有的距离大,有的距离小。微软没有解释这么安排的理由,但可以猜想,如果一段文字既有标题也有正文,用Shrink和Grow方法调整大小时,这种分级安排有利于维持某种平衡。
用FitToPages和Shrink方法缩减文档页数时,应注意两者的工作方式有所不同。Shrink按照预定义的级别缩减字体大小,字体可以小到1磅。FitToPages不同,它按照0.5磅的精度精确调整字体大小,但不允许正文字体小于6磅。
VBA没有提供直接与图三“将字体缩小1磅”、“将字体增大1磅”对应的命令。如果用记录宏的办法获取这两个按钮对应的代码,可得到如下结果:
如果选中一段包含多种字体大小的文字,然后点击“将字体缩小1磅”按钮,Word会自动根据原有的字体大小,分别将它们减小1磅。但是,如果将这个操作记录成宏,然后再重新执行这个宏,Word会提示错误“运行时错误4120,参数无效”。有兴趣的读者,可在VB调试器中分析代码的运行过程。对于“将字体增加1磅”按钮,情况也完全一样。可见,对于这两个命令,VBA不支持包含多种字体大小的情形!
怎么办呢?我们知道,点击“将字体缩小/增加1磅”按钮不会因为存在多种字体大小而出现问题。因此,只要我们模拟按钮点击动作,就可以绕过Selection和Font对象。
每一个Office的CommandBar按钮都有一个数值型的ID属性,“将字体缩小1磅”按钮的ID是310,“将字体增加1磅”按钮的ID是311。只要有了按钮的ID,就可以用CommandBars属性的FindControl方法创建按钮对象,然后利用Execute方法执行与CommandBar关联的操作。也就是说,用下面的代码可以执行“将字体缩小1磅”和“将字体增加1磅”命令:
1.2 调整行距
下面来看看如何模拟WordPerfect第二类调整文档空间的方法,即调整行距。Word有三个内建的命令来调整行距(各个行之间的垂直距离),如图三最下方的三个按钮所示,它们对应的VBA代码是:Selection.ParagraphFormat.Space1,Selection.ParagraphFormat.Space15,Selection.ParagraphFormat.Space2。
Space1方法为指定段落设置单倍行距,准确的间距将取决于各行内字符最大的字号。Space15方法为指定段落设置1.5倍行距,即各段内字符的最大字号加上6磅;Space2方法设置2倍行距,即各段内字符最大的字号加上12磅。
但是,利用这些方法来调整行间距不是最好的办法,因为对于需要精确的场合,它们实在显得太粗糙了。使用“格式->段落”菜单可以更精确地控制行距,如图五所示:
图五:指定行距的六种规则
“段落”对话框允许按照六种不同的规则指定行距。如果指定了后面三种规则之一,还必须指定“设置值”。与此对应,在Word VBA中,行距是由两个属性控制的:LineSpacingRule和LineSpacing。Paragraph、ParagraphFormat、Paragraphs集合对象都提供了这些属性。行距的规则通过LineSpacingRule属性值设定,可以指定的常量值包括:wdLineSpaceSingle、wdLineSpace1pt5、wdLineSpaceDouble、wdLineSpaceAtLeast、wdLineSpaceExactly和wdLineSpaceMultiple。如果把规则设置成后面三个常量值之一,同时还必须设定LineSpacing属性。LineSpacing属性用来返回或设置指定段落或范围的行距,单位是磅。要在文档中扩展或收缩文字,最好把LineSpacingRule属性设置成wdLineSpaceMultiple。这样,我们可以让行距总是和段落中最大字体的大小有关。例如下面的代码将当前文档所有段落的行距增加百分之三:
With ActiveDocument.Paragraphs
.LineSpacingRule = wdLineSpaceMultiple
.LineSpacing = LinesToPoints(1.03)
End With
这里用到了LinesToPoints方法。这个方法用来把度量单位从行转换为磅(1行=12磅),返回值是Single类型。按照这种方法,行距值可以调整到相当精确的程度。但应当注意的是,Word会以二十分之一磅为单位调整最终的行距。例如在上面的例子中,最终的行距不是12.36磅(1.03 X 12),而是12.35磅。
为了便于阅读,最好不要将行距缩小到90-92%(约11磅)以下。如果小于这个值,行的高度可能不够,某些文字的顶端或末端可能被切割。但是,如果要扩展一个文档,这种办法是非常理想的,不仅效果显著,而且永远不会影响文档的可读性。
1.3 页边距
WordPerfect允许修改页四边的空白距离,下面来看看如何在Word中作类似的调整。不过首先需要注意的是,无限制减小页边距不一定安全,因为可能与打印机支持的边距范围产生冲突。另外,如果文档包含页眉、装订线等,调整整个文档的边距之后可能会出现意外的结果。
在Word VBA中,与四种边距对应的TopMargin、BottomMargin、LeftMargin和RightMargin属性属于PageSetup对象,属性的类型是Single,可用来设置或返回页边与文字边缘的距离,单位是磅。如果你不习惯用磅作为计量单位,可以借助转换函数使用自己熟悉的单位,例如英寸。例如,下面的例子中,InchesToPoints函数把1.5英寸转转成108磅(1英寸等于72磅):
With ActiveDocument.PageSetup
.LeftMargin = InchesToPoints(1.5)
.RightMargin = InchesToPoints(1.5)
End With
PageSetup对象属于Section对象。也就是说,如果一个文档包含多个节(Section),就可以提取出多个PageSetup对象,每一个PageSetup对象可能有不同的边距。所以,如果文档包含多个节且各个节的左边距不同,下面的代码不能得到正确结果,它将返回wdUndefined值(9999999):
Debug.Print ActiveDocument.PageSetup.LeftMargin
如果要调整整个文档的边距,就必须分别处理各个节的边距,如下面的例子所示。这段代码首先获取各个节的当前左、右边距,然后把它们缩小20%:
Dim ObjSection As Section
For Each ObjSection In ActiveDocument.Sections
With ObjSection.PageSetup
.LeftMargin = .LeftMargin * 0.8
.RightMargin = .RightMargin * 0.8
End With
Next
二、实现自动调整功能
前面分析了在VBA中调整文档占用空间的各种途径。利用这些技术可以编写出下面的DocSizer类,实现调整文档页数的核心功能。
DocSizer类主要提供了10个属性和一个Execute方法。10个属性的含义如表一所示。
表一 | |
属性 | 说明 |
AdjustItems | 可选,Long。指定可以通过哪些项目来调整文档占用的空间。允许使用下列枚举常量的任意组合:adjFontSize(1),adjLineSpacing(2),adjMarginLeft(4),adjMarginRight(8),adjMarginTop(16),andadjMarginBottom(32)。如果忽略,则默认是adjAll常量(63),即前面6个选项的组合。 |
NumTargetPages | 必需,Long,可读写。指定目标页数,与现有页数的差距不能超过50%。 |
MinLeftMargin MinRightMargin MinTopMargin MinBottomMargin | 可选,Single。用来指定必须保留的最小边距,可用来防止边距缩小得太多以至于超出打印机允许的范围。如果忽略,默认保留的最小边距是文档第一节边距的70%。 |
MinFontSize | 可选,Single。指定“正文”样式的字体最小可以调整到多少。如果忽略,则默认最小允许的字体是6磅。 |
MinLineSpacing | 可选,Single。指定“正文”样式中的行距最小可以调整到多少。如果忽略,则默认11磅(相当于一行的92%)。如果行距小于11磅,字符可能被切割。 |
NumCurrentPages | 只读,Long。返回文档初始的页数。执行Execute方法之后,返回值将是调整后的页数。 |
UndoAfterFailure | 可选,Boolean。如果设置成True,一旦调整之后未能达到预定的页数要求,则撤销所有对文档格式的修改,恢复文档的原始状态。默认值是True,如果要保留调整后的结果(即使页数不能达到预定的要求),则应当显式地把该属性设置成False。 |
Execute方法启动调整文档页数的操作,如果能够达到预期的页数要求,返回True;否则返回False。另外,Execute方法还可能返回错误,利用Err.Description可获得错误的描述信息。下面是DocSizer的主要代码:
'公用变量,可读写的属性
Public NumTargetPages As Long '目标页数
Public UndoAfterFailure As Boolean '调整失败后恢复
'可调整的项目
'包括:字体,行距,左、右、上、下边距
Public Enum adjItems
adjFontSize = 1
adjLineSpacing = 2
adjMarginLeft = 4
adjMarginRight = 8
adjMarginTop = 16
adjMarginBottom = 32
End Enum
'全部项目都可以调整
Const ADJ_ALL = adjFontSize Or adjLineSpacing Or adjMarginLeft Or _
adjMarginRight Or adjMarginTop Or adjMarginBottom
'错误信息
'错误信息常量声明,略...
Const ERR_MSG_OPERATION_SUCCESSFUL = "操作成功!该文档现在包含预定的页数。"
Const ERR_MSG_SHRINK_FAILED = "错误!无法把该文档缩减到预定的页数。"
Const ERR_MSG_STRETCH_FAILED = "错误!无法把该文档扩展到预定的页数。"
Const ERR_MSG_TARGETPAGES_OVERFLOW = "错误!目标页数与现有页数的差距不能超过50%。"
Const ERR_MSG_TARGETPAGES_SAME_AS_CURRENT_PAGES = "错误!目标页数与现有页数完全一样。"
Const ERR_MSG_NO_TARGETPAGES = "错误!没有指定目标页数。"
'默认允许的最小字体
Const MIN_FONTSIZE As Single = 6
'默认允许的最小行距
Const MIN_LINESPACING As Single = 11
'允许将页边距缩小到多少(百分比)
Const MIN_MARGINS_PERCENTAGE As Single = 0.7
Const DEF_MARGIN_ADJUSTMENT As Long = 12
'声明其他对象型变量,略...
'======类的属性和方法=======
'类初始化
Private Sub Class_Initialize()
If Documents.Count Then
'用于撤销已执行的格式修改操作
Set objUndoList = CommandBars.FindControl(ID:=128)
End If
'默认修改失败后恢复
UndoAfterFailure = True
End Sub
Private Sub Class_Terminate()
Set objUndoList = Nothing
End Sub
Property Get CurPageCount() As Long
'获得文档的当前页数
If Documents.Count Then
CurPageCount = ActiveDocument.ComputeStatistics(wdStatisticPages)
End If
End Property
Private Property Get sDefaultLineSpacing()
'返回"正文"样式的行距,磅
With ActiveDocument.Styles(wdStyleNormal).ParagraphFormat
'行距规则?
Select Case .LineSpacingRule
Case wdLineSpaceMultiple
sDefaultLineSpacing = .LineSpacing
Case wdLineSpaceSingle, wdLineSpace1pt5, wdLineSpaceDouble ' 0, 1,或 2
sDefaultLineSpacing = (.LineSpacingRule + 1) * 12
Case Else '忽略wdLineSpaceExactly和wdLineSpaceAtLeast
sDefaultLineSpacing = 12
End Select
End With
End Property
Private Property Get DefaultFontRange() As Range
'找到一个按照"正文"样式格式化的Range。
'缩小字体时,查看该Range就可以知道字体缩小
'到了什么程度
If Documents.Count Then
With Selection
Set objOriginalSel = .Range
With .Find
.ClearFormatting
.Font.Size = ActiveDocument.Styles(wdStyleNormal).Font.Size
.Text = vbNullString
.Forward = True
.Wrap = wdFindContinue
.Format = True
If .Execute Then
Set DefaultFontRange = Selection.Range
Else
Set DefaultFontRange = Nothing
End If
End With
objOriginalSel.Select
End With
End If
End Property
Private Property Get nCurUndoItems()
'返回‘常用’工具栏‘撤消’列表的项目数
If IsObject(objUndoList) Then
With objUndoList
If .Enabled Then nCurUndoItems = .ListCount
End With
End If
End Property
Function Execute() As Boolean
'根据设定的参数,将文档页数调整到预定的数量
On Error Resume Next
nInitialPages = CurPageCount '初始页数
'‘撤消’列表的当前项数
nInitialUndoItems = nCurUndoItems
'默认允许以所有手段调整文字占用的空间
If nAdjustItems = 0 Then nAdjustItems = ADJ_ALL
'是否允许调整边距
bAdjustAnyMargin = CBool(nAdjustItems And adjMarginLeft) Or _
CBool(nAdjustItems And adjMarginRight) Or _
CBool(nAdjustItems And adjMarginTop) Or _
CBool(nAdjustItems And adjMarginBottom)
'根据目标页数的不同,执行不同的操作(缩减页数或扩展页数)
Select Case NumTargetPages
Case 0
'没有指定目标页数
Err.Raise ERR_NO_TARGETPAGES, , ERR_MSG_NO_TARGETPAGES
Case Is > nInitialPages * 1.5, Is < (nInitialPages + 1) \ 2
'目标页数与现有页数的差距不能超过50%
Err.Raise ERR_TARGETPAGES_OVERFLOW, , ERR_MSG_TARGETPAGES_OVERFLOW
Case Is < nInitialPages
'缩减页数
ShrinkToFit
Execute = (CurPageCount = NumTargetPages)
If Execute = True Then
'缩减页数成功
Err.Raise ERR_OPERATION_SUCCESSFUL, , ERR_MSG_OPERATION_SUCCESSFUL
Else
'缩减页数失败
Err.Raise ERR_SHRINK_FAILED, , ERR_MSG_SHRINK_FAILED
'是否恢复到调整页数之前的原始文档?
If UndoAfterFailure Then UndoAll
End If
Case Is > nInitialPages
'扩展页数
StretchToFit
Execute = (CurPageCount = NumTargetPages)
If Execute = True Then
'扩展页数成功
Err.Raise ERR_OPERATION_SUCCESSFUL, , ERR_MSG_OPERATION_SUCCESSFUL
Else
'扩展页数失败
Err.Raise ERR_STRETCH_FAILED, , ERR_MSG_STRETCH_FAILED
'是否恢复到调整页数之前的原始文档?
If UndoAfterFailure Then UndoAll
End If
Case Else
End Select
If IsObjectValid(objOriginalSel) Then objOriginalSel.Select
'刷新屏幕
Application.ScreenRefresh
End Function
Private Sub StretchToFit()
'扩展文档页数
With ActiveDocument
'如果允许调整字体...
If CBool(nAdjustItems And adjFontSize) Then
Do Until bFontDone
'增大字体
.Range.Font.Grow
'分析页数
nCurPages = CurPageCount
'当前页数是否等于目标页数?
If nCurPages = NumTargetPages Then
' 已经达到目标页数
Exit Sub
ElseIf nCurPages > NumTargetPages Then
'页数太多了。必须撤消最后一次修改操作
.Undo 1
bFontDone = True
Else
'文档页数仍旧太少,继续扩展文档
End If
Loop
End If
'调整行距,略...
'调整边距,略...
End With
End Sub
Private Sub ShrinkToFit()
'缩减文档页数
With ActiveDocument
'如果允许调整字体...
If CBool(nAdjustItems And adjFontSize) Then
Dim sCurFontSize As Single
'确定最小字体
If sMinFontSize = 0 Then sMinFontSize = MIN_FONTSIZE
Dim rNormalFont As Range
Set rNormalFont = DefaultFontRange
Do Until bFontDone
' FitToPages执行失败会出现5538和5539错误
On Error Resume Next
.FitToPages
Select Case Err
Case 5538, 5539
' FitToPages失败,结束循环
Err.Clear
bFontDone = True
Case 0 'FitToPages执行成功
'文档是否包含用‘正文’样式格式化的文字?
If IsObjectValid(rNormalFont) Then
'它当前的字体大小是多少?
sCurFontSize = rNormalFont.Font.Size
'如果字体已经达到允许的最小极限,则结束循环
If sCurFontSize < sMinFontSize Then
.Undo 1
bFontDone = True
End If
Else
'文档不包含‘正文’样式大小的文字。字体可能被调整到6
'磅(FitToPages的下限)
End If
Case Else '其他不能确定的异常
Err.Clear
bFontDone = True
End Select
'分析页数
nCurPages = CurPageCount
'当前页数是否等于目标页数?
If nCurPages = NumTargetPages Then
'已经达到目标页数
Exit Sub
ElseIf nCurPages < NumTargetPages Then
'页数太少了。如果不允许调整行距,撤消最后的操作
If Not CBool(nAdjustItems And adjLineSpacing) Then
.Undo 1
End If
bFontDone = True
Else
'页数仍旧太多,继续缩减页数
End If
Loop
End If
'调整行距,略...
'调整边距,略...
End With
End Sub
Private Sub UndoAll()
'撤消所有调整页数的操作,略...
End Sub
可以看到,扩展页数的过程(StretchToFit)比较简单,缩减页数的过程(ShrinkToFit)稍微复杂一点。代码的主要思路是:每次调整字体大小、行距或边距之后,检查一下CurPageCount属性,看看是否已经达到目标页数。如果已经达到,调整过程结束,Execute方法返回True;如果调整之后页数变得太多(或太少),例如缩小字体之后页数小于目标页数,则撤消最后一次操作,尝试另一种调整页数的办法。当所有调整页数的办法都已经试遍,而页数仍未能达到要求,则Execute返回False。
建议至少用字体和行距两种办法调整文档的页数,这样可以大大增加调整成功的可能性。当然,不能排除未能达到目标页数的情形出现,导致这种情形的主要原因可能是文档包含太多手工插入的分页符、大型图片,或者是因为目标页数和现有页数的差距太大。
三、设计用户界面
前面介绍了调整文档页数的核心思路和代码。下面来看看制作用户界面的过程。用户界面主要包括两部分:菜单和对话框。图六是加入到“文件->打印预览”前面的“调整页数”菜单。
图六:“调整页数”菜单
“调整页数”菜单包括三个选项。第一个选项允许指定目标页数,然后根据默认的参数执行调整操作。第二个选项等同于Word的“减少一页”功能,但原来这个功能只能在打印预览状态下使用。第三个选项显示出图二的对话框,用户自定义调整页数的参数,然后点击“确定”按钮开始调整页数。如果用户试图调整文档页数时,文档没有保存,会出现图七的提示。
图七:调整页数之前保存文档
设置菜单的代码和其他代码一起保存在一个插件模板DocSizer.dot之中。DocSizer.dot的主要内容如图八所示。其中:
图八:DocSizer模板的主要内容
DocSizer.dot必须放入Word的自动启动目录。对于Windows 2000/XP和Office 2000/XP,启动目录通常是\Documents and Settings\<用户名字>\Application Data\Microsoft\Word\STARTUP。Word会自动加载该目录下的模板,这样我们就可以在Word启动时加入“调整页数”菜单。
因篇幅关系,这里不再介绍Context类模块和Interface模块的代码,但你可以参见本文的附件DocSizer.dot,代码中包含了详细的注释。将DocSizer.dot安装到Word的启动目录下,启动Word就可以看到“文件->打印预览”前面增加了一个“调整页数”菜单。启动目录的具体位置,可查看Word菜单“工具->选项”再选择“文件位置”。如果你要查看DocSizer.dot的完整代码,先按正常方式打开它,然后选择菜单“工具->宏->宏”,再选择“编辑”即可。
作为一种通用软件,Word无法照顾到每一个用户的特殊要求和使用习惯。但是,Word提供了VBA编程支持和完善的对象模型。正如本文例子所显示的,利用这些技术,我们可以随心所欲地定制Word,提高工作效率。
相关视频
相关阅读 Mac访问Windows共享文件夹Windows 7正版系统验证方法windows 8.1系统版本号查看方法Windows 8.1系统电话激活时无法输入微软返回代码解决方法Windows 8如何调整屏幕分辨率windows8.1磁盘占用100%解决方法Mac双系统如何删除Boot Camp安装的Windows分区Apple教你如何在Mac 上运行 Windows
热门文章 毕业论文格式设置图文Word 2010 的十大优点
最新文章
Word拼音怎么打?Word给毕业论文格式设置图文
Word无法读取文档,文档可能损坏怎么办word统一图片大小实例图文教程Word中批量修改图片大小和缩放比例方法毕业论文格式设置图文教程
人气排行 下划线怎么打?是哪个键?Word里输入乘号和除号的6种方法如何让word生成的目录显示4级标题并自动缩进word中表格允许跨页断行不能选择是灰色在Word2003文档中怎么分栏设置怎么制作红头文件Word中批量修改图片大小和缩放比例方法Word无法启动转换器mswrd632的解决办法
查看所有0条评论>>