ImageMagick 示例 —— 文本处理
创建文本标签,或者向图像添加文本,大概是 ImageMagick 最基本、最常用的操作之一。它也是最简单的操作之一,但仍有产生非常精彩结果的空间。因此,这里是开始探索 IM 各项能力的好起点。
ImageMagick 中的文本操作符
ImageMagick 有很多不同的方式可以在图像中绘制文本,这凸显了这个图像处理库的多功能性。本页详细介绍绘制文本的具体方法和样式。在学习这些示例时你必须记住,ImageMagick 主要是一个图像转换器和修改器。因此,所提供的每种方法都是简单的文本绘制操作符,例如向图像添加标签和版权信息。参见为图像添加注释。所有文本操作符也都理解并使用一组标准的文本处理设置,例如要使用的 "[-font](https://imagemagick.org/command-line-options/#font)"、"[-pointsize](https://imagemagick.org/command-line-options/#pointsize)"。还有 "[-fill](https://imagemagick.org/command-line-options/#fill)" 颜色设置,以及在更复杂的文本绘制中使用的 "[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)"、"[-stroke](https://imagemagick.org/command-line-options/#stroke)" 和 "[-undercolor](https://imagemagick.org/command-line-options/#undercolor)" 颜色。在你确实需要创建新图像的情形下,例如标签和说明文字,"[-background](https://imagemagick.org/command-line-options/#background)" 颜色设置也会被用到。最后是较新的 "[-kerning](https://imagemagick.org/command-line-options/#kerning)" 和 "[-interword-spacing](https://imagemagick.org/command-line-options/#interword-spacing)" 修饰符。ImageMagick 不是 一个完整的格式化文本和文档处理器。如果你需要繁重的文本处理,最好使用一个完整的交互式字处理软件,或者像 "TeX" 这样的批量文本排版工具(或它的某个变体(参见下文的一套完整的文本处理系统)。这些程序的输出(通常是 postscript 格式)随后可以被转换成图像,并由 ImageMagick 进一步修改。也就是说,要用合适的工具做合适的事。话虽如此,一些混合字体处理还是可以做到的。作为切入点,请看本页底部附近的创建混合字体样式的文本行。现在,让我们来看看把文本变成图像的基本方式。稍后在下一节(复合字体)中,我们将探讨如何生成一些有趣的字体效果。
Label —— 简单文本标签
基本标签
使用 "label:" 图像创建一个字体图像,是在 ImageMagick 中快速绘制字体的较为典型的方式。最大的优点是它会根据当前的 "[-background](https://imagemagick.org/command-line-options/#background)" 和 "[-fill](https://imagemagick.org/command-line-options/#fill)" 颜色设置生成它自己的画布,其尺寸会匹配所绘制的文本。例如,下面是一个典型的生成标签。
magick -background lightblue -fill blue \
-font Candice -pointsize 72 label:Anthony \
label.gif
上面这个大概是 label 最典型的用法,通过字体选择和 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)" 来定义结果。但它绝对是生成文本标签最没意思的方式。 |
生成的 'label:' 图像还会把 'label' 图像属性元数据设置为同一字符串。某些文件格式,例如 MIFF 和 PNG,会保存该特定属性,并可在之后的图像处理程序中使用。关于使用 'label' 元数据的示例,参见使用已保存元数据的 Montage。 |
|---|---|
如果你同时指定一个 "[-size](https://imagemagick.org/command-line-options/#size)",那么生成的 label 图像将以该尺寸创建。 |
magick -background lightblue -fill blue -font Candice \
-size 165x70 -pointsize 24 label:Anthony label_size.gif
![[IM Output]](../static/img/text/label_size.gif)
你也可以使用 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 来设置 label 在那个更大的框中的位置。 |
magick -background lightblue -fill blue -font Candice \
-size 165x70 -pointsize 24 -gravity center \
label:Anthony label_gravity.gif
![[IM Output]](../static/img/text/label_gravity.gif)
当然,如果你没有为 label 设置 "[-size](https://imagemagick.org/command-line-options/#size)",那么生成的 "label:" 中将没有多余空间可供 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 使用,这就让它变得相当无用了。同时使用 "[-size](https://imagemagick.org/command-line-options/#size)" 和 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)" 的问题在于,文本可能会“溢出”所指定的图像尺寸。 |
magick -background lightblue -fill blue -font Candice \
-size 165x70 -pointsize 72 -gravity center \
label:Anthony label_overflow.gif
![[IM Output]](../static/img/text/label_overflow.gif)
| 在 6.5.2-4 版本之前,如果同时给出 "[-size](https://imagemagick.org/command-line-options/#size)" 设置,IM 会完全忽略 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)" 设置。这会导致上面图像中的文本按“最佳适配”处理自动调整大小(参见下一组示例)。
---|---
最佳适配图像
使用 label 生成特定 "[-size](https://imagemagick.org/command-line-options/#size)" 图像的最大技巧,是不要为该 label 指定 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)"。这样一来,IM 就能自由地尝试选择一个 最佳适配 所请求图像尺寸的字体大小。也就是说,所绘制的文本会被调整以适应给定的尺寸! |
magick -background lightblue -fill blue -font Candice \
-size 165x70 label:Anthony label_size_fit.gif
![[IM Output]](../static/img/text/label_size_fit.gif)
正如你所见,通过设置一个 "[-size](https://imagemagick.org/command-line-options/#size)" 设置,图像右侧或下方可能会出现一些多余空间。 | 当 IM 创建一个“最佳适配”的 label 时,它实际使用的点大小也会被保存到 'label:pointsize' 图像属性中,让你之后可以使用该信息。这一功能在 IM v6.6.2-7 中添加,源自论坛讨论点大小报告。
---|---
你仍然可以通过调整 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 设置来调整 label 在那块多余空间中的位置。 |
magick -background lightblue -fill blue -font Candice \
-size 165x70 -gravity center label:Anthony label_size_gravity.gif
![[IM Output]](../static/img/text/label_size_gravity.gif)
当然,如果你没有为 label 设置 "[-size](https://imagemagick.org/command-line-options/#size)",那么生成的 "label:" 中将没有多余空间可供 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 使用,所以只有当你要求图像为特定尺寸时它才有意义。现在是最好的消息。如果你给出的 "[-size](https://imagemagick.org/command-line-options/#size)" 设置只包含 label 的宽度或高度,那么字体将被调整以最佳适配那个给定的维度。另一个未指定的维度随后会被自动调整以适应该文本! |
magick -background lightblue -fill blue -font Candice \
-size 160x label:Anthony label_size_width.gif
![[IM Output]](../static/img/text/label_size_width.gif)
基本上这意味着上面的 "label:" 将始终为 160 像素宽,并使用适合该宽度的最大字体大小。label 的高度随后会被调整以适配。如果指定了高度而不是宽度,也会做同样的事。 |
magick -background lightblue -fill blue -font Candice \
-size x40 label:Anthony label_size_height.gif
![[IM Output]](../static/img/text/label_size_height.gif)
这个 label 是 40 像素高,文本未定义的点大小被调整以适应该高度,然后未定义的宽度被设置为适应所绘制的文本。正如你所预期的那样。当然在这种情况下,同样几乎没有多余空间供任何 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 设置施展。
跨多行的标签
"label:" 生成器(从 IM 版本 6.2.5 起)可以生成多行 label。 |
magick -background lightblue -fill blue -font Ravie -pointsize 20 \
label:'ImageMagick\nRules - OK!' label_multiline.gif
![[IM Output]](../static/img/text/label_multiline.gif)
正如你所见,"label:" 理解使用 '\n' 表示换行。这意味着你可能需要预先处理输入文本,确保在命令行上放置数据时任何特殊字符都被转义。更多细节参见下文的文本参数中的特殊转义字符。由于 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 也会影响 "label:" 的生成(从 IM 版本 6.2.6 起),你可以用它来“对齐”多行 label。 |
magick -background lightblue -fill blue -font Corsiva -pointsize 24 \
-gravity center label:'ImageMagick\nExamples\nby Anthony' \
label_centered.gif
![[IM Output]](../static/img/text/label_centered.gif)
IM 的一个重要特性是它可以从文件读取要使用的文本数据。做法是在文件名前加上一个“at”字符 '@',并将其用作字符串参数。例如,这里我们从我工作站的 'message of the day' 文件创建一个 label……
magick -background lightblue -fill blue \
label:@/etc/motd label_file.gif
你也可以从标准输入管道读取 label 的文本。例如这里我把一个名言生成器的输出变成一个多行 label。
mesgs ImageResolution |\
magick -background lightblue -fill blue \
label:@- label_file_multiline.gif
注意我使用的文件名只是一个 '-' 字符。这意味着文件将从标准输入读取。记住你可以使用 '@_filename_' 把任意命令行字符串参数读入 IM。这包括下面给出的所有其他文本输入方法。但是它只能用来替换整个字符串参数,而不能替换字符串参数的一部分。还要注意在上面的示例中,label 图像里多了一个空白行。这个空白行是由输入文本文件末尾的一个换行符造成的。除非你设法去除输入文件末尾的换行符(参见下文的 caption: 示例了解修复方法),否则从文本文件读取时 "label:" 总会有这个空白行。 |
大多数较旧版本的 IM(v6.2.5 之前)不处理多行 label。在这些版本中,这些行会被拼接在一起,形成单独的一行非常非常长的行。 |
|---|---|
竖排标签
当然你也可以向输入文本本身添加换行。例如,这里我取一个简单的单词,在每两个字母之间添加一个换行,以创建一些居中的竖排文本。 |
echo -n "Vertical" | sed 's/./&@/g; s/@$//' | tr '@' '\012' |\
magick -background lightblue -fill blue -font Ravie -pointsize 24 \
-gravity center label:@- label_vertical.gif
注意 "sed" 命令在每个字符后添加一个 '@' 字符,但字符串末尾除外。"tr" 随后把 '@' 字符替换为换行。它还假设输入文本不以换行结尾,否则会在生成图像的底部添加一个额外的空白。 ![[IM Output]](../static/img/text/label_vertical.gif)
运行 linux、因而使用 GNU 版 "sed" 命令的用户可以去掉 "tr",并在 sed 命令中把 '@' 替换为 '\n',这样它会直接在每个字符之间插入换行。另请参见特殊属性行间距,可用于调整字符之间的间距。
Caption —— 自动换行的标签
从文本输入生成的 "caption:" 图像,在大多数方面与 "[label:](#label)" 完全一样,区别在于:它不会扩大文本尺寸以适配指定的 "[-size](https://imagemagick.org/command-line-options/#size)" 设置,而是会把任何不适合所指定 "[-size](https://imagemagick.org/command-line-options/#size)" 宽度的长行进行自动换行。不过 "[-size](https://imagemagick.org/command-line-options/#size)" 设置不是可选的,至少必须指定以像素为单位的最大宽度。例如,下面是一个不适合所指定宽度的长行的 caption。
magick -background lightblue -fill blue -font Corsiva -pointsize 36 \
-size 320x caption:'This is a very long caption line.' \
caption.gif
| 生成的 'caption:' 图像还会把 "caption" 图像属性元数据“设置”为同一字符串,让你之后可以重用该信息。所有常见的图像文件格式都会随图像保存此信息。参见使用已保存元数据的 Montage。
---|---
默认情况下文本全部左对齐,不过从 IM 版本 6.2.0 起,"caption:" 会遵循 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 进行文本对齐。
magick -background lightblue -fill blue -font Candice -pointsize 40 \
-size 320x -gravity Center caption:'ImageMagick Rules OK!' \
caption_centered.gif
如果你在 "[-size](https://imagemagick.org/command-line-options/#size)" 设置中同时提供了高度和宽度,那么图像高度也会被设置为该高度。然后你也可以使用 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 设置来垂直定位文本。
magick -background lightblue -fill blue -font Gecko -pointsize 32 \
-size 320x100 -gravity South caption:'Captions at their height!' \
caption_height.gif
不过请注意,如果在给定的 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)" 下文本(在高度方向)放不进你指定的 "[-size](https://imagemagick.org/command-line-options/#size)",那么文本将会溢出该框。当前的 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 设置自然会决定文本的哪一部分被裁掉。例如,下面这个与前一个示例完全相同,只是图像 "[-size](https://imagemagick.org/command-line-options/#size)" 对结果而言太小了。
magick -background lightblue -fill blue -font Gecko -pointsize 32 \
-size 320x60 -gravity South caption:'Captions at their height!' \
caption_height_toosmall.gif
最佳适配 Caption
从 IM v6.3.2 起,如果你同时提供最终图像的宽度和高度,但不定义字体的 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)"(或用 "[+pointsize](https://imagemagick.org/command-line-options/#pointsize)" 关闭点大小),IM 将尝试自动调整字体大小,以最佳地填满你所请求的图像 "[-size](https://imagemagick.org/command-line-options/#size)"。例如,这里我让 ImageMagick 填满一块相当大的区域……
magick -background lightblue -fill blue -font Candice -size 320x140 \
caption:'This text is resized to best fill the space given.' \
caption_filled.gif
现在用同样的字体和文本字符串,填一块小得多、窄得多的区域。 |
magick -background lightblue -fill blue -font Candice -size 80x110 \
caption:'This text is resized to best fill the space given.' \
caption_filled_sm.gif
![[IM Output]](../static/img/text/caption_filled_sm.gif)
注意最后两个示例之间唯一的区别是所生成图像的 "[-size](https://imagemagick.org/command-line-options/#size)"。IM 调整文本和自动换行,以尝试最佳填满所指定的图像尺寸。这对于把一段未知的文本放进给定空间而不溢出该区域边界非常有用。但在内部它相当于多次运行 caption,因为 IM 会搜索合适的点大小来最佳填满给定空间。换句话说,它常常会比你提供一个具体的 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)" 慢 10 倍甚至更多。
带段落的 Caption
"caption:" 图像操作符(从 IM v6.2.5 起)理解使用 '\n' shell 转义(因此你需要双反斜杠 '\\' 来转义反斜杠),表示一个新行或段落。在此版本之前,分开的段落必须由分开的 "caption:" 操作来处理。
magick -background lightblue -fill blue \
-font Ravie -pointsize 24 -size 360x \
caption:"Here I use caption to wordwrap.\nTwo separate lines." \
caption_multi_line.gif
你可以从文件,或从标准输入(来自上一个管道命令)读取要绘制的文本,使用 '@' 文件名前缀,正如我们对 "[label:](#label)" 所做的那样。
mesgs FilePrivate |\
magick -background lightblue -fill blue -pointsize 12 \
-size 320x caption:@- caption_file.gif
正如你所见,输入文本中的换行(从 IM v6.2.5 起)将被当作段落分隔符。这包括输入文件中任何末尾的换行。当然 "[label:](#label)" 不会对行进行自动换行,而是会保留它们。如果你真的想把一个文件当作单个段落处理,那么你需要把换行符替换为空格字符,使你的文本全在一行上。例如,这里我们取同样的文本,但把换行替换为空格,然后把单词之间的多个空格替换为单个空格……
mesgs FilePrivate | tr '\012' ' ' | sed 's/ */ /g' |\
magick -background lightblue -fill blue -pointsize 12 \
-size 320x caption:@- caption_one_line.gif
正如你所见这样效果好多了。然而通常你想要的是把空行当作一个段落分隔。这意味着你需要去除所有换行,除了那些涉及空行的换行。下面是一个特殊的 "sed" 命令,可把这样的文本变成 "caption:" 所需的格式。在这个例子中,文本是 "magick" man 手册的第一页。
man magick | col -b | expand | \
sed '/^$/d; :loop y/\n/ /; N; /\n$/! b loop; s/ */ /g; s/^ //' |\
head -n 7 | magick -size 400x caption:@- caption_manual.gif
| caption 没有“两端对齐”的文本选项。但pango: 文本排版器(使用一个外部库)确实具有该功能,以及更多功能。
---|---
文本属性
最初影响文本处理的设置包括:"[-font](https://imagemagick.org/command-line-options/#font)"、"[-fill](https://imagemagick.org/command-line-options/#fill)"、"[-pointsize](https://imagemagick.org/command-line-options/#pointsize)"、"[-size](https://imagemagick.org/command-line-options/#size)" 和 "[-gravity](https://imagemagick.org/command-line-options/#gravity)"。我们在上面已经介绍了其中许多属性控制。但还有一些不太常用、最初也不影响 "label:" 或 "caption:" 文本图像生成的属性控制。从 IM v6.3.2 起,你也可以对 "label:" 或 "caption:" 使用 "[-stroke](https://imagemagick.org/command-line-options/#stroke)"、"[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)" 和 "[-undercolor](https://imagemagick.org/command-line-options/#undercolor)"。例如,这里我使用了许多不同的设置来控制 IM 文本图像渲染的属性…… |
magick -background white -fill dodgerblue -font Candice \
-strokewidth 2 -stroke blue -undercolor lightblue \
-size 165x70 -gravity center label:Anthony label_color.gif
![[IM Output]](../static/img/text/label_color.gif)
关于这些设置的更多细节,参见下文的底色框,以及绘图章节中的 Stroke、StrokeWidth。 | 目前,你不能对 "label:" 或 "caption:" 使用通过 "[-tile](https://imagemagick.org/command-line-options/#tile)"、"[-fill](https://imagemagick.org/command-line-options/#fill)"、"[-background](https://imagemagick.org/command-line-options/#background)" 和 "[-origin](https://imagemagick.org/command-line-options/#origin)" 定义的平铺图像。只能使用纯色。试图这样做只会产生一个未定义的(黑色)颜色。
---|---
点大小、密度与实际字体大小
像素是显示器上或图像中的点,这正是 IM 所处理的。另一方面,图像以特定分辨率打印(指定为“每英寸点数”(dpi)或每英寸像素数(ppi))。因此,图像的分辨率会影响其他程序如何把图像调整到特定介质上的大小。即:它影响图像在现实世界中的物理尺寸。图像的分辨率(密度或 dpi)与图像的像素尺寸无关,也与图像在内存或磁盘上占用的空间量无关。一般来说,它也与大多数 IM 图像操作无关。因此,对 ImageMagick 而言,分辨率只是随图像存储的一组数字,通常被忽略。分辨率或密度唯一变得相关的时候,是处理字体,以及把像 postscript、pdf、MWF 这样的矢量格式转换为 IM 所处理的栅格图像格式时。"[-density](https://imagemagick.org/command-line-options/#density)" 设置告诉 IM 输出设备上每英寸有多少像素(点)(ppi),IM 随后可以用它来调整图像生成和字体大小以匹配。例如,默认情况下 IM 以 72 ppi 的 "[-density](https://imagemagick.org/command-line-options/#density)" 设置工作,这是在显示器或网页上显示图像的典型设置。由于字体大小以“点”为单位指定(使用 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)"),按定义 1 点为 1/72 英寸,那么 72 点的字体应当产生大约 1 英寸高的文本…… |
magick -pointsize 72 label:Hello pointsize.gif
![[IM Output]](../static/img/text/pointsize.gif)
然而大多数现代显示器的分辨率比这更好,通常介于每英寸 90 到 120 像素(ppi)之间。因此…… |
magick -density 90 -pointsize 72 label:Hello density.gif
![[IM Output]](../static/img/text/density.gif)
在 90 dpi 显示器上应当产生一个 1 英寸高的 label。在我的显示器上就是这样!你可以在屏幕上测量这些图像的高度,以检查你显示器的分辨率。由于每英寸像素数更大,所绘制的字体在图像像素数方面自然也更大,因而产生更大的图像。不同的图像程序常有不同的默认密度,这可能导致字体在不同程序绘制时显示不同,即使在相同点大小下也是如此。注意 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)" 实际上指的是字体的行间隔(实际上是其绘制区域高度),而不指所绘制字母的实际高度!因此,在相同的点大小和密度下,一种字体可能比另一种字体显得更大或更小。只有字体的行间距实际上会相同,其他一切都取决于字体及其设计者。因此,在默认 "[-density](https://imagemagick.org/command-line-options/#density)" 为 72dpi(此时 1 点 = 1 像素)下,一个 12 点的字体应当在两行文本的基线之间有 12 像素的间隔。 | 注意,生成的 "[label:](#label)" 图像的高度基于图像的绘制区域或边界框,通常就是字体的行间距和点大小。但情况并不总是如此,因此仅仅把多行文本竖直拼接其实是不正确的字体处理!
---|---
有些字体甚至可能远远延伸到正常行间隔边界之外,远高于、或更常见地远低于行间距。手写脚本字体尤其如此。字体的外观也受字体的 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)" 和 "[-density](https://imagemagick.org/command-line-options/#density)" 的影响。把字体的点大小加倍("-pointsize 24")所产生的字体,看起来与把密度或分辨率加倍的字体大小大致相同。但因为字体被设计成以某种特定方式呈现,所以在更大的点大小下字体中线条的粗细可能变化不大。也就是说,更大的字体大小略有不同。但如果你只是把密度加倍("-density 144"),一个 12 点的字体将以其尺寸加倍来绘制,看起来仍应像原来的 12 点字体,只是以更大的比例绘制,并对边缘有更好的平滑处理。然而在非常低的分辨率下,像素的物理尺寸限制也可能影响字体的外观。这意味着由于密度所定义的大像素尺寸,较低密度下细线可能被加粗。“密度”与“点大小”之间的关系是一个非常复杂的问题,只有专业的字体平面设计师才能完全理解,并设计他们的字体以正确处理。据 IM 论坛的 Lithium 所言……
我认为这是 TrueType 字体渲染器的一个特性。TrueType 字形不仅是一组曲线,它可能包含多个细节层级,以及根据输出的像素大小调整点坐标的指令,这在以像素为单位的小尺寸下更明显。正因为如此,小号文本看起来不同(而且可以注意到更清晰)于缩小的大号文本。
未来示例:在相同‘像素’大小但不同密度和点大小下字体的差异。 基本上,在增大其中一个因素、同时把另一个减小相同量时,可能 不会 产生相同的结果。尤其在线条粗细和字体的整体“风格”方面。你最好为你正在做的事情调整正确的因素。在为输出设备缩放字体,或之后调整字体大小时,使用 "[-density](https://imagemagick.org/command-line-options/#density)";在普通字体大小更改时,使用 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)"。如果你想更多了解字体,那么看看文档 TrueType 基础(PDF),我觉得它很有意思。
标签图像边界
在使用某些奇特字体时,字体可能会使用扩展字符,过去 IM 在为这些字体创建 label 时遇到很多麻烦。也就是说,文本会溢出所提供的画布。例如,下面是一个让人联想起某著名软饮料的 'LokiCola' 字体中的两个大写字母。 |
magick -background lightblue -fill blue -font LokiCola -pointsize 64 \
label:HC label_overflow_font.gif
![[IM Output]](../static/img/text/label_overflow_font.gif)
正如你所见,IM 成功地把这个字体容纳在一个 label 中,而没有裁掉字体的前导或尾随图形。 | 在 IM v6.3.2 之前,在上面的示例中,"[label:](#label)" 会裁掉 'H' 的引入部分以及两个字符的尾部部分。
---|---
存在这个问题的原因,是字体的“字形”或字符描述会在特定字母的字体定义边界之外绘制,使它们能够(通常在上方或下方)与字体内的其他字符重叠。这是字体本身的设计和定义方式的问题,并非 IM 的过错,不过 IM 现在会以最符合用户利益的方式处理这些奇怪的情形。在其他情形下它仍可能是问题,并且由于多行文本的相互作用而无法简单解决。更多信息参见下文的边界框溢出示例,以获得更精确的描述。
Unicode 或 UTF8 格式文本
这种向 IM 提供字符串参数的方法非常重要,因为它让你能做一些通常很难从命令行做到的事情。具体来说就是处理“unicode 文本”,或使用字符代码选择特定字符。现在,如果你能在命令或脚本中输入 unicode 字符,你就可以直接使用它们……
magick -background lightblue -fill blue -pointsize 32 \
label:' é è à ù ç Ö ÿ ‘ ’ “ ” ° ² ³ € x ÷ ' label_i8n.gif
然而很少有人的键盘或编辑器被正确设置为可处理 unicode 字符输入。即使你不能直接输入 unicode 字符,一个简单的解决办法是从某个现有的 UTF-8 文本文件或网页中“复制-粘贴”所需的字符。我就是这么做的!如果你想绘制的 UTF-8 文本已经生成好了,你可以使用 '@filename' 直接从文件读取它。例如这里我从一个 UTF-8 编码的中文文本文件(文件中没有末尾换行)创建一个中文 label。
magick -background lightblue -fill blue -pointsize 48 \
-font ZenKaiUni label:@chinese_words.utf8 label_utf8.gif
| _上面示例中使用的字体是一种特殊字体,定义了完整的中文字形集,例如 fedora linux 字体 'SimSun'(或字体文件 "gkai00mp.ttf")、"ZenKaiUni"(在文件 "ukai.ttf" 中)或 "ShanHeiSunUni"(在文件 "uming.ttf" 或 "zysong.ttf" 或 "bsmi00lp.ttf" 中的任一个)。
注意 windows 字体 'Mincho'(在后面的示例中使用)也定义了许多中文字形,但并不完整。如果你把它用于上面的情形,你会看到一些问号代表未定义的字形。
特殊脚本 "[imagick_type_gen](../static/img/scripts/imagick_type_gen)" 被用来查找、提取字体的正式名称,并把该字体添加到一个 ImageMagick "type.xml" 配置文件中。
_
---|---
我们也可以使用 'GNU' "printf" 程序(在 linux 系统上)从 unicode 字符代码生成 UTF-8 字符串,把 unicode 数字变成具体的 UTF-8 编码字符串,在这个例子中是正确排版的开引号和闭引号(同样在 UTF-8 输入中没有末尾换行)。例如这里我使用 unicode 字符代码生成 UTF-8 文本,并通过命令管道(用 "@-" 从 'stdin' 读取)馈送它,而不是从实际文件读取。
env LC_CTYPE=en_AU.utf8 \
printf "‘single’ - “double”" | \
magick -background lightblue -fill blue -pointsize 36 \
label:@- label_quotes.gif
在其他系统上(如 Mac OSX 和 Windows)你可以使用 perl 的 "printf" 从 unicode 字符代码输出一个 UTF-8 编码的字符字符串。
perl -e 'binmode(STDOUT, ":utf8"); \
print "\x{201C}Unicode \x{2018}\x{263A}\x{2019} Please\x{201D}";' |\
magick -background lightblue -fill blue -pointsize 36 \
label:@- label_unifun.gif
更多信息以及查找各种语言和符号的 unicode 字符代码,参见 Unicode 字符代码表。unicode 字符不仅可以包含国际字符,而且通过合适的字体,你还可以使用它定义的特殊“符号”集。其中最著名的是 'DingBats' 符号字体。这种字体已变得如此常见,以至于它现在成为标准 Unicode 字体集的一部分。例如,这里我使用我自己编写的特殊 "**[graphics_utf](../static/img/scripts/graphics_utf)**" shell 脚本,提取 'DingBats' unicode 符号区域的前 24 个字符,把一个 unicode 字符“块”生成为 UTF-8 文本。
graphics_utf -N 2701 2718 |\
magick -font Mincho -pointsize 32 label:@- label_dingbats.gif
上面的问号是一些特定字符,它们没有被 unicode 定义,也没有被 windows 的 'Mincho' 字体定义。更具体地说,那个原本是原始 'dingbat' 字体一部分的符号在 unicode 中是存在的,但使用了另一个 unicode 字符代码,而不是预期的 dingbat 代码。更多细节参见 Dingbats Unicode 规范表,以及它对“缺失”dingbat 字符应使用的正确 unicode 字符的指引。许多字体不会显示问号,而是会为这样的未定义字符打印一个方框或一个空白字符。如果你在输出中看到太多这样的字符,或缺失的字符,你大概应该使用一种不同的字体。庞大的 unicode 字符字体中还包含其他可用的符号集,包括:托尔金的卢恩符文符号、数学符号、罗马数字、箭头、盲文以及技术符号。这是一个值得探索的庞大字符集,而 "**[graphics_utf](../static/img/scripts/graphics_utf)**" shell 脚本可以帮助你探索它。下面是另一个 Microsoft 'Mincho' 字体可以渲染的 unicode 字符示例。在这个例子中来自 "杂项符号" 部分……
graphics_utf -N 2620 2630 |\
magick -font Mincho -pointsize 40 label:@- label_misc.gif
在 DOS 脚本中使用 unicode 比在 UNIX 和 LINUX 下困难得多。Wolfgang Hugemann 在 Windows 字符编码中提供了关于在该环境下使用 unicode 的特别说明。
符号字体
更常被寻找特殊文本图像的人使用的,是特殊的“符号字体”。这些字体比庞大完整的 Unicode 字体小得多,因为它们只把普通的标准 ASCII 字符(字母和数字)替换为一组不同的特定形状和图像,不过有时(很少)它们在拉丁元字符区域也有更多符号。'DingBat' 字体符号最初就是这样出现的,但如前所述,它们现在是 Unicode 字符集的一部分。例如,我相当喜欢使用的一个符号来自字体 "WebDings"。它是一个相当漂亮的“弯曲心形”符号,在该字体的定义中替换了普通的 'Y' 字符……
magick -size 20x20 -gravity center -font WebDings label:Y label_heart_20.gif
magick -size 40x40 -gravity center -font WebDings label:Y label_heart_40.gif
magick -size 60x60 -gravity center -font WebDings label:Y label_heart_60.gif
magick -size 80x80 -gravity center -font WebDings label:Y label_heart_80.gif
要记住的重要一点是,所有 truetype 字体实际上都是一种特殊类型的矢量图像格式,字体中包含多个图像(每个字符一个)。由于它们是矢量图像,这意味着字体应当允许你使用 "[-size](https://imagemagick.org/command-line-options/#size)"、"[-pointsize](https://imagemagick.org/command-line-options/#pointsize)" 和 "[-density](https://imagemagick.org/command-line-options/#density)" 提供的控制,以几乎任意大小(比例)“绘制”一个字符、形状或符号。正如你在上面所见,“弯曲心形”可以以我想要的几乎任何大小“渲染”。有些字体非常专门化。例如你可以从 IDAutomation 获取一个名为 "IDAutomationHC39M.ttf" 的字体文件,可用于生成条形码。例如……
![[IM Output]](../static/img/text/label_heart_80.gif)
|
magick -font IDAutomationHC39M -pointsize 16 label:'*314-76*' \
-bordercolor white -border 5x5 label_barcode.gif
![[IM Output]](../static/img/text/label_barcode.gif)
下面是我出于这样那样的原因收集的各种符号字体中找到的一些其他有趣符号……
magick -pointsize 48 -font WebDings label:' " _ ~ ) - ' label_webdings.gif
magick -pointsize 48 -font LittleGidding label:' x o w ' label_ltgidding.gif
magick -pointsize 48 -font WingDings2 label:'ab' label_wingdings2.gif
magick -pointsize 48 -font Zymbols label:' ? , - I Z ' label_zymbols.gif
magick -pointsize 48 -font TattoEF label:' B Y D I H ' label_tatooef.gif
magick -pointsize 48 -font SoundFX label:' V 3 t f 9 ' label_soundfx.gif
这只是可用资源的一小部分。WWW 上有几乎能想象到的每一种符号、形状或图像的庞大库,供你浏览和下载。
![[IM Output]](../static/img/text/label_soundfx.gif)
| 记住每个绘制的字符有两个可以分别绘制的部分:“填充”区域(我在上面展示的),以及“描边”或轮廓,它看起来可能与填充区域非常不同。这些区域中的每一个都可以分别绘制,或以不同颜色绘制,所以以多种方式更仔细地检查一个有潜力的符号或形状可能是个好主意。你可能会得到一个非常令人惊讶的结果。关于这样做的一些示例,参见复合字体,描边。
| _许多符号字体的创建者使用一个简单的扫描仪和位图到矢量的转换器来生成形状,而没有对图像或形状进行任何适当的设计或清理。在查看这类“扫描”字体时建议谨慎。
上面展示的最后一种字体就是这样一种“扫描”字体的例子,与其他设计更恰当的字体相比,它有一种粗劣的“点状”质感。_
---|---
字符间距 Kerning
从 IM v6.4.7-8 起,你可以使用 "[-kerning](https://imagemagick.org/command-line-options/#kerning)" 在文本字符串中每个字母之间插入额外的字符间距。例如 |
magick -pointsize 12 label:Anthony label_kerning_0.gif
magick -pointsize 12 -kerning 1 label:Anthony label_kerning_1.gif
magick -pointsize 12 -kerning 2.5 label:Anthony label_kerning_2.gif
magick -pointsize 12 -kerning 5 label:Anthony label_kerning_5.gif
magick -pointsize 12 -kerning -1 label:Anthony label_kerning-1.gif
![[IM Output]](../static/img/text/label_kerning-1.gif)
注意实际的 kerning 值可以是浮点值,甚至可以是负值。关于使用负 "[-kerning](https://imagemagick.org/command-line-options/#kerning)" 值的另一个示例,参见连接复合字体示例。
词间距
同样从 IM v 6.4.8-0 起,选项 "[-interword-spacing](https://imagemagick.org/command-line-options/#interword-spacing)" 可用于修改单词之间所用空格字符的大小。例如 |
magick label:'I Love IM!' label_wspace_off.gif
magick -interword-spacing 1 label:'I Love IM!' label_wspace_1.gif
magick -interword-spacing 10 label:'I Love IM!' label_wspace_10.gif
magick -interword-spacing 25 label:'I Love IM!' label_wspace_25.gif
![[IM Output]](../static/img/text/label_wspace_25.gif)
注意你不仅可以增大单词之间空格字符的大小,也可以减小其默认大小。但要注意,空格会导致单词被重新对齐到像素边界(与上面的字符间距 Kerning 不同),所以把空格设为零的 label 输出仍会与一个完全不含任何空格的 label 不同。字符间距 Kerning 和词间距都会影响 IM 将文本字符串自动适配到特定尺寸图像的能力的结果。 |
magick -size 150x label:'I Love IM!' label_wsize_of.gif
magick -size 150x -interword-spacing 25 label:'I Love IM!' label_wsize_25.gif
magick -size 150x -interword-spacing 50 label:'I Love IM!' label_wsize_50.gif
![[IM Output]](../static/img/text/label_wsize_50.gif)
所发生的是,通过设置 "[-interword-spacing](https://imagemagick.org/command-line-options/#interword-spacing)",“空格”字符的大小不再随文本其余部分的大小变化。因此,当 IM 试图算出最佳 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)" 时,每个单词之间的间距量是固定的,因而在把文本行适配到给定固定宽度的过程中不起作用。结果是,"[-interword-spacing](https://imagemagick.org/command-line-options/#interword-spacing)" 越大,实际把文本行适配到同一指定图像宽度所需的字体大小就越小。可以使用负值,事实上它可以使单词重叠,或使用特定字符和字体产生不寻常的效果。但把它弄得太负,就会出现未定义的行为。如果你尝试这样做,建议谨慎。虽然上面这个不是文本两端对齐的示例(尽管看起来像),但你可以把这些选项用作提供正确文本两端对齐的起点。如果你确实需要那种程度的文本格式化和两端对齐,那么你最好看看其他生成预格式化文本或 Postscript 的方法,例如基于命令行的 "TeX" 或 "LaTeX" 软件。更好的是,你可以使用 SVG(rsvg 库版本),或 Pango 标记语言(见下文),来生成两端对齐的文本。
行间距
从 IM v6.5.5-8 起又添加了另一个选项 "[-interline-spacing](https://imagemagick.org/command-line-options/#interword-spacing)"。鉴于前面的那些设置,这是用户大量请求的功能,而且在许多方面更有用。基本上它会在各行文本之间增加或减少这么多像素。也就是说,你可以用它来扩展或压缩各行文本。例如……
magick label:'First\nSecond' label_lspace_off.gif
magick -interline-spacing 5 label:'First\nSecond' label_lspace_5.gif
magick -interline-spacing 10 label:'First\nSecond' label_lspace_10.gif
magick -interline-spacing 20 label:'First\nSecond' label_lspace_20.gif
magick -interline-spacing -5 label:'First\nSecond' label_lspace-5.gif
magick -interline-spacing -10 label:'First\nSecond' label_lspace-10.gif
要最好地利用这个设置,你需要能够计算特定字体的正常行间距。这正是 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)" 的定义,它连同当前分辨率或 "[-density](https://imagemagick.org/command-line-options/#density)" 设置一起,定义了字体的行间距。它实际上并不定义字体的实际高度或线条粗细,尽管它会影响特定字体的这些方面。那么以 '72' 每英寸点数的 "[-density](https://imagemagick.org/command-line-options/#density)",并且知道按定义每英寸有 72 个“点”,你可以算出一个 12 点的字体将有 12 像素的行间距。有了这个信息,你可以使用 "-interline-spacing 12" 设置把你的 12 点文本行“双倍行距”。这会在行之间增加 12 个额外像素。 |
magick -density 72 -pointsize 12 -interline-spacing 12 -font Arial \
label:'First\nSecond\nThird' label_lspace_double.gif
![[IM Output]](../static/img/text/label_lspace_double.gif)
当然,正如之前在词间距中所见,在文本元素之间增加固定数量的像素,往往会在使用自动点大小处理时使文本变小。也就是说,提供了最终图像 "[-size](https://imagemagick.org/command-line-options/#size)" 而没有 "[-pointsize](https://imagemagick.org/command-line-options/#pointsize)" 设置时。 |
magick -size x70 -interline-spacing 18 -font Arial \
label:'First\nSecond\nThird' label_lspace_size.gif
![[IM Output]](../static/img/text/label_lspace_size.gif)
使用负的行间距也可以作为一种粗略而简便的方法,用于在竖直方向上“加粗”一行,做法只是重复那一行,并减去比基线间隔多 1 个像素的值。 |
magick -density 72 -pointsize 12 -interline-spacing -13 -font Arial \
label:'Bolded Word\nBolded' label_lspace_vbold.gif
![[IM Output]](../static/img/text/label_lspace_vbold.gif)
当然,如果你真的想要两行,而不只是加粗文本,这就行不通了。它在等宽字体下也效果更好。
文本参数中的特殊转义字符
我们在上文已经介绍了各种文本参数中使用的特殊转义字符。具体来说,你可以使用反斜杠 '\' 转义像换行这样的特殊字符,或者你可以使用百分号 '%' 转义把额外信息插入字符串,如图像属性页面所定义。另外有一个特殊的 '@' 转义,如果用在一行的开头,会把文本参数的其余部分当作一个文件名,从所指定的文件读取数据(如果使用 '-' 则从 STDIN 读取)。 |
某些系统(如 ubuntu)通过安全策略禁用 '@{file}' 转义的使用。键入magick -list policy 可查看你系统上存在哪些策略以及它们从何处设置。 |
|---|---|
这些转义字符不仅影响 "[-format](https://imagemagick.org/command-line-options/#format)",供 "magick identify"(以及 "[-identify](https://imagemagick.org/command-line-options/#identify)" 和 "[info:](files.html#info)")使用,它们也影响 "[label:](#label)" 和 "[caption:](#caption)" 文本到图像生成器,并控制图像元数据设置选项 "[-label](https://imagemagick.org/command-line-options/#label)"、"[-comment](https://imagemagick.org/command-line-options/#comment)"、"[-caption](https://imagemagick.org/command-line-options/#caption)"。最后它们也被 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 使用。 |
_虽然反斜杠 '\' 被 "[-draw](https://imagemagick.org/command-line-options/#draw)" 的 'text' 方法使用,但百分号 '%' 转义不被使用,因为它会干扰 ImageMagick 的 SVG 图像处理。这是为 IM 版本 6 创建 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 操作符的原因之一。 |
---|---
关于转义字符的另一个重要要点是,虽然它们用于命令行文本参数,但它们在任何时候都不适用于从文本文件读取的数据(通常使用 '@' 转义读入)。这意味着你不必担心为文本文件数据转义“转义符”,但也意味着如果你需要把信息插入文本,你必须在 IM 之外自行处理文件数据。 | _保护输入文本文件不受转义处理是在 IM 版本 6.3.3 中最终确定的。
---|---
例如,这里我使用两种方法从一个源文本文件设置并报告图像的 'label' 和 'comment' 元数据。"info.txt" 文件包含字符串
,(无末尾换行)。 |
magick -label @info.txt rose: -format '%l label' info:
magick -comment @info.txt rose: -format '%c set "' info:
magick rose: -set label @info.txt -format '%l caption' info:
magick rose: -set comment @info.txt -format '%c set "' info:
| ![[IM Text]](../static/img/text/file.txt.gif)
注意 IM 没有 展开它使用 '@' 文件读取转义读入的任何转义字符序列。这很重要,因为它意味着任何时候 IM 从文件读取文本,它都 绝不 会处理该文件中存在的任何特殊字符。
IM 把文本文件当作字面文本读取,不做任何转义
不幸的是这也包括正在读取的文件(或流)中可能存在的任何末尾换行!当输入文本末尾有一个换行时(这是非常常见的做法),这可能导致在生成的图像中出现一个额外的“空白”行。例如…… |
echo "Anthony" | magick label:@- label_stdin.gif
![[IM Output]](../static/img/text/label_stdin.gif)
正如你所见,label 不仅包含输入字符串,还因为 "echo" 命令在末尾添加的换行符而多了一个空白行。如果你不想要那个末尾换行,你需要自己去除它。然而这可能是个棘手的问题,取决于文本来自哪里、如何来源或创建,以及你从什么 API 运行 IM。最好的办法是设法一开始就不生成那个末尾换行。例如对 "echo" 使用一个 '-n' 标志。 |
echo -n "Anthony" | magick label:@- label_stdin_2.gif
printf "Anthony" | magick label:@- label_stdin_3.gif
![[IM Output]](../static/img/text/label_stdin_3.gif)
或者你可以用一个巧妙的 perl 单行命令“丢弃”那个末尾换行…… |
echo "Anthony" | perl -0777 -pe 's/\n$//' |\
magick label:@- label_stdin_4.gif
![[IM Output]](../static/img/text/label_stdin_4.gif)
在其他 API 中,你可以在通过“管道打开”把文本馈送给 IM 命令之前,查找那个末尾换行。
用户定义的选项转义
一个主要问题是,试图在某个其他图像中使用来自一个图像的转义信息,例如在生成一个单独的 "[label:](#label)" 或 "[caption:](#caption)" 图像时。这是一个非常困难的问题,当前的解决方案(对于单个图像)是创建一个特殊的“用户选项”,把它附加到正在处理的图像上。这个“设置”随后可以在需要时被 "[label:](#label)"、"[caption:](#caption)" 或 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 作为一个百分号转义序列查找到。例如,这里我使用来自内置 rose 图像的信息创建一个全新的 label 图像。那个作为信息源的图像随后被删除,不过我也可以同样轻松地把这个新 label 附加到原始图像上。 |
magick rose: \
-set option:roseinfo 'rose image\nsize = %w x %h\nwith %k colors' \
label:'%[roseinfo]' -delete 0 label_escape.gif
![[IM Output]](../static/img/text/label_escape.gif)
是的,上面这个很棘手,但那是由于涉及一些 IM 内部核心库的限制。更多细节参见从其他图像访问数据。
转义“转义符”
如果你必须把一个字符串作为参数馈送给 IM(特别是作为 API 调用),但不希望 IM 展开转义,你可以简单地用一个额外的反斜杠 '\' 来“转义”全部三种转义符。注意 '@' 只有在它是第一个字符时才需要被“转义”,而且为了向后兼容,百分号转义也可以通过加倍来转义。也就是说,'%%' 会产生一个百分号。例如…… |
magick -background lightblue -fill blue -font Candice -pointsize 48 \
label:'\@ \\n \% 5%% ' label_escapes.gif
![[IM Output]](../static/img/text/label_escapes.gif)
| 在 IM 版本 6.3.2 之前,你不能使用反斜杠来转义开头的 '@' 以关闭“从文件读取”功能。在那种情况下,转义开头 '@' 的唯一方法是从文件读取它。这在 API 中不太实用。
---|---
下面是一个类似的针对 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 的“转义转义符”示例…… |
magick rose: -fill white -stroke black -font Candice -pointsize 20 \
-gravity center -annotate 0 '\@ \\n 5%%' annotate_escapes.gif
![[IM Output]](../static/img/text/annotate_escapes.gif)
当然如前所示,从文件读取文本(使用 '@' 转义)将始终被当作字面处理,没有任何特殊含义。这避免了对文本进行任何预处理的需要,只需注意任何末尾换行。 |
echo -n '@ \n 5%' |\
magick rose: -fill white -stroke black -font Candice -pointsize 20 \
-gravity center -annotate 0 '@-' annotate_escapes_file.gif
![[IM Output]](../static/img/text/annotate_escapes_file.gif)
换句话说,从文件读取时你不必担心转义任何东西,而可以直接写出你想让 IM 使用的确切文本。
较旧版本 IM 上的转义
上述定义是在 IM 版本 6.3.3 中才最终确定的。在此之前,根据 IM 用户发送的任何请求、问题和抱怨,转义有时在某些选项中被处理,有时不被处理。在涉及 "[label:](#label)" 和 "[caption:](#caption)" 的百分号转义方面尤其如此,这些转义曾一度被认为是“没有意义的”。例如,你是否会在下面的 label 图像中看到 '%c',非常依赖于版本(至少在 IM v6.3.3 之前)。 |
magick -background lightblue -fill blue -font Candice -pointsize 48 \
label:'ab%cde' label_percent.gif
![[IM Output]](../static/img/text/label_percent.gif)
你看到的是 'abde'(应用了百分号转义)还是 'ab%cde'(未应用百分号),取决于你使用的具体 IM 版本。 | _IM v6.2.4 中,百分号转义因被认为没有意义而从 "[label:](#label)" 和 "[caption:](#caption)" 中移除。
然而它们在 IM v6.3.2 中作为一个新的 '%[fx:...] 构造回归,它可以引用任意图像,使文本到图像生成器中的百分号转义再次有用。参见 FX 表达式转义。_
---|---
关于从文件处理转义,这个“什么被转义”的问题也曾是个麻烦。在 IM v6.3.3 之前,下面这个会产生两行,而不是单独的一行。 |
echo -n ' Love \n/ Hate ' |\
magick -background lightblue -fill blue -font Ravie -pointsize 18 \
label:@- label_escapes_file.gif
![[IM Output]](../static/img/text/label_escapes_file.gif)
由于转义处理的结果在不同版本之间差异很大,在比 v6.3.3 旧的 IM 中,我建议脚本测试它的转义处理,如果这对程序的正确运行很重要,就自行调整。如果有人想为 IM 脚本创建一个自动测试,请贡献。或者如果你找到这样一个测试,请告诉我。
Pango —— 基本格式化文本
"pango:" 文本编码器(从 IM v6.7.6-3 起完全可用)的工作方式与 Label 和 Caption 编码器大致相同。它在安装了“Pango”的系统上提供一种有限的文本格式化语言。在 Linux 和 MacOSX 系统上 pango 是标准的,在 Windows 上是可选的。下面是一个不使用任何特殊 pango 格式化的简单示例…… |
magick -background lightblue pango:"Anthony Thyssen" pango.gif
![[IM Output]](../static/img/text/pango.gif)
不过你应当注意,之前的一些文本属性在 pango 中不起作用,这基本上是由于它的文本格式化要求。例如,虽然你可以设置 "[-background](https://imagemagick.org/command-line-options/#background)" 颜色,但你不能设置默认填充色或底色,也不能设置要使用的具体字体。这是因为这些属性是通过 Pango 标记语言来选择的(见下文)。建议你使用 "[-size](https://imagemagick.org/command-line-options/#size)" 来定义输出图像的宽度和高度限制,pango 将对输入文本自动换行(对中文则是按字符换行)。 |
magick -background lightblue -size 150 \
pango:"Anthony Thyssen is the main author of IM Examples" \
pango_size.gif
![[IM Output]](../static/img/text/pango_size.gif)
你甚至可以使用 Define "pango:justify" 让 Pango 正确地“两端对齐”文本…… |
magick -background lightblue -size 150 -define pango:justify=true \
pango:"Contributions to IM Examples are welcome via the IM Forum." \
pango_justify.gif
![[IM Output]](../static/img/text/pango_justify.gif)
不过请注意,虽然文本可以两端对齐,但空格和换行仍会被文本排版考虑在内。另外 pango 理解 TAB 的使用(与 label 和 caption 不同)。 |
printf "col1\tcol2\nabc\txyz\n123\t789" |\
magick -background lightblue pango:@- pango_tabs.gif
![[IM Output]](../static/img/text/pango_tabs.gif)
| 注意,虽然上面的 "printf" 命令可以使用 '\t' 转义生成 tab 字符,但 IM 并不理解这种转义的使用。不过它确实理解字符串中的 '\n' 转义序列。
---|---
然而用 TAB 生成列效果不太好,因为你无法在 API 之外轻松定义“制表位”。因此以这种方式使用 TAB 不推荐,除非作为行和段落的缩进。
Pango 标记语言
然而 pango 真正的威力在于“Pango 标记”语言,它默认启用。你可以使用 "-define pango:markup=false" 关闭 pango 标记,但那样你不如改用 Caption。“Pango 标记”很像 HTML,你在文本中隐藏一组 "<...>" 标记标签,用于控制文本如何被格式化。下面是一些关于该标记语言的指南(不含 API 之类的杂项)
- Pango 标记语言
- Pango 文本属性标记语言 (来自 Gnome)
- Pango 文本属性标记语言 (来自 GTK)
- Pango 脚本画廊(示例)
例如…… |
magick -background lightblue -gravity center -size 180x \
pango:"The <b>bold</b> and <i>beautiful</i>" \
pango_formatting.gif
![[IM Output]](../static/img/text/pango_formatting.gif)
"<span ... >" 标签是 pango 标记中要使用的主要标签。它让你能控制所含文本的确切大小、颜色和位置。例如……
magick -background lightblue \
pango:' Some <span size="49152" rise="-20480"
foreground="red" background="blue"
> Big Red on Blue </span> Text ' \
pango_span.gif
注意大多数数值会乘以一个 1024 的系数,因此上面示例中 "size="49152"" 的值,意味着 48 点的文本点大小。而负的 rise("rise="-20480")意味着把文本位置降低 20 点(在 72dpi 下即 20 像素)。但我也可以不为文本指定一个点大小,而是使用一个特殊的尺寸标签,例如 "size="x-large""。参见下一个示例的源代码。注意上面引号中的引号。标签内的引号是必需的。然而标签内的换行和额外空格在文本格式化中不起作用。因此,把额外的换行隐藏在标记标签中,或隐藏在标记注释 "<!-- ... -->" 中,会非常有用。再次参见下一个示例的源文本。作为 pango 格式化威力的最后一个示例,这里我用它来格式化一个预先准备好的文件 "[pango_test.txt](../static/img/images/pango_test.txt)"。它包含你最可能用到的大多数常见 pango 标记标签。把这个标记文件与下面生成的图像作比较。
magick -gravity center pango:@pango_test.txt pango_test.png
Pango 注意事项与问题
- Gravity(引力)
- 我一直无法让 pango 选择性地只居中单独一行文本。你只能通过 "
[-gravity](https://imagemagick.org/command-line-options/#gravity)" 设置居中所有内容,或不居中任何内容。原因似乎是 pango 被设计为为应用程序生成单独的文本标签。也就是说,标题通常与所显示文本的主体分开生成。Pango 并不打算成为一个全面的文本页面格式化引擎。 - Fonts(字体)
- Pango 可以在渲染过程中更改字体。它已经能轻松地对粗体和斜体文本这样做。然而字体规范来自 GTK,因而使用一个与 ImageMagick 总体上不同的系统。你可以运行 "gtk-demo" 程序,双击 "Pickers" 和 "Text Widget",了解更多关于使用 GTK 的字体的信息。
- Defines(定义)
- 有许多特殊的 Defines 可用于全局控制 pango 文本格式化的各个方面。这些目前列在伪文件格式上,不过我自己还没有探索它们全部。下面是我用过的那些……
-define pango:markup=false- 关闭标记语言标签。任何标签随后会被包含在输出中。文本中不可能进行任何 pango 格式化。这在调试中尤其有用,让你看到 pango 实际看到的输入。
-define pango:justify=true- 在图像尺寸的宽度上把文本两端对齐。也就是说,添加额外的词内间距,使一块文本行的左右两边都对齐。
关于 Pango 的更多信息
| 要了解究竟能做什么,参见 Pango 脚本画廊。如果你用 pango 做了什么有趣的事,请贡献。要么给我发邮件(地址在页脚),要么把它发布到 IM 讨论论坛。 | 在安装了 pango 的系统上,你也可以使用命令 "pango-view" 生成 pango 格式化的图像。然而它默认的“密度”或“dpi”设置是你的显示器(IM 默认使用 72 dpi),因而可能因主机而异。 |
|---|---|
Text —— 多页纯文本
"text:" 输入格式被设计为把纯文本变成图像,每页文本一张图像。它是 ImageMagick 的“分页文本”输入操作符。换句话说,它的目的是把较大的预格式化文本文件变成多页,方式与打印机把纯文本打印到一张张纸上大致相同。 | _不要把 "text:" 文件输入格式与类似的 "[txt:](files.html#txt)" 输入格式混淆。后者会首先尝试把文件作为一种 'IM 像素枚举' 图像格式来读取。
这并不意味着一个带 ".txt" 的纯文本文件会失败。事实上这样的文件大概会如你所预期那样被转换,因为如果一个枚举图像未被识别,"[txt:](files.html#txt)" 文件格式会自动回退到 "text:" 格式。
_
---|---
不过以这种方式处理文本有许多问题。首先,文本被绘制到一个大画布上,如果不想要那些未使用的空间,你就要面对去除它们的问题。另一个问题是行不会被“自动换行”,如果太长会溢出画布并被截断。最后,对于很长的文本文件,除非采取一些额外的预防措施,否则会生成多页(图像)。另一方面,"text:" 会处理几乎任何文本文件,而不修改最终生成的图像尺寸,也不对很长的行进行自动换行。你也不需要像在命令行上使用文本时那样预处理特殊字符。最后,更重要的是,如果使用等宽字体(如 Courier),带有间隔列数据的文件,仍会让那些数据保持在间隔列中。基本上 "text:" 会“原样”把输入文件变成图像。 | _从文件读取的输入文本数据本质上被直接传递给字体库以绘制 UTF 文本。因此,某些控制字符可能会用不寻常的“字形”绘制。这包括TAB 和 FORMFEED 字符,在撰写本文时,'freetype' 库会把它们弄错。
如果你对此有顾虑,你可能想用一个过滤程序(如 "expand")预处理你的文本文件,把TAB 字符变成相应数量的空格。_
---|---
绘制文本时,会以当前分辨率(用 "[-density](https://imagemagick.org/command-line-options/#density)" 设置)创建一个大的“letter”尺寸页面(或用 "[-page](https://imagemagick.org/command-line-options/#page)" 指定的页面尺寸或类型)。默认情况下(在 72 dpi)这将是 '612x792' 像素大小,对大多数用途而言非常大。例如,下面是把 "magick" 命令的纯文本格式手册直接转换为图像(它很大,所以要查看它,请选择右边的“page”图像)…… |
man magick | col -b | expand | \
magick -font CourierNew text:- -delete 1--1 text_manpage.gif
| _不过上面的手册到图像转换会生成多页(图像),所以我删除了第二页及之后的页面,只留下第一页,而不是所有页面的 GIF 动画。
我也可以在输入文件名后附加一个读取修饰符 '[0]',例如 "text:-'[0]'",告诉 IM 只读取生成的第一张图像。不过目前所有的页面选择仍是通过生成所有页面、删除不想要的页面来处理的。_
---|---
我在上面特意使用了“等宽”字体 'CourierNew',以保留打印页面中存在的字符间隔格式。注意这个输出与上面 caption: 的输出有何不同。这个图像的整体外观也可以用接下来 Postscript 章节中给出的同样技术来改善。如果你只想知道比如一个 'A5' 页面在 100 dpi 下有多大,那么这个命令生成一个该尺寸的单一空白页面,并以像素返回其尺寸。文件名 "/dev/null" 是一个始终为空的特殊 UNIX 文件。
magick -page A5 -density 100 -units PixelsPerInch text:/dev/null \
-format 'Page Size at %x = %w x %h pixels' info:
修剪文本页面
由于文本被“绘制”到一个大画布上,你很可能想要去除所有产生的未使用空间。这可以通过使用图像操作 "[-trim](https://imagemagick.org/command-line-options/#trim)"、"[+repage](https://imagemagick.org/command-line-options/#repage)" 来完成,然后为了让它看起来合理,使用 "[-border](https://imagemagick.org/command-line-options/#border)" 重新添加一些边缘空间。当然你还需要让重新添加的 "[-bordercolor](https://imagemagick.org/command-line-options/#bordercolor)" 匹配你使用的 "[-background](https://imagemagick.org/command-line-options/#background)" 颜色。听起来复杂?其实并不复杂,例如……
echo " Hello Cruel World " |\
magick -background lightblue -fill blue -pointsize 18 \
text:- -trim +repage -bordercolor lightblue -border 3 \
text_trimmed.gif
在上面的示例中,"[-trim](https://imagemagick.org/command-line-options/#trim)" 用于去除 "text:" 页面图像中大量的额外空白。然而这也会去除一行前面的任何前导空格!不过有一个有趣的技术,可以让你把图像 "[-trim](https://imagemagick.org/command-line-options/#trim)" 到绘制到页面上的实际文本的大小,包括输入中的任何前导和尾随空格。这使用了一个特殊的 "[-undercolor](https://imagemagick.org/command-line-options/#undercolor)" 设置(稍后详细介绍)。
echo " Hello Cruel World " |\
magick -background white -undercolor lightblue -fill blue \
-pointsize 18 text:- -trim +repage \
-bordercolor white -border 3 text_boxed.gif
文本底部的额外空间是文本输入中最后一个“换行”字符的结果,它在图像中创建了一个额外的空白行。但正如你所见,输入文本的前导和尾随空格被保留了下来。如果你在上面使用透明背景色,你随后可以把修剪后的图像扁平化,把未绘制的区域变成与所用“undercolor”相同的颜色。
echo " Hello Cruel World " |\
magick -background none -undercolor lightblue -fill blue \
-pointsize 18 text:- -trim +repage \
-bordercolor lightblue -border 3 \
-background lightblue -flatten text_box_trimmed.gif
上面的结果(除了添加的 "[-border](https://imagemagick.org/command-line-options/#border)")实际上几乎就是 IM 现在使用 "[label:](#label)" 并从一个 '@' 转义的文件名读取所产生的结果。然而 "[label:](#label)" 以更快、更干净的方式做到这一点(通过 "freetype" 库,而不是 postscript 转换)。你可以指定一个更小的 "[-page](https://imagemagick.org/command-line-options/#page)" 尺寸,要么以像素(见下一个示例),要么使用介质页面尺寸(例如 'A5'),使用 "[-density](https://imagemagick.org/command-line-options/#density)" 或像素分辨率设置。你还可以指定相对于左上角开始在页面上绘制文本的偏移。例如……
echo "This is a long line that shows that 'text:' does not word wrap." |\
magick -background lightblue -pointsize 18 \
-fill blue -page 320x95+50+10 text:-'[0]' +repage text_page.gif
| _几乎所有其他图像创建操作符都使用 "[-page](https://imagemagick.org/command-line-options/#page)" 设置来设置一个更大的虚拟“画布”以及图像在该画布上的“偏移”,通常用于图层叠加图像或生成动画。正因如此,在任何 "text:" 或 "ps:" 操作之后,使用 "[+page](https://imagemagick.org/command-line-options/#page)" 重置你的 page 设置大概是个好主意,否则你在同一命令行上稍后读入的任何后续图像可能会得到意外的结果。
这也是我在上面的示例中添加一个 "[+repage](https://imagemagick.org/command-line-options/#repage)" 操作符的原因,否则文本会被偏移,生成的图像也会被偏移!
关于使用此偏移的更多细节,参见页面图像属性。
---|---
注意在上一个示例中,任何太长而无法适配页面宽度的文本行都会溢出页面,而不会被“换行”。这实际上会裁掉并丢弃行的末尾。另外,如果行数太多,那么 "text:" 会生成 _多页,因而生成 多张图像,文本文件的 postscript 转换所生成的每一页对应一张。如果你只对文本的第一页感兴趣,或者只想避免出现多张图像的可能,就在 "text:" 文件名后添加一个 '[0]',告诉 IM 在文本被转换为图像 之后 只读取生成的第一页(见上一个示例)。
Postscript/PDF —— 预格式化文本和图形输入
(或其他矢量图像格式)
下面给出一种标准的矢量图像处理技术,它不仅可用于 "PS:"(postscript)图像,也可用于所有其他用矢量图形处理的图像。这包括如下图像格式:"PDF:"(便携式文档格式)、"[TEXT:](#text)"(分页纯文本),甚至 "[SVG:](draw.html#svg)"(可缩放矢量图形)和 "[WMF:](formats.html#wmf)"。这种方法可以扩展,让你非常精细地控制文本作为图像究竟会是什么样子。例如,有了合适的“文本到 postscript”过滤器,你可以控制 postscript 图像中的自动换行、两端对齐、多字体处理、加粗、边框、标题、文件名、日期和其他装饰。然而由于本节是关于文本到图像,这意味着你需要首先把你的文本变成一个格式化的 postscript 文件。有很多外部程序可用于做这件事。例如 "a2ps"、"enscript" 或 "pstext"。本质上你可以使用一个字处理软件(如 'OpenOffice' 或 'Word',甚至 'Notepad'),或者如果你想要一个批量文本处理系统,你可以使用 'TeX' 和 'LaTeX' 来生成你的预格式化文本(参见下文的一套完整的文本处理系统)。这些程序都被设计为处理把普通、粗体、不同大小和字体的文本混合在一起的复杂性,以及自动换行、两端对齐和分段控制。这些程序的输出随后可以传递给 IM,把它转换成你所期望的尺寸和质量的图像。那么我们先生成一些 postscript(从一个个人 fortune 程序转换文本)。 |
mesgs LN-ManKzinWars | \
a2ps -q -1 --rows=10 --prologue=bold \
--center-title="Postscript via 'a2ps'" \
--header='' --left-footer='' --right-footer='' \
-o ps_version.ps
现在我们可以把它变成一张图像,对结果进行修剪(如上面的 "text:" 示例),以去除默认生成的页面/画布中多余的空白区域。
magick ps_version.ps'[0]' \
-trim +repage -bordercolor white -border 3 ps_version_raw.gif
注意使用 '[0]' 来把输入限制为只有第一页。如果你的 postscript 图像生成多页,它仍会被 "ghostscript" 委托完全处理,但 IM 只会读取返回的第一张图像,而不是生成多张图像(每页一张)。如果你的 postscript 非常大,你可能想用其他 postscript 实用工具,在用 IM 和 "ghostscript" 处理它之前限制页面数量。正如你所见,转换为默认 "[-density](https://imagemagick.org/command-line-options/#density)" 72 dpi 的 postscript,常常看起来没有它本应有的那么好,只有最低限度的抗锯齿。在处理 postscript 字体时尤其如此,这些字体并非为在这些低分辨率下工作而设计。为改善这一点,你可以使用一种超采样技术来生成更好的图像。在这种情况下,你让 "ghostscript" 以更高的分辨率(或图像 "[-density](https://imagemagick.org/command-line-options/#density)")绘制页面。然后你可以 "[-resample](https://imagemagick.org/command-line-options/#resample)"(一种专门的缩放)把这张更大的图像带回到更“正常”的屏幕分辨率密度。
magick -density 196 ps_version.ps'[0]' -resample 72 \
-trim +repage -bordercolor white -border 3 ps_version.gif
值 '196' 是最终 72dpi 的 3 倍,这意味着使用 "[-resample](https://imagemagick.org/command-line-options/#resample)" 时大约 3×3 像素被合并成每个像素。这在文本边缘产生更好的抗锯齿像素,改善了结果的整体外观。还要注意,使用更大的密度或分辨率与仅仅放大字体并不完全相同。字体定义可能含有处理低分辨率情形的调整。例如,比较两张图像中字母 'e' 的孔洞。原始版本由于字体内部的特殊处理而更清晰,尽管总体上超采样版本更清楚。更多信息参见下文的分辨率、点大小与实际字体大小。你不一定要用上面的值,因为有时略有不同的值可能产生更好或更理想的结果。当然,让 "ghostscript" 生成一张大 2、3 甚至 4 倍的图像,也意味着 IM 生成该图像将多花 4、9 或 16 倍的时间!它还会使用那么多更多的内存和临时磁盘空间!但结果通常是值得的。最好的办法就是为你自己的文档试一试,看看什么能给你最好的结果。另外,如果你需要更多抗锯齿,与其使用更大的输入分辨率,你可以尝试在缩小尺寸之前把图像模糊一个亚像素的量(比如 '-blur 0x0.7')。我有时也发现,在缩放之后做一点点非锐化处理(一种常见的 photoshop 技术),可以改善总体最终结果。
magick -density 196 ps_version.ps'[0]' \
-blur 0x0.7 -resample 72 -unsharp 0x0.7 \
-trim +repage -bordercolor white -border 3 ps_unsharp.gif
但我会对这些调整保持谨慎,因为它可能使情况变得更糟。
如果你想要透明背景而不是白色,你可以指定一个 'RGBA' 的 "[-channel](https://imagemagick.org/command-line-options/#channel)" 设置,把 alpha 通道包含进图像。当然你需要使用一种能处理半透明颜色的图像格式。
magick -channel RGBA -density 196 ps_version.ps'[0]' -resample 72 \
-trim +repage -bordercolor none -border 3 ps_transparent.png
注意横幅的背景仍使用一种灰白色,而不是也被变成透明或半透明。这是因为所生成的 postscript 实际上绘制了那个背景,替换了页面的默认背景(无论是白色还是透明)。像这样把背景变透明,将让你可以把你的 postscript 图像叠加在一个特定的背景色上。
magick ps_transparent.png -background skyblue -flatten ps_bgnd_color.gif
使用一种 Alpha 合成方法,你甚至可以把它叠加到一个特定的背景图像上,或一个平铺背景上。
magick composite -tile bg.gif ps_transparent.png -compose DstOver \
ps_bgnd_tiled.gif
由于几乎所有 postscript 打印机只能使纸张或投影透明片变暗(这包括彩色打印机),当上面的内容被打印时,横幅会被自动变为半透明。如果你也想让 IM 做打印机会做的同样的事,你可以使用一种特殊的 '[Multiply](compose.html#multiply)' alpha 合成,把“白色背景”图像叠加到所需的“纸张”背景上。
magick composite -tile bg.gif ps_version.gif -compose Multiply ps_multiply.gif
如果你有一张彩色 postscript 图像,你也可以使用特殊的 '[BumpMap](compose.html#bumpmap)' compose 方法,模拟一台在彩色纸张上工作的纯黑白打印机。这会先把源叠加图像灰度化,然后再用 multiply 把图像合成在一起。你也可以生成相当于投影透明片的灰度图像。这基本上把(来自上面的)不透明白色背景图像用作一个“遮罩”,用于通过 Alpha 形状操作符设置成形的透明图像。
magick ps_version.gif -negate -background black -alpha shape ps_overhead.png
与上面的 "[text:](#text)" 转换器一样,"ps:" 转换器也利用 "[-page](https://imagemagick.org/command-line-options/#page)" 设置来设置页面绘制其上的图像“介质”的画布尺寸。不过所提供的偏移会被忽略。然而由于大多数 postscript 文件在内部定义了绘制介质的尺寸,这通常没有必要。 | _大多数其他图像创建操作符使用 "[-page](https://imagemagick.org/command-line-options/#page)" 设置来设置一个“虚拟画布”以及在该虚拟画布上的偏移(例如用于生成 GIF 动画)。因此,在为 "text:" 或 "ps:" 图像读取操作使用它之后,使用 "[+page](https://imagemagick.org/command-line-options/#page)" 重置它大概是个好主意,否则你后续的图像可能会得到意外的结果。
关于使用此偏移的更多细节,参见页面图像属性。_
---|---
作为最后一个实际示例,看看我的光线追踪四面体图像。其他类似图像可见于多面体研究。背景页面是用产生所显示的 3D 数学对象所用的同一数据生成的。文本数据使用 "a2ps" 转换,然后用 IM 把它变成一张图像。在这张图像上添加了同一数学对象的其他预先准备好的线条图。这张最终图像(以 'targa' 或 TGA 格式保存)随后被传递给 "[PovRay](http://www.povray.org/)" 光线追踪器,以纳入最终图像或光线追踪场景。
直接使用 GhostScript
虽然这严格说来不属于 IM,但 Richard Bollinger 报告说,直接运行 "ghostscript" 委托效率高得多,由于 IM 减少了文件处理,处理速度快了一个数量级。例如,与其运行……
magick -density 300x300 -compress Group4 file.ps file.tif
你可以让 GhostScript 直接做这件事。
gs -dBATCH -dNOPAUSE -sDEVICE=tiffg4 -r300x300 \
-sOutputFile=file.tif file.ps
这避免了 IM 生成一个大临时文件的需要(用于安全和流水线图像处理)。因此,直接使用 GhostScript 可以节省相当多的文件处理和 IO 处理,并在处理 postscript 和 PDF 文件时带来重大的性能提升。然而 "ghostscript" 不能调整图像大小(除了调整输出密度或分辨率),并且大概无法以你需要的图像文件格式或你想要的质量输出图像。但你随后总是可以把 GhostScript 的输出馈送给 ImageMagick 来完成任务。也就是说,特别是当你想要对结果进行超采样(更高分辨率的输入,缩放成更小的输出)时。GhostScript 可能是一个难以弄清如何使用、或难以针对特定类型 postscript 进行修正的程序。Cristy 不断地代表 IM 用户与这些问题搏斗,在这方面他做出了卓越的努力。不幸的是,在应对可能(并确实)发生的诸多情况时,IM 无法为通过 GhostScript 处理 postscript/PDF 提供一种简化的方法。
Draw —— 在现有画布上绘制文本
通过使用底层的 "[-draw](https://imagemagick.org/command-line-options/#draw)" 操作符来绘制字体,我们获得了多得多的控制,尤其是对字体的确切位置,以及它所绘制进入的图像尺寸。
magick -size 320x100 xc:lightblue -font Candice -pointsize 72 \
-fill blue -draw "text 25,65 'Anthony'" text_draw.gif
然而要使用它,我们需要生成一个适当尺寸的背景图像来绘制字体,这在绘制某些未知文本时可能很棘手。关于解决此问题的方法,参见字体图像的自动尺寸。除了标准文本选项之外,还有许多额外选项会影响 "[-draw](https://imagemagick.org/command-line-options/#draw)" 实际如何在图像上绘制文本。你不仅可以指定一个 "[-fill](https://imagemagick.org/command-line-options/#fill)" 颜色,还可以指定一个 "[-undercolor](https://imagemagick.org/command-line-options/#undercolor)",以及一个边缘或 "[-stroke](https://imagemagick.org/command-line-options/#stroke)" 颜色,这两者默认都是关闭的(设为颜色 'none')。"[-fill](https://imagemagick.org/command-line-options/#fill)" 颜色也可以被一个 "[-tile](https://imagemagick.org/command-line-options/#tile)" 图像图案替换,而描边边缘宽度可以用 "[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)" 更改。然后所绘制文本的相对位置可以用一个 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 设置更改。例如,这里我使用了我刚提到的许多额外特性。
magick -size 320x100 xc:lightblue -font Candice -pointsize 72 \
-tile bg.gif -undercolor dodgerblue -stroke navy -strokewidth 2 \
-gravity center -draw "text 0,0 'Anthony'" text_options.gif
| _从 IM 版本 6.2.4 起,"[-draw](https://imagemagick.org/command-line-options/#draw) text" 操作不再理解使用 '\n' 表示换行,也不再理解使用百分号 '%' 图像信息转义。(参见绘制百分号的 Bug)。
然而这些能力和问题在新的 IM v6 操作符 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 中仍然可用。参见下文的 Annotate 文本绘制操作符。_
---|---
上述所有选项也可以在 "[-draw](https://imagemagick.org/command-line-options/#draw)"(MVG —— Magick 矢量图形)字符串中使用。然而如果你在 draw 参数内设置上述选项,该选项将只应用于那个特定的 draw MVG 字符串。除此之外,draw MVG 格式还可以做得多得多,例如文本旋转和字体“修饰”,当然你还可以在图像上绘制各种形状,如圆形。例如,这里我们绘制带下划线的旋转文本,叠加在两个背景圆之上。
magick -size 320x120 xc:lightblue \
-draw "fill tomato circle 250,30 310,30 \
fill limegreen circle 55,75 15,80 \
font Candice font-size 72 decorate UnderLine \
fill dodgerblue stroke navy stroke-width 2 \
translate 10,110 rotate -15 text 0,0 ' Anthony '" \
draw_mvg.gif
如果你真想最大限度地利用 "[-draw](https://imagemagick.org/command-line-options/#draw)" 来创建你的图像,我建议你看看绘图示例页面。
底色框
"[-undercolor](https://imagemagick.org/command-line-options/#undercolor)" 颜色设置,如上面以及后面下文所示,会为该字符和字体所定义的绘制区域上色。一般来说它只是恰好贴合所绘制的字符。这在所绘制字体的左右边缘尤其如此,因为上下边缘通常足够大,可以容纳所有字符。绘制区域基本上代表围绕字体绘制区域的字符“单元”边界。使用 "[-undercolor](https://imagemagick.org/command-line-options/#undercolor)" 选项的主要用途,是作为一种简单快捷的方式,清除文本周围“嘈杂”的背景。例如看看在图像顶部添加注释。不过建议你在那种情况下,在所绘制字符串的开头和结尾各额外添加一个空格字符。
边界框溢出
在绘制文本,或一般处理字体时,你可能遇到的最大问题之一是,并非所有字体都遵守正常规则。字体设计者可以把单个字符(或“字形”)“绘制”在相对于当前文本位置(称为 caret,光标)的任意位置。字体位置甚至不必向前移动,在某些国际字体中甚至可能向后移动!这种设计自由的结果是,某些“字形”不适合字体为该字符声明所适合的所定义绘制区域,尤其是在倾斜或脚本类字体上,其中字母的某些部分远远延伸到边界之外,进入后续(或之前)字符所用的区域。我在这方面见过的最糟糕的字体是 'LokiCola' 字体,它把大约一半的大写字母绘制成带有长长波浪尾巴的样子,远远超出单个字符单元的边界。这种字体基本上假设每个大写字母后面会跟着 3 个或更多小写字母。为展示这一点,我将分别绘制该字体的若干个大写字母,让你看到这些字母可能延伸到“undercolor”单元或绘制边界之外多远。我还用其中几个组成该字体的名字,让你看到它们正如设计预期的那样被使用,以及它们为何溢出其边界框。
magick -size 500x200 xc:lightblue \
-font LokiCola -pointsize 72 -undercolor dodgerblue \
-draw "text 15,65 'L'" -draw "text 130,65 'C'" \
-draw "text 245,65 '1'" -draw "text 360,65 'H'" \
-gravity South -draw "text 0,10 'Loki Cola'" draw_undercolor.gif
另外注意 'H' 实际上在其绘制区域的左侧和右侧都溢出了。这会使它在行的开头难以使用。 |
记住这个问题不是 IM 中的 bug,而是由 IM 所用字体库与字体本身设置之间的相互作用造成的,通常是字体设计者有意为之。IM 只是按字体中所编排的那样使用结果,这并不总能产生用户想要的效果。因此对不寻常的字体建议谨慎。 |
|---|---|
Annotate —— 文本绘制操作符
在 IM 版本 6 中,一个新的字体绘制操作符 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 变得可用。这个操作符在许多方面比使用 "[-draw](https://imagemagick.org/command-line-options/#draw) text" 操作简单得多,但由于它使用 'annotate()' API(应用程序接口),它也更强大。虽然该操作符确实利用 "[-draw](https://imagemagick.org/command-line-options/#draw)" 原语,但它以更复杂的方式做到这一点,例如展开特殊的转义字符以添加额外的图像信息甚至多行,以及对所绘制文本应用坐标系变换以产生倾斜和旋转。正因如此,该操作符现在是所有 ImageMagick 文本绘制和图像注释的首选文本绘制操作符,这一点现在也反映在这些示例页面中。下面是使用这个操作符的一个基本示例。
magick -size 320x100 xc:lightblue -font Candice -pointsize 72 \
-fill blue -annotate +25+70 'Anthony' annotate.gif
"[-annotate](https://imagemagick.org/command-line-options/#annotate)" 操作符的一个额外特性是,它可以把所绘制文本的 X 轴和 Y 轴彼此完全独立地旋转。这是通过在操作符参数中以一个“图像尺寸”的形式提供旋转每个轴的角度来完成的。为展示单个 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 操作可以多么复杂,下面是一张带框、带描边且倾斜的图像……
magick -size 320x100 xc:lightblue -font Candice -pointsize 72 \
-tile bg.gif -undercolor dodgerblue -stroke navy -strokewidth 2 \
-annotate 0x20+20+67 'Anthony' annotate_opts.gif
在这个示例中给出了全部四个 annotate 参数:X 轴旋转、Y 轴旋转,以及字体在背景图像上的 X 和 Y 位置。还要注意,填充图案(用 "[-tile](https://imagemagick.org/command-line-options/#tile)" 设置)也随字体一起倾斜。这是因为它是使用一个被剪切/旋转的坐标系绘制的,该坐标系也剪切了平铺在所绘制文本内的填充图案。这种剪切能力的另一个示例是剪切阴影字体示例。把它与用等效的 "[-draw](https://imagemagick.org/command-line-options/#draw)" MVG 字符串创建的倾斜字体作比较。关于总结 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 剪切操作效果的表格,参见 Annotate 参数用法。例如,下面是一些略微旋转的文本……
magick -size 320x100 xc:lightblue -font Candice -pointsize 72 \
-annotate 350x350+20+90 'Anthony' annotate_rotated.gif
| 注意,给 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 的角度必须为正,IM 才能正确理解它)。这个的例外是,如果使用逗号分隔的 4 数字形式的几何参数。例如 "-annotate '-10,-10,20,90' 'Anthony'",本可以在上一个示例中使用。
---|---
这可以用于生成成角度的压缩标注。例如…… |
magick -size 100x60 xc:skyblue \
-annotate 300x300+20+50 "First" \
-annotate 300x300+35+50 "Second" \
-annotate 300x300+50+50 "Third" \
-annotate 300x300+65+50 "Fourth" \
-annotate 300x300+80+50 "Fifth" \
annotated_labels.jpg
![[IM Output]](../static/img/text/annotated_labels.jpg)
你也可以使用转义字符把关于当前图像的其他信息添加到 Annotated 字符串中。例如,让我们用关于图像尺寸的信息覆盖内置的 "rose:" 图像。为把文本居中在图像上,我们使用一个 "[-gravity](https://imagemagick.org/command-line-options/#gravity)" 设置,并通过使用一个 '0' 的 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 参数关闭任何及所有旋转和偏移。 |
magick rose: -fill white -stroke black -font Candice -pointsize 20 \
-gravity center -annotate 0 '%wx%h\nPixels' annotate_rose.gif
![[IM Output]](../static/img/text/annotate_rose.gif)
更多信息参见下文的文本参数中的特殊转义字符。关于以各种方式(例如居中在侧边,或在右下角旋转)把文本注释到较大图像上的其他示例,参见实用文本注释示例。
自动尺寸的注释文本画布
你常常需要比 "[label:](#label)" 所能提供的多得多的控制。例如你想使用一个平铺或渐变图像,这要求你Annotate文本。不幸的是,你随后需要预先知道你的注释文本所需画布的尺寸。下面是这个问题的一个典型示例。我第一次设置这个命令时,我把尺寸设为我想要的结果,起初它工作得相当好。但后来我得到了这个……
magick -size 480x80 gradient:yellow-green \
-font ArialBkI -pointsize 70 -tile gradient:blue-red \
-annotate +10+65 'Gradient Fun' funfont_gradients.jpg
不幸的是,在猜测画布尺寸时,我在上面拼错了单词 'Gradient'(漏了字母 'i')。当然,当我修正那个拼写时,我的图像尺寸现在就错了,产生了上面所示的不正确结果。我们需要的是能够使用 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 操作符,但让画布尺寸适配注释文本。一个解决方案是使用一个大得多的画布,然后把背景 "[Trim](crop.html#trim)" 到正确的尺寸。我还添加了一个 "[Border](crop.html#border)",在字体与图像最终边缘之间增加一点额外空间,以获得更好的外观。
magick -size 800x120 xc:black -font Corsiva -pointsize 100 \
-tile tile_disks.jpg -annotate +20+80 'Psychedelic!' \
-trim +repage -bordercolor black -border 10 funfont_groovy.jpg
这种方法比试图猜测你最终图像应该有多大要好得多,然而 "[画布修剪](crop.html#trim)" 不会修剪一个平铺的多彩背景。更好的解决方案是使用 "[label:](#label)" 创建正确尺寸的画布。然后使用一个绘制颜色填充在画布(以及 label 文本)上平铺一张图像,最后我们使用另一张平铺图像Annotate我们的文本。
magick -font Ravie -pointsize 72 label:'Get Wet!' -border 10 \
-tile tile_aqua.jpg -draw "color 0,0 reset" \
-tile tile_water.jpg -gravity center -annotate +0+0 'Get Wet!' \
autosize_wet.jpg
| 注意,居中的 "[label:](#label)" 图像中文本的位置,可能不会与居中的 "[-annotate](https://imagemagick.org/command-line-options/#annotate)" 操作的文本位置完全匹配。这两种方法遵循完全不同的处理算法,因而可能不匹配,尤其在涉及不寻常字体时。
---|---
使用“底色框”自动尺寸
与其使用一个 "[label:](#label)" 图像,你可以使用一个底色框和一个大描边宽度,在一个大画布上绘制字体,然后再把画布修剪到合适大小。例如
magick -size 500x100 xc:lightblue -font SheerBeauty -pointsize 72 \
-gravity center -undercolor white -stroke none -strokewidth 3 \
-annotate +0+0 ' Invitation ' -trim +repage -shave 1x1 \
invitation_box.jpg
字体周围的空间量可以用 "[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)" 设置来调整。唯一重要的要求是,初始画布是一种不同于背景色的颜色(在这个例子中是 'lightblue'),并且比最终结果大。提个醒,有些字体会把字符绘制到单个字符绘制区域之外很远的地方。(例如参见上面的底色框)。在这种情况下,上面的结果仍能工作,但可能要求你使用一个透明画布,然后把结果叠加到白色之上(例如使用一个像 "-background white -flatten" 这样的操作),把未使用且仍透明的区域变成白色。然而那个字符很可能会接触到生成图像的某个边缘。基本上你不可能在所有情况下都赢,所以尽力而为就好。
为灰度文本图像上色
我特意把上面的图像生成为灰度黑白图像,因为这可以用作一个遮罩模板。从这样一张纯净的图像出发,你随后可以分别或同时为图像的背景和前景上色。例如,这里我使用按颜色分级操作符,"[+level-color](https://imagemagick.org/command-line-options/#level-color)",全局地修改图像颜色,以便为前景和背景分配具有特定值的颜色。
magick invitation_box.jpg -colorspace sRGB \
+level-colors navy,lightblue invitation_colored.jpg
例如,这里我使用合成遮罩,用图案图像替换背景和前景。
magick invitation_box.jpg -colorspace sRGB \
\( +clone -size 300x150 -tile gradient:LightYellow \
-draw "color 0,0 reset" \) \
\( +clone -size 300x150 -tile plasma:tomato \
-draw "color 0,0 reset" \) \
-reverse -composite invitation_rose.jpg
上面的反转操作符用于重新排序图像,使第一张图像成为合成中的第三张“遮罩”图像。前景("plasma:")图像随后成为第一张,背景在中间。关于以这种方式为灰度图像上色的其他技术,参见使用遮罩限制合成区域。更一般地参见对图像使用遮罩。关于生成用于平铺的渐变的其他方法,参见颜色渐变、稀疏色点和随机化画布。
字体
施工中
至于对字体路径排序,那只是对 XML 文件中所指定的字体进行排序。
起点是系统字体,其后是系统安装的
"type.xml" 文件,在我的系统上这是 "/etc/ImageMagick-6/type.xml"。
这个系统安装的 "type.xml" 文件通常只是一个“包含”其他
type-* 文件的列表。包含的顺序将指定
额外系统字体相对于 Ghostscript 字体的顺序。
在该文件之后,会查找其他 "type.xml" 文件,例如在“home”
目录中,甚至当前目录中。
后面的字体不会替换前面的字体,因此如果两个字体有
相同的名字,只有第一个会被 IM 注意到。(一项安全措施)。
要查看已加载的字体,使用
magick -list font
它列出每个字体列表所在 type 文件的 "Path:",但
这些路径以逆序列出,系统字体在最后。
例如,我有一个个人字体名为 "Courier",但它没有列在
上面的列表中,因为它是在“系统字体”区域中找到的那个 "Courier"
之后定义的(后者列在上面输出的末尾)。
另一方面,我自己的个人字体 "CourierNew" 被列出了,因为它不
与任何系统或系统配置定义的字体冲突。
要查看某个具体请求所选择的字体字形文件,
使用……
magick -debug annotate xc: -font Courier \
-annotate 0 'Test' null: 2>&1 |
grep '^ *Font '
不使用 API 确定字体度量
一种特定字体及其单个字符包含大量信息。这些信息可以非常有用,尤其当你想使用 IM 拼接许多不同字体的文本时。还要记住的重要一点是,大多数字体是比例字体,意味着每个单个字符将有不同的宽度,以及不同的光标(或原点)“自然”前进量。因此,每个具体的字符“字符串”被渲染(绘制)的长度都不同,与字符串中实际使用的字符数量并无真正关系。这方面的例外是“等宽”字体,例如 "Courier"、"Typewriter" 或 "Terminal" 字体,其中所有字符都有相同的宽度,让你能轻松生成文本列。调试设置 "[-debug](https://imagemagick.org/command-line-options/#debug) annotate" 可用于让 IM 直接报告一个 TTF 字体针对特定字符串的度量。例如……
magick -debug annotate xc: -font Candice -pointsize 24 \
-annotate 0 'Test' null: 2>&1 |\
grep Metrics: | fmt -w80
| 正如你所见,你得到了一堆可以使用的混杂信息:从所绘制字符串相对于原点的声明边界(这不一定是字符串的实际边界);到在绘制下一个字符串之前“光标”(原点)应当前进的量。完整的调试输出(相当冗长,上面未显示)还报告实际使用的字体文件(两次),所以你也可以用它来检查你是否使用了正确的字体。 | "[-debug](https://imagemagick.org/command-line-options/#debug) annotate" 方法在 IM v6.3.9-2 中添加。 |
|---|---|
较旧的技术
不过这个调试输出可能不方便,或者你可能不得不处理比此版本更旧的 IM。下面是一些较旧的示例,其中文本实际上以各种方式和颜色绘制,然后从生成的图像中提取信息(作为整数)。例如,让我们找出 'Ravie' 字体在 72 点下相对于一个固定基线的尺寸。 下面是我们将研究的图像,作为参考。你实际上不需要把它绘制并保存为图像,因为我们只是在提取数据,而不是图像。这张图像的颜色将被修改,以便我们可以使用 "[-trim](https://imagemagick.org/command-line-options/#trim)" 提取所用的度量,分别查看白色和黑色部分。 |
magick -size 100x150 xc:lightblue -font Ravie -pointsize 72 \
-fill black -undercolor white -annotate +20+100 'A' font_drawn.gif
![[IM Output]](../static/img/text/font_drawn.gif)
对于基本的字体度量,我们首先用一种透明颜色('None')绘制字体本身,以便我们能测量找出这个具体字符在这个字体下的边界框或绘制区域的尺寸和位置。注意对于高度信息,你可以绘制任何东西。
magick -size 100x150 xc:lightblue -font Ravie -pointsize 72 \
-fill none -undercolor white -annotate +20+100 'A' -trim info:
从上面的结果我们可以看到,一个 'Ravie' 字体在 72 点下,将有 74 像素的总边界框高度。框的顶部距图像顶部 42 像素,由于基线被定位在 y 坐标 100 像素处,框开始处在基线上方 100 - 42 即 58 像素。这就为边界框在基线以下、给降部留下了 74 - 58 即 16 像素。 |
注意,并非所有字体都把它们的绘制限制在其所定义的绘制边界框之内!然而有些字母可能延伸到那些边界之外很远。这就是为什么上面的示例把 "[-fill](https://imagemagick.org/command-line-options/#fill)" 颜色设为 'none'。这样行为不端的字体就不会影响上面的测量。 |
|---|---|
还要注意,分隔行(实际上是基线)的距离应当纯粹由字体的点大小决定,与字体如何绘制无关。在我们的示例中,由于字体的点大小为 72 点,而一点被定义为 1/72 英寸,那么基线之间应当相距 1 英寸。在当前输出分辨率(密度)为每英寸 72 像素下,这意味着基线将相距 72 像素。有趣的是,这意味着对于这个字体,在 74 像素的边界框下,字体在正确单倍行距的文本行之间的绘制区域有两个像素的重叠!同样从上面的测量我们可以看到,在这个字体、这个点大小下绘制字符串 "A" 时,下一个字符应当绘制在起点(称为 caret,光标)右侧 66 像素处。这是字符串的“逻辑”长度。也就是说,下一个字符的“光标”或起点应当从 20 + 66,即在 '+86+100' 处开始(基线不会垂直变化)。要注意有些阿拉伯字体实际上可以从右向左绘制,所以“光标”偏移会是负的。这给了我们字符 'A' 的字体度量,但所绘制 'A' 相对于“光标”或起点的物理尺寸又如何呢?嗯,只需交换那两个颜色设置…… |
magick -size 100x150 xc:lightblue -font Ravie -pointsize 72 \
-fill black -undercolor none -annotate +20+100 'A' -trim info:
| 在高度方面,这个字符在其所定义的绘制边界之内,其高度从基线上方 100 - 43 即 57 像素(紧贴其边界框),到基线下方仅 60 - 57 即 3 像素。换句话说,这个字母在基线以下区域没有绘制“降部”。由此我们可以看到,'A' 绘制范围从光标 之前 3 像素(定位在 +20 处,但最终图像在 +17 处),到光标位置 之后 70 - 3 即 67 像素。换句话说,这个字体在绘制时比其水平边界框略宽。注意,虽然这给了你实际绘制的字符串长度,但这与在追加文本时所需的光标偏移不同(后者由字符串的边界框定义,而非其绘制长度)。换句话说,文本应当使用它们的边界框来追加在一起,而非像我们在其他示例中所做的那样使用它们的实际绘制长度尺寸。当然,如果你遇到一个非常行为不端的字体,你可能想检查某个具体字符串绘制超出其边界多远,以便你仍能为它提供空间,比如在行尾。 | 从字体提取的尺寸也会随绘制字体所用的当前 "[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)" 而变化。如果你增大轮廓描边的大小,那么绘制字体所需的尺寸(和边界框大小)也会增大相同的量,以容纳更粗的轮廓。 |
|---|---|
| 尺寸也会随操作系统(类型和版本),以及 IM 在该系统上所用委托字体绘制库的版本而变化,即使确切相同的字体库和 IM 版本没有改变。当不同的计算机可能被用于文本绘制时建议谨慎,因为即使是同一字体,结果也可能不同。 | |
| --- | --- |
| 更多信息参见文档 TrueType 基础(PDF)。这表明即使我上面的一般性概括也可能并非总是成立,尽管通常如此。注意上面的示例只会以整数像素返回尺寸,而字体所用的所有尺寸都是浮点数。事实上,一个字体是否甚至从一个整数像素(光标)起点开始绘制,可能依赖于应用程序,并影响字体的最终外观。 |
创建混合字体样式的文本行
使用多种字体、点大小和样式创建单独一行,并不是 IM 真正擅长做的事情。当你还开始考虑诸如文本两端对齐、自动换行,以及围绕图像和其他东西换行时,情况会变得更糟。这是字处理软件、网页浏览器、文档打印机做得非常好的那类事情,通常是在用户交互下进行的,但很少有能在程序控制下做得这么好的。这方面的一个例外是 "TeX" 及其程序家族(参见下文的一套完整的文本处理系统),所以如果你认真地想要以图形方式处理文本,我建议你看看这个程序家族。另一个备选方案是看看各种文档“美化”打印程序,例如一个 HTML 转换器。你可以用这些把程序生成的文档变成 postscript,IM 随后可以愉快地把它后处理成你想要的任何图像格式或样式。"El Supremo"(来自 IM 讨论论坛)创建了一个 API 解决方案(使用 C MagickWand API 接口),在他的 FontMetrics 程序中。这里是 "FontMetrics" 的示例输出。
现在,虽然 IM 命令行不是为“字处理”而设计的,但这并不意味着你不能用它来做。只是更困难而已。这里我将给出一些混合不同字体和样式文本的示例,给人们一个起点。人们通常想到的最简单的解决方案,就是把 "[label:](fonts.html#label)" 图像追加在一起……
magick -font LokiCola -pointsize 36 label:'LokiCola ' \
-font Candice -pointsize 24 label:'Candice ' \
-font SheerBeauty -pointsize 48 label:'SheerBeauty' \
+append wp_label_append.jpg
然而正如你所见,所有图像都垂直对齐到图像顶部,除非你使用相似的字体,否则看起来不会很好。或者,你可以使用一个追加对齐技巧,把它们沿底部对齐。
magick -size 1x50 xc:none +size \
\( -background white -font LokiCola -pointsize 36 \
label:'LokiCola ' \
-clone 0 +swap -background none -append \) \
\( -background white -font Candice -pointsize 24 \
label:'Candice ' \
-clone 0 +swap -background none -append \) \
\( -background white -font SheerBeauty -pointsize 48 \
label:'SheerBeauty' \
-clone 0 +swap -background none -append \) \
-delete 0 -gravity South -crop 0x50+0+0 +append \
-bordercolor none -border 1 -trim +repage \
-background white -flatten wp_label_bottom.jpg
这样做是在每个 label 顶部添加一些额外填充,并在把它们水平追加之前把它们全部裁剪到相同高度。之后用一个简单的 "[-trim](https://imagemagick.org/command-line-options/#trim)" 和一个 "[-flatten](https://imagemagick.org/command-line-options/#flatten)" 把这行的高度设为最高的 label,并填入背景。正如你所见,这产生了更好的效果,但小字体往往会产生类似下标的行为,而不是正确对齐的文本。我们真正需要做的是按所有文本字符串的“基线”对齐它们,而那在没有访问更多文本信息的情况下非常困难。这种信息在程序 API 下很容易获得,但从命令行困难得多。前一节展示了一种方法。然而,不实际收集基线信息也能按基线对齐单词。虽然 "[label:](fonts.html#label)" 文本图像不提供任何关于图像基线的线索,但你可以专门在一个固定基线上绘制图像。在没有 API 的情况下,你也不能直接找出所绘制文本有多长或多高,所以你首先需要使用一个足够大的画布,以确保我们不会丢失任何关于文本图像的信息。然后,为保留尾随空格和文本高度,你还必须充分利用文本注释可用的("[-undercolor](https://imagemagick.org/command-line-options/#undercolor)")特性,为图像修剪提供一个边界。那么让我们看看你如何从命令行做到这一点。
magick -size 500x100 xc:none -fill blue -draw 'line 15,0 15,99' \
-undercolor white -fill black \
\( -clone 0 -font LokiCola -pointsize 36 \
-annotate +5+60 'Loki Cola ' \) \
\( -clone 0 -font Candice -pointsize 24 \
-annotate +5+60 'Candice ' \) \
\( -clone 0 -font SheerBeauty -pointsize 48 \
-annotate +5+60 'Sheer Beauty' \) \
-delete 0 -trim +repage +append \
-transparent blue -trim +repage \
-background white -flatten wp_draw_baseline.jpg
和之前一样,图像的修剪分两步进行。首先在一个含有一条垂直蓝线的基础图像上绘制文本。这样当我们修剪文本时,只有文本图像的宽度会被修剪,使所有单词保持在相同的基线高度。在把它们追加在一起后,我们现在可以通过把那条蓝色构造线变为完全透明来移除它。如果你只是生成一张黑白图像,更好的办法是改为提取其中一个非蓝色通道,这能确保你真正得到全部的构造线。然后第二次修剪会修剪掉顶部和底部部分,把它缩小到最大的边界框。最后一次扁平化到与边界框相同的颜色,随后就消除了它在构造这行时被使用的一切痕迹。正如你所见,所有文本现在都正确地按基线对齐了,无论使用何种字体或点大小。当然,在这些示例中我只用了白底黑字的文本。可以使用其他颜色,只要它们不干扰用于文本对齐的构造线和透明背景。有了这种技术,你现在可以生成混合字体文本行,并把它们全部垂直追加在一起,成为一个更大的文档。你也可以看到,做这一切是大量的工作,是字处理软件和网页浏览器通常对用户隐藏的工作。如果你确实打算大量做这类事,我确实建议你研究一下我之前提到的备选方案。
表单填充
你有一张某个标准填写表单的图像,你想填写那些处于已知位置的字段。所以你有一个数据文件,例如这里所示的 "[text_data.txt](../static/img/images/text_data.txt)"……
这些字段是文本宽度、引力(gravity,对齐)、颜色、位置 x、y,以及这个字段要放置的实际文本。现在你可以使用一个简单的循环 shell 脚本,按照上面所述生成一个文本 label,把适当的文本分配到一个表单(背景)图像中所定义的文本位置。
cat text_data.txt |
while read width gravity color pointsize x y text
do
magick -size ${width}x -gravity $gravity -fill $color -background wheat \
-pointsize $pointsize -page +${x}+${y} label:"${text}" miff:-
done |
magick -size 200x100 xc: - -flatten text_layered.jpg
这个例子中的“表单图像”只是一张空白图像,但实际上可以是任何东西。我还把 label 的背景色设为 'wheat',以便填入的区域可见,但你通常会把它设为 none。上面这个不使用临时文件,而是使用一个 MIFF 格式图像的管道。这是使用图像流式格式的一个示例,其中单个图像只是被一个接一个地追加在一起,在一个文件或管道中。这只是一个起点。表单字段可以来自某个定义文件,而要填入的文本来自一个数据库或其他数据源。还可以设置其他属性,例如要使用的字体、文本旋转等等。你也可以同时包含宽度和高度,或者如果文本应当被自动换行,应使用 Caption 而不是 Label。另请参见地图上的图钉,那是这种技术的另一个示例,与上面非常相似。
文本处理的备选方案
| 生成完全格式化的文本文件和文档的理想方式,是把 ImageMagick 用作一个更大的图像和文本处理系统的一部分。 工具 | 用于…… |
|---|---|
| ImageMagick | 图像批量处理和准备 |
| Gimp | 用于一次性问题修复的 GUI 图像编辑 |
| LyX | GUI 字处理,构建用于生成…… |
| LaTeX | 用于文档和书籍的文本处理器…… |
| TeX | 底层文本格式 |
| (在页面上定位符号和字体) | |
| Metafont | TeX 字体生成器 |
| 基本上 ImageMagick 可以做很多事情,但这并不意味着它是做那些事情的最佳工具。对于较大的文档准备,你最好把它当作一个更大整体的一部分。上面给出的各种 'TeX' 工具通常是大多数 Linux 系统的标准安装,可以把文本和图像结合成一个统一的整体。更重要的是,它把文本保持为文本,并按你所指定的那样恰当地格式化文本,做几乎所有的字词和分页换行、以及与图像排布的繁重工作。但不会把一个 'doc' 文件填满无用的格式化垃圾。你拥有完全的控制,也可以把决定留给它来做。它们提供了一种生成任何类型文档的方式,从一个简单的页面、通讯,乃至一整本书。如果你认真对待文档生成,那么这些工具非常值得一看和学习。 | |
| Pango(仅限 Linux 和 MacOSX)也提供了一个备选方案。它提供了许多 ImageMagick 中没有的文本到图像处理特性。例如 TAB、两端对齐、边距、页眉等等。它甚至有某种标记语言,允许在文本中途更改字体。 | |
| 其他解决方案还包括许多文本到 postscript 转换程序,例如 "a2ps",我在上面的 Postscript 处理中演示了用它生成一个示例 postscript 文件。它转换并格式化许多不同类型的文本文件,带有自动换行、加粗和制表符控制,以及相当不错的页眉、页脚、边框和多页选项。当然这是通过一个 Postscript 或 PDF 中间语言进行的间接图像处理。另一种是使用 SVG,或 ImageMagick 绘图命令来排布文本,不过你随后需要处理排布。外面有很多把文本变成图像的工具,大多数都可以与 ImageMagick 结合,以后处理文本图像并把它合并到你的图像中。这让 ImageMagick 能专注于它最擅长的事,图像处理。 |
![[IM Output]](../static/img/text/label.gif)
![[IM Output]](../static/img/text/label_file.gif)
![[IM Output]](../static/img/text/label_file_multiline.gif)
![[IM Output]](../static/img/text/caption.gif)
![[IM Output]](../static/img/text/caption_centered.gif)
![[IM Output]](../static/img/text/caption_height.gif)
![[IM Output]](../static/img/text/caption_height_toosmall.gif)
![[IM Output]](../static/img/text/caption_filled.gif)
![[IM Output]](../static/img/text/caption_multi_line.gif)
![[IM Output]](../static/img/text/caption_file.gif)
![[IM Output]](../static/img/text/caption_one_line.gif)
![[IM Output]](../static/img/text/caption_manual.gif)
![[IM Output]](../static/img/text/label_i8n.gif)
![[IM Output]](../static/img/text/label_utf8.gif)
![[IM Output]](../static/img/text/label_quotes.gif)
![[IM Output]](../static/img/text/label_unifun.gif)
![[IM Output]](../static/img/text/label_dingbats.gif)
![[IM Output]](../static/img/text/label_misc.gif)
![[IM Output]](../static/img/text/label_lspace_off.gif)
![[IM Output]](../static/img/text/label_lspace_5.gif)
![[IM Output]](../static/img/text/label_lspace_10.gif)
![[IM Output]](../static/img/text/label_lspace_20.gif)
![[IM Output]](../static/img/text/label_lspace-5.gif)
![[IM Output]](../static/img/text/label_lspace-10.gif)
![[IM Output]](../static/img/text/label_stdin_2.gif)
![[IM Output]](../static/img/text/pango_span.gif)
![[IM Output]](../static/img/text/pango_test.png)
![[IM Text]](../static/img/text/page_size.txt.gif)
![[IM Output]](../static/img/text/text_trimmed.gif)
![[IM Output]](../static/img/text/text_boxed.gif)
![[IM Output]](../static/img/text/text_box_trimmed.gif)
![[IM Output]](../static/img/text/text_page.gif)
![[IM Output]](../static/img/text/ps_version_raw.gif)
![[IM Output]](../static/img/text/ps_version.gif)
![[IM Output]](../static/img/text/ps_unsharp.gif)
![[IM Output]](../static/img/text/ps_transparent.png)
![[IM Output]](../static/img/text/ps_bgnd_color.gif)
![[IM Output]](../static/img/text/ps_bgnd_tiled.gif)
![[IM Output]](../static/img/text/ps_multiply.gif)
![[IM Output]](../static/img/text/ps_overhead.png)
![[IM Output]](../static/img/text/text_draw.gif)
![[IM Output]](../static/img/text/text_options.gif)
![[IM Output]](../static/img/text/draw_mvg.gif)
![[IM Output]](../static/img/text/draw_undercolor.gif)
![[IM Output]](../static/img/text/annotate.gif)
![[IM Output]](../static/img/text/annotate_opts.gif)
![[IM Output]](../static/img/text/annotate_rotated.gif)
![[IM Output]](../static/img/text/funfont_gradients.jpg)
![[IM Output]](../static/img/text/funfont_groovy.jpg)
![[IM Output]](../static/img/text/autosize_wet.jpg)
![[IM Output]](../static/img/text/invitation_box.jpg)
![[IM Output]](../static/img/text/invitation_colored.jpg)
![[IM Output]](../static/img/text/invitation_rose.jpg)
![[IM Text]](../static/img/text/font_metrics.txt.gif)
![[IM Text]](../static/img/text/font_boxinfo.txt.gif)
![[IM Text]](../static/img/text/font_drawn.txt.gif)
![[IM Output]](../static/img/text/wp_label_append.jpg)
![[IM Output]](../static/img/text/wp_label_bottom.jpg)
![[IM Output]](../static/img/text/wp_draw_baseline.jpg)
![[IM Text]](../static/img/text/text_data.txt.gif)
![[IM Output]](../static/img/text/text_layered.jpg)