ImageMagick中文使用手册:文字写入图片处理用法教程

创建文本标签或添加文本到图像中,可能是使用ImageMagick进行的最基本也是最常见的操作之一。同时,它还是最简单,但又能产生一些非常奇妙结果的操作。因此这是一个很好的选择,用来开始我们对IM能力的探索。

ImageMagick的文本操作选项

ImageMagick中有很多不同的方式可以用来在一张图像中绘制文本,这全靠了它强大的多功能图像处理库。本页的将详细介绍各种文本绘制的具体方法和风格。

当你研究这些例子时必须要记住的是,ImageMagick主要是一个图像转换器和修改器。因此每个方法中提供的都是一些简单的文本绘制选项,如将标签和版权信息添加到图像中。参见Annotating Images。

所有这些文字操作选项也支持和使用一套标准的文本处理设置,如命令里使用的“-font”和“-pointsize”设置等。同时还包括“-fill”颜色设置和更复杂文本使用的“-strokewidth”、“-stroke”和“-undercolor”等选项。实际上,当你创建一张新的图像时,例如标签和标题,那么可能还要使用“-background”颜色设置。

最后还有新加入的字距调整(-kerning)和词间的间距(-interword-spacing)选项。

但是ImageMagick并不是一个很好的格式化文本或文档处理器。如果你想进行高级的文字处理,你最好使用一个完整的交互式文字处理器,或者文本批处理器,如“TeX”(或与它相同的工具,参看下面介绍的完整文字处理系统)。这些方案的输出(一般是PostScript格式)接下来可以被转换成图像,然后使用ImageMagick来进一步修改,也就是说,使用合适的工具进行合适的工作。

这就是说可以完成一些混合的字体处理。详情参考本页最后介绍的创建混合字体样式的线条。

现在,让我们一起学习将文本转换成图像的基本途径,接下来我们再看看如何生成一些有趣的字体效果。

标签——简单的文本标签

基本标签

通过使用“label:”图像命令就可以创建一张字体图像,这是ImageMagick中比较典型的快速绘制字体的方法。这种方法的最大好处是,可以根据当前的-background和-fill颜色设置生成自己的画布,用来匹配绘制文本的尺寸。

例如,这里是一个典型的生成标签的例子。

      convert -background lightblue -fill blue \
                -font Candice -pointsize 72 label:Anthony \
                label.gif

在上面大概是最典型的添加标签的方法,通过选择字体(-font)和字号(-pointsize)来定义结果图像。但是,这也是生成文本标签的最没趣的方法。


使用“label:”命令生成标签图像,也会在图像属性设置的元数据中产生相同的字符串。某些文件格式,例如MIFF和PNG,将保存这些特定的信息并影响或在以后的图像处理操作中使用。使用标签(label)元数据的例子,参看Montage using Saved Meta-Data。

如果你还指定了-size选项,那么生成的标签图像将被创建为这个设定的尺寸。

      convert -background lightblue -fill blue  -font Candice \
                -size 165x70 -pointsize 24 label:Anthony     label_size.gif

你还可以使用-gravity选项在更大的区域中确定文本标签的位置。

      convert -background lightblue -fill blue  -font Candice \
                -size 165x70  -pointsize 24  -gravity center \
                label:Anthony     label_gravity.gif

当然,如果你不使用-size选项设置标签的尺寸,那么在“label:”生成的标签中就不会有多余的空间,将导致使用-gravity选项不会有任何效果。

同时使用“-size”和“-pointsize”选项的问题是,文字可能会“溢出”指定图像的区域。

      convert -background lightblue -fill blue  -font Candice \
                -size 165x70  -pointsize 72  -gravity center \
                label:Anthony     label_overflow.gif


在6.5.2-4版本之前,IM将完全忽略-pointsize设置,如果命令中同时给定了-size设置。这将导致上面的图像文本根据最合适原则来自动调整尺寸(见下一组示例)。

最合适的文本图像

使用“label:”命令来生成指定尺寸(-size)图像的秘诀就是不要为标签文字指定-pointsize选项。如果发生了这种情况时,IM将会自由尝试和选择最适合图像尺寸的字体大小。也就是说绘制的文本将被进行调整,以匹配给定的图像尺寸!

      convert -background lightblue -fill blue  -font Candice \
                -size 165x70  label:Anthony     label_size_fit.gif

正如你所看到的通过设置-size选项,你可以在图像的右侧或者下方得到一些额外的空间。


当IM创建了一个最合适的标签时,它所使用的实际字号(pointsize)也将保存到“label:pointsize”图像属性中,以便你以后使用这些信息。这是在IM v6.6.2-7版本中新加入的,经过在IM论坛中对pointsize进行讨论决定的。

你仍然可以通过使用-gravity选项在这些额外的空间中对文本标签的位置进行调整。

      convert -background lightblue -fill blue  -font Candice \
                -size 165x70 -gravity center label:Anthony     label_size_gravity.gif

当然,如果你不对标签进行-size设置,那么“label:”命令就不会生成多余的空间用来让-gravity选项进行位置调整,所以只有当你的图像指定了大小尺寸时,它才会有意义。

现在有个最好的消息。如果-size选项设置中,你只给定了标签的宽度或者高度,那么字体将按照给定的维度被调整到最适合的尺寸,那些未指定的其它维度,将自动进行调整,以适应文本!

      convert -background lightblue -fill blue -font Candice \
                -size 160x  label:Anthony     label_size_width.gif

基本上,这意味着上述“label:”命令生成的图像将永远是160像素宽,并且设置为匹配这个宽度的最大字体。然后自动调整标签的高度以适应文本标签。

如果指定的是高度而不是宽度,程序也会进行相应的操作。

      convert -background lightblue -fill blue -font Candice \
                -size x40  label:Anthony     label_size_height.gif

这个标签的高度为40像素,由于未定义文本的pointsize,因此可以自动进行调整以适应这个高度,然后由于未定义图像宽度,那么会自动调整为适合绘制文本尺寸的宽度。正如你所期望的。

当然,在这种情况下并不会在图像中产生额外的空间,所以设置-gravity选项也不会产生效果。

多行标签

“label:”命令还可以用来生成多行的文本标签(在IM v6.2.5版本)。

      convert -background lightblue  -fill blue  -font Ravie -pointsize 20 \
                label:'ImageMagick\nRules - OK!'     label_multiline.gif

正如你所看到的“label:”命令中支持使用“\n”来代表换行符。这意味着你可能要预先处理输入文本,以确保放置在命令行上的数据中任何可能的转义字符都正确。详情请参阅下面介绍的文本特殊转义字符参数。

-gravity选项同样可以影响“label:”命令生成标签的效果(IM v6.2.6版本),你可以用它来调整多行标签的位置。

      convert -background lightblue -fill blue -font Corsiva -pointsize 24 \
                -gravity center    label:'ImageMagick\nExamples\nby Anthony' \
                label_centered.gif

IM的另一个重要特性就是,它可以从一个文件中读取需要使用的文本数据。这可以通过给文件名添加前缀字符“@”,并将它作为字符串参数来实现。

例如,下面我在这里创建一个标签,从我的工作站文件中提取信息。

      convert -background lightblue  -fill blue \
                label:@/etc/motd   label_file.gif

你也可以从标准输入通道中读取标签的文本内容。例如在这里,我将引用发生器的输出转换成多行标签进行显示。

      mesgs ImageResolution |\
          convert -background lightblue  -fill blue \
                  label:@-   label_file_multiline.gif

请注意,其中我使用的文件名只是一个“-”字符。这就表示该文件是从标准输入读取的。记住你可以使用“@文件名”格式将任何命令行字符串参数读入IM中。这包括下面介绍的所有其它文本输入方法。但是它只能用于替换整个字符串参数,而不是字符串参数的其中一部分。

还要注意,在上面的例子中,有一个额外的空白行被添加到标签图像中了。这是由于输入文本文件中最后的换行符产生的空白行。除非你用某种方式从输入文件中(参见下面的“caption:”中介绍的方法来解决这个问题)删除最后的换行符,否则“label:”命令永远会从输入的文本文件中读入一个空行。


大部分旧版本的IM(v6.2.5版本之前)并不支持处理多行标签。在这些版本中,多行文本会被连接在一起,形成一行单一的很长的文本。

垂直标签

当然,你也可以通过在输入文字中添加换行符来实现。举例来说,在这里我选择了一个简单的单词,并在每一个字母之间添加了换行符,以创建一个居中的垂直文本。

      echo -n "Vertical" | sed [url=]'s/./&@/g[/url]; s/@$//' | tr '@' '\012' |\
          convert -background lightblue -fill blue -font Ravie -pointsize 24 \
                  -gravity center    label:@-   label_vertical.gif

请注意,“sed”命令在每一个字符后都添加“@”字符,除了字符串的末尾处。然后“tr”命令使用了换行符来替换所有的“@”字符。它还假定了输入文本不采用换行符作为结束,这将导致结果图像在底部产生一个额外的空白区域。

用户运行的Linux,因此使用GNU版本“sed”命令的用户就可以删除“tr”,直接在“sed”命令中用“\n”取代“@”,这样它就直接在每个字符之间插入新行了。

也可以参考特殊属性Inter-Line Spacing,用来调整字符之间的间距。

标题——包裹文字的标签

通过文本输入器生成的“caption:”图像,在很多方面就和“label:”完全一样,除了这个选项并不是放大文本的尺寸,以适应指定的“-size”设置,而是将任何不合适的很长一行文字包裹到“-size”指定的宽度区域中。

-size选项设置是不可选的,但是你至少必须使用像素作为单位来指定图像的最大宽度。

例如,这里是一行很长的文字标题,并且和指定的图像宽度并不匹配。

      convert -background lightblue  -fill blue  -font Corsiva -pointsize 36 \
                -size 320x   caption:'This is a very long caption line.' \
                caption.gif


“caption:”生成的图像,同样会将“caption”图像属性的元数据设置到相同的字符串中,让你在之后可以重新使用这些信息。所有常见的图像文件格式都会保存这些图像信息。参看Montage using Saved Meta-Data的例子。

默认情况下,所有文本都是左对齐的,但在IM v 6.2.0版本中,“caption:”命令也支持使用-gravity选项进行文本对齐设置。

      convert -background lightblue  -fill blue  -font Candice -pointsize 40 \
                -size 320x  -gravity Center  caption:'ImageMagick Rules OK!' \
                caption_centered.gif

如果你在-size选项中还提供了一个高度数值,不仅仅设置了宽度,那么图像的高度将会设置为那个值。然后,你同样还可以使用-gravity进行设置,将文本垂直放置。

      convert -background lightblue  -fill blue  -font Gecko -pointsize 32 \
                -size 320x100  -gravity South caption:'Captions at their height!' \
                caption_height.gif

但是请注意,如果你对文本指定的pointsize并不适合放入-size设置的高度中,那么文字将从文本框中溢出。当前的-gravity设置将会确定文字的哪一部分被切掉。

例如,下面这个例子与前面的例子完全相同,但是其中-size选项设置的尺寸对结果而言太小了。

      convert -background lightblue  -fill blue  -font Gecko -pointsize 32 \
                -size 320x60  -gravity South caption:'Captions at their height!' \
                caption_height_toosmall.gif

最佳匹配标题

IM v6.3.2版本中,如果你提供了最终图像的宽度和高度,但没有定义字体的pointsize(或者使用“+pointsize”命令关闭了pointsize设置),这时IM将会尝试自动调整字体的大小,以最好的匹配你所要求的图像尺寸(-size)。

例如,在这里我要求ImageMagick去覆盖一个面积相当大的区域。

      convert -background lightblue -fill blue -font Candice -size 320x140 \
                caption:'This text is resized to best fill the space given.' \
                caption_filled.gif

现在我们采用一个小得多的区域,并且使用相同的字体和文本字符串。

      convert -background lightblue -fill blue -font Candice -size 80x110 \
                caption:'This text is resized to best fill the space given.' \
                caption_filled_sm.gif

请注意,最后两个例子之间唯一的区别就是产生图像的-size选项设置。IM对文本和文字进行了最佳尺寸调整,来尝试和对指定的图像尺寸进行最好的填充匹配。

它对于将未知位数的文本字符串放入一个指定的空间中非常有用,如果没有这个功能文本将会溢出区域的边界。但是,在内部它就相当于多次运行添加标题命令,直到IM找到匹配给定区域的最佳字体尺寸。换句话说,它往往比你如果提供了一个指定的“-pointsize”设置进行相同的操作要慢10倍或更多。

标题与段落

“caption:”图像命令中(如IM v6.2.5版本)支持使用shell转义符“\n”(因此你需要使用双反斜线“\\”表示反斜杠字符),这表示一个新的行或段落。在这个版本之前绘制分开的段落必须单独多次使用“caption:”命令。

      convert -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:”命令中一样的用法。

      mesgs FilePrivate |\
          convert -background  lightblue  -fill blue  -pointsize 12 \
                  -size 320x  caption:@-  caption_file.gif

正如你所看到的,在输入文本中的换行符(如IM v6.2.5版本)将被视为段落分隔符。这包括输入文件中任何最后的换行符。当然“label:”命令并不会对文字行进行换行,而是保留它们。

如果你确实需要一个文件被作为一个段落进行处理,那么你必须将换行符替换为空格字符,这样你所有的文字就是一行了。例如,在这里我们采用了相同的文字内容,但用空格替代换行符,然后将每个单词之间的连续多个空格字符替换为一个空格。

      mesgs FilePrivate |      tr '\012' ' ' | sed 's/  */ /g' |\
          convert -background  lightblue  -fill blue  -pointsize 12 \
                  -size 320x  caption:@-  caption_one_line.gif

正如你所看到的,这个方法工作得更好。

但是,通常你想要的是将空白行作为一个段落符来对待。这意味着你需要删除所有换行符,除了那些空白行中的。这里有一个特殊的“sed”命令可以将这样的文本转换成“caption:”所需的格式。在这种情况下,文本将是“convert”联机帮助页的第一页。

      man convert | col -b | expand | \
          sed '/^$/d; :loop y/\n/ /; N; /\n$/! b loop;  s/   */ /g; s/^ //' |\
            head -n 7 |    convert -size 400x  caption:@-  caption_manual.gif

文本属性

通常可以影响文本处理效果的设置包括:“-font”、“-fill”、“-pointsize”、“-size”和“-gravity”。我们在上面的介绍中已经详细讲解了其中许多控制属性。但是实际上还有许多不经常使用的控制属性,并且它们通常不会影响“label:”和“caption:” 生成的文字图像。

比如IM v6.3.2版本中,还可以在“label:”或“caption:”命令中使用“-stroke”、“-strokewidth”和“-undercolor”设置。例如在这里,我使用了很多不同的设置来控制IM文字图像的渲染属性。

       convert -background white -fill dodgerblue  -font Candice \
                 -strokewidth 2  -stroke blue   -undercolor lightblue \
                 -size 165x70 -gravity center label:Anthony     label_color.gif

了解更多这些选项的细节信息,请参看下面的undercolor区域介绍,以及绘图部分关于-stroke和-strokewidth的介绍。


现在,你在“label:”或“caption:”命令中不能使用平铺拼接图像定义选项,如“-tile”、“-fill”、“-background”和“-origin”,而只可以使用纯色背景。如果你要尝试这种做,最终将会产生一个未定义颜色(黑色)。

字号、密度和实际字体尺寸

像素指的显示屏或图像上的点,这就是IM工作的单位。

另一方面,图像将显示为指定的分辨率(指定为点数每英寸(dpi)或像素每英寸(ppi))。由于图像的分辨率将会影响其它方案将图像显示到一个指定尺寸媒体中的方法。例如:它将会影响图像在现实世界中的物理尺寸。

图像的分辨率(密度或dpi)与图像的像素尺寸、图像所占据的内存或磁盘空间无关。同时,在一般情况下,它也与IM大多数的图像操作选项无关。因此,对于ImageMagick而言,分辨率只是一组存储在图像中的数字,并且通常被忽略。其中唯一需要使用图像分辨率或密度的地方就是IM处理字体和将矢量格式图像如PostScript、PDF或MWF转换为光栅图像格式的时候。

-density选项设置会告诉IM在输出设备上每英寸范围内显示了多少个像素点(PPI),它可以在接下来的操作中用来调整生成的图像,并与字体尺寸相匹配。

例如在默认情况下,IM的-density选项设置为72 ppi,这是图像显示在监视器或网页上时的一个典型设置。由于字体大小被指定为“点”(使用-pointsize选项),并且根据定义1点等于1/72英寸,所以72点的字体显示出来的文字尺寸大约是1英寸高。

      convert -pointsize 72 label:Hello  pointsize.gif

然而,大多数现代化的显示器拥有比这更好的分辨率,通常介于90至120像素每英寸(PPI)之间。

convert -density 90 -pointsize 72 label:Hello  density.gif

上述命令应该产生一个标签,它在90 dpi的显示器上是1英寸高,在我的显示器上就是这样的!你可以通过测量屏幕上这张图像的高度,来检查你的显示器分辨率。

由于每英寸中像素的个数越多,所绘制的字体也自然较大(根据图像中的像素数目),从而产生出较大的图像。不同的图像处理程序往往有不同的默认密度设置,这就可能会导致使用不同图像程序绘制的字体也不完全相同,即使它们都使用相同的point尺寸。

请注意,-pointsize选项实际上表示字体的分离边线(即是它的绘图区域实际高度),而并非指绘制字母的实际高度!由于不同的字体在相同的-pointsize和密度设置时,也可能会显示得大一些或小一些。所以实际上只有字体的分离边线是相同的,其它任何东西都是依赖于字体类型和字体设计的。

作为一个默认的-density为72dpi(其中1点=1像素)的情况中,12点的字体,两行文字的基线应该相距12个像素。


需要注意的是“label:”命令生成图像的高度是基于图像绘制区域或边界框的,它通常就是字体的行间距和pointsize。但也并非总是如此,因为仅仅进行文本行的垂直连接实际上会造成不正确的字体处理!

某些字体甚至可能会远远超出正常分离边界的界限,大量延伸到高于或低于通常字体的行间空白区域。对于手写脚本的字体尤其如此。

字体的外观同样也受到字体“-pointsize”和“-density”设置的影响。

将字体的pointsize翻倍(“-pointsize24”),也会产生和将字体的密度或分辨率翻倍同样尺寸的字体。但是由于字体是以一种特殊的方式进行设计的,因此在更大point设置下字体中的线的粗细可能并不会发生大的变化。这就是说较大的字体尺寸只会产生细微的不同。

但是如果你对密度选项翻倍(“-density 144”),那么一个12点的字体将会绘制出原来双倍的密度,应该还是和原来的12点字体非常相似,只是在较大密度下可以绘制更好更平滑的边缘效果。

但是,在非常低的分辨率下,像素的物理尺寸限制同样可能影响字体的外观。这意味着细的线条在较低的密度下可能会变粗。“density”和“pointsize”之间的关系始终是一个非常复杂的问题,只有专业的字体图形设计师可以完全理解,让他们能正确设计和处理字体。

我认为这是一个TrueType字体渲染的特性。TrueType字形不仅是一组曲线,它可能包含多层次的细节和说明,用来根据输出像素尺寸调整点坐标,并且对于较小像素尺寸显示更加清晰。正因为如此,较小的文字看起来和缩小的大尺寸文本并不一样(更加清晰,并且人们可以看出来)。

未来的例子:在相同的像素尺寸下,字体的密度(density)和点(point)也可能不同。

基本上,增加这些因素并同时减少相同数量的其它因素可能不会产生相同的结果。特别是关于线条的粗细和字体的整体风格。你最好将你所进行的工作调整到正确的配置。使用-density选项可以在输出设备上缩放字体,或者稍后进行字体尺寸调整时,使用-pointsize选项进行正常的字体大小变化。

如果你想知道更多关于字体的信息,可以看看这个文档TrueType Fundamentals (PDF),我发现它非常有趣。

标签图像边界

当使用一些外来的字体时,字体可以使用扩展字符,在过去的IM版本中创建这些字体的标签会出现很多的麻烦。因为这些文本会溢出我们提供的画布。

例如这里在“LokiCola”中的两个大写字母让人联想到某著名的饮料品牌。

      convert -background lightblue -fill blue -font LokiCola -pointsize 64 \
                label:HC  label_overflow_font.gif

正如你所看到的,IM成功地将这个字体包含到标签中,而没有切割字体的前面或尾部图形。


在IM v6.3.2版本之前,在上面的例子中“label:”命令会切割掉“H”字母的前面的部分和两个字母的尾部。

这个问题存在的原因是,因为字体的字形或字母的描述将会绘制指定字母定义边界以外的部分,允许它们重叠(通常是高于或低于)在其它字符的字体上面。

这实际上是字体本身设计和定义方式的问题,并不是IM造成的错误,尽管IM现在可以根据用户的最佳利益来处理这些奇怪的情况。但是在其它情况下这仍然是一个问题,由于多行文本的相互作用,它并不能被根本解决。更多相关信息,请参阅下面的溢出边界的例子,以得到更加精确的描述。

Unicode或UTF8格式文本

这种向IM提供字符串参数的方法是非常重要的,因为它允许你进行使用命令行通常很难完成的事情。可以专门处理“Unicode文本”,或使用字符代码来选择指定的字符。

现在,如果你需要在命令或脚本中键入Unicode字符,你可以直接使用它们。

      convert -background lightblue -fill blue -pointsize 32 \
                label:' é è à ù ? ? ? ‘ ’ “ ” ° ? ? € x ÷ '    label_i8n.gif

然而,只有少数人有合适的键盘或编辑器可以正确实现Unicode字符的输入编辑。即使你不能直接键入Unicode字符,一个简单的解决方案就是从一些现有的UTF-8文本文件或网页中将所需的字符进行复制粘贴操作。就像我这样!

如果已经生成了用于绘制的UTF-8文本,你可以使用“@文件名”的方法从文件中直接读取。例如,我在这里使用UTF-8编码的中文文本文件创建了一个中文标签(在文件的最后没有换行符)。

      convert -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”来查找并提取字体的专有名称,并将这种字体添加到ImageMagick的“type.xml”配置文件中。

我们还可以从Unicode字符代码来生成UTF-8字符串,使用GNU中的“printf”程序(Linux系统中)将Unicode字符代码转换为具体的UTF-8编码字符串,在这种情况下,需要使用前引号和后引号进行适当的排版(同样在UTF-8输入中没有最后的换行符)。

例如,这里我使用Unicode字符代码产生了一个UTF-8文本,并使用管道命令读入到程序(使用“@-”从标准输入“stdin”中读取),而不是从实际文件中读入。

      env LC_CTYPE=en_AU.utf8 \
          printf "\u2018single\u2019 - \u201Cdouble\u201D" | \
            convert -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}";' |\
            convert -background lightblue -fill blue -pointsize 36 \
                    label:@-  label_unifun.gif

需要了解更多的信息,或者查找各种语言和符号的Unicode字符代码,请参阅Unicode字符代码图表Unicode Character Code Charts。

现在你不仅可以使用Unicode字符来绘制各种国际字符,并且使用合适的字体,你还可以使用里面定义的特殊“符号”图形。其中最有名的就是“dingbats”符号字体。这种字体已经被非常广泛的使用,并且现在被收录为标准Unicode字体集的一部分。

例如,我在这里使用一个自己编写的特殊“graphics_utf”shell脚本,提取了“dingbats”Unicode符号区域中的前24个字符,来生成一个Unicode字符的UTF-8文本块。

      graphics_utf -N 2701 2718 |\
          convert -font Mincho -pointsize 32 label:@-   label_dingbats.gif

上面显示的问号对应的字符表示它们没有在任何Unicode或windows中的“Mincho”字体中进行过定义。更特别的是,现在在Unicode中呈现出的原始“dingbats”符号中的一部分,使用的是另一种Unicode字符编码,而不是预期的“dingbats”编码。参见dingbats Unicode的规格表Dingbats Unicode Specification Chart获得更详细的信息,查找现在替代那些丢失的“dingbats”字符的正确Unicode字符。

除了问号,还有许多字体也会给这些未定义的字符,仅仅打印一个字符框或空白区域。如果在结果图像中你发现太多这样的字符,或在你的输出中缺少了字符,你应该考虑使用不同的字体。

作为大量的Unicode字符字体中的一部分还可以选用其它很多符号设置包括:Tolkan文字符号、数学符号、罗马数字、箭头和技术符号等。这是一个需要探索的非常大的集合,我们的“graphics_utf”shell脚本可以帮助你发掘它们。

这里是微软“Mincho”字体可以渲染的Unicode字符的另一个例子。在杂项符号章节Miscellaneous Symbols中的一个例子。

      graphics_utf -N 2620 2630 |\
          convert -font Mincho   -pointsize 40 label:@- label_misc.gif

在DOS脚本中使用Unicode字符必须比UNIX和Linux下更困难。在这种系统环境中使用Unicode的特别说明由Wolfgang Hugemann提供Windows Character Encoding。

符号字体

人们更普遍使用的查询特殊文本图像的方法,就是使用特殊的“符号字体”。它们比完整的较大的Unicode字体要小很多,因为它们仅仅将标准的ASCII字符(字母和数字)更换为一组不同的具体形状和图像,尽管有时(很少)它们在拉**字符区域中有更多的符号。

“dingbats”字体符号就是以这种方式开始的,但正如上面所提到的,现在它们是Unicode字符集的一部分了。

例如,我比较喜欢使用的一个符号就来自“WebDings”字体。它是一个相当漂亮的“弯曲的心脏”的图标,其实这是在那种字体定义中的正常“Y”字符的替代图像。

      convert -size 20×20 -gravity center -font WebDings label:Y label_heart_20.gif

convert -size 40×40 -gravity center -font WebDings label:Y label_heart_40.gif

convert -size 60×60 -gravity center -font WebDings label:Y label_heart_60.gif

convert -size 80×80 -gravity center -font WebDings label:Y label_heart_80.gif




需要记住的最重要的是,所有的TrueType字体实际上都是一个特殊类型的矢量图像格式Vector Image Format。字体中包含有多张图像(每个字符都是一张)。因为它们是矢量图像,这就意味着字体应该允许你绘制(-draw)几乎所有尺寸(规模)的任何一个字符、形状或符号,使用“-size”、“-pointsize”和“-density”选项进行控制。

正如你在上面可以看到的,“弯曲的心脏”图像可以呈现在几乎任何我需要的尺寸中。

有些字体是非常专业的。例如,你可以从IDAutomation中获得一个被称为“IDAutomationHC39M.ttf”的字体文件,它可以用来生成条形码图像。例如

      convert -font IDAutomationHC39M -pointsize 16 label:'*314-76*' \
                -bordercolor white -border 5x5  label_barcode.gif

下面是一些其它有趣的符号,从我通过不同途径收集的各种字符集中发现的各种符号和字体。

      convert -pointsize 48 -font WebDings label:' " _ ~ ) - '  label_webdings.gif
        convert -pointsize 48 -font LittleGidding label:' x o w ' label_ltgidding.gif
        convert -pointsize 48 -font WingDings2      label:'ab'    label_wingdings2.gif
        convert -pointsize 48 -font Zymbols  label:' ? , - I Z '  label_zymbols.gif
        convert -pointsize 48 -font TattoEF  label:' B Y D I H '  label_tatooef.gif
        convert -pointsize 48 -font SoundFX  label:' V 3 t f 9 '  label_soundfx.gif



这些只是所有可用字体中的一个小样本。互联网上拥有巨大资源,几乎包括你可以想象的每一种符号、形状或图像,供你浏览和下载。


请记住,每个可供绘制的符号都有两个独立的部分,它们是:“-fill”填充区域(我在上面展示的)以及“-stroke”或边框区域,后者看上去与填充区域非常不同。上述各部分都可以分别进行绘制,或者使用不同的颜色,所以这可能是一个好主意,我们使用不同的方法进一步检查每一个希望的图像或形状。你可能会得到一个非常令人吃惊的结果。参考复Compound Fonts, Stroke获得更多的例子。


许多符号字体设计者使用一个简单的扫描仪和位图矢量变换器来生成图形,而不进行任何适当的设计或对图像形状进行清理。建议在寻找这样的“扫描”字体时更加小心一些。

上面最后显示的一张字体就是“扫描”字体的例子,因此它的质量比起其它设计更加合理的字体时,就显得非常差劲了。

字符间距

在IM v6.4.7-8版本中,你可以使用字间距-kerning选项在文本字符串的每个字母之间插入额外的字符空间。例如

      convert -pointsize 12            label:Anthony   label_kerning_0.gif
        convert -pointsize 12 -kerning 1    label:Anthony   label_kerning_1.gif
        convert -pointsize 12 -kerning 2.5  label:Anthony   label_kerning_2.gif
        convert -pointsize 12 -kerning 5    label:Anthony   label_kerning_5.gif
        convert -pointsize 12 -kerning -1   label:Anthony   label_kerning-1.gif

请注意,实际的字间距值可以是一个浮点值,甚至为负值。

另一个使用负值字间距的例子,请参看Joined Compound Font例子。

单词间距

IM v6.4.8-0版本中单词间距-interword-spacing选项可以用来修改单词之间插入的空格字符的数量。例如

      convert                    label:'I Love IM!'  label_wspace_off.gif
        convert -interword-spacing 1   label:'I Love IM!'  label_wspace_1.gif
        convert -interword-spacing 10  label:'I Love IM!'  label_wspace_10.gif
        convert -interword-spacing 25  label:'I Love IM!'  label_wspace_25.gif

注意:你不仅可以增加每个单词之间的空格字符数量,而且可以降低默认的尺寸。

但是请注意,空格会导致重新对齐像素边界(与上面的字符间的间距不同),所以输出包含空格(其宽度设置为零)的标签仍然和不包含任何空格的标签是不同的。

这两个设置,单词间距和字符间距都会影响IM在结果中将文本字符串匹配到一个指定尺寸图像中的自动调整效果。

      convert -size 150x                       label:'I Love IM!' label_wsize_of.gif
        convert -size 150x -interword-spacing 25 label:'I Love IM!' label_wsize_25.gif
        convert -size 150x -interword-spacing 50 label:'I Love IM!' label_wsize_50.gif

我们正在做的事情就是,通过设置-interword-spacing选项使两两字符之间的距离尺寸不再随着其余文字尺寸而变化。因此,IM尝试将最佳的-pointsize设置的每个单词之间的空间量固定下来,从而不用再对放置到固定宽度区域中的文本进行调整适应。总而言之,就是单词间距-interword-spacing的尺寸越大,就需要更小尺寸的字体来将文本匹配到指定宽度的图像中。

并且可以使用负值,这样实际上会让字体重叠,或者使用特定的字符和字体,产生出不同寻常的效果。但是可能也会产生过于重叠和不确定的效果,如果你尝试使用负值请更加谨慎。

虽然上面并不是一个文本对齐的例子(虽然它看起来像是),但是你可以用这些选项作为出发点,来提供适当的文本对齐。

如果你确实需要这个级别的文本格式化和对齐操作,那么你最好采用其它方法来进行预格式化文本或Postscript,例如基于命令行的“TeX”或“LaTeX”软件。更好的方法是,你可以使用SVG绘图生成对齐的文本。

行间距

在IM v6.5.5-8版本中我们又添加了一个行间距选项“-interline-spacing”。这在以前的设置中是没有的,由于用户的强烈要求,我们将它加入进来,并且在许多方面它都是非常有用的。

通常情况下,它会在文本的各行之间添加或减去相应的像素。也就是说你可以用它来扩大行间距或让文本的各行靠在一起。

例如。

      convert                         label:'First\nSecond'  label_lspace_off.gif
        convert -interline-spacing  5   label:'First\nSecond'  label_lspace_5.gif
        convert -interline-spacing 10   label:'First\nSecond'  label_lspace_10.gif
        convert -interline-spacing 20   label:'First\nSecond'  label_lspace_20.gif
        convert -interline-spacing -5   label:'First\nSecond'  label_lspace-5.gif
        convert -interline-spacing -10  label:'First\nSecond'  label_lspace-10.gif






为了更好地使用此设置,你必须要能够计算出一个特定字体的正常行间距。这是-pointsize选项的实际定义,并与当前的分辨率设置或者-density密度设置一起定义了字体的行间距。实际上它并不定义字体的实际高度或线条粗细,虽然在特定的字体中它会影响这些方面的设置。

所以我们采取的-density设置为72点每英寸,并且知道根据定义图像中每英寸包含了72个点。你就可以计算出对于12点(-point)的字体,将会有12个像素点的行间距。

有了这些信息,你可以将图像中的12点的行间距进行翻倍,通过使用“-interline-spacing 12”命令来设置。它将会在两行文字之间增加12点宽度的额外像素。

      convert -density 72 -pointsize 12 -interline-spacing 12  -font Arial \
                label:'First\nSecond\nThird'  label_lspace_double.gif

当然,就像前面介绍过的单词间距尺寸的情况,在文本元素之间添加一个固定数量的像素空间,当程序采用自动的pointsize处理时往往会使文本变得更小。也就是最终图像进行了-size选项设置,但是没有提供一个-pointsize设置。

      convert -size x70  -interline-spacing 18  -font Arial \
                label:'First\nSecond\nThird'  label_lspace_size.gif

使用负值的行间距也可以被用来作为对垂直方向上行文本加粗的一种比较粗糙和可行的方法,我们只需简单地重复这一行,并减去超过1像素的基线分离。

      convert -density 72 -pointsize 12 -interline-spacing -13 -font Arial \
                label:'Bolded Word\nBolded'  label_lspace_vbold.gif

当然,如果你真的想要两行文字而不仅仅对文本进行加粗操作时,这种方法就不会正常工作了。同时,对于固定宽度的字体会工作得更好。

文本——文本格式页

“text:”输入格式就是设计出来用于将纯文本转换成图像,并且让每页面文本组成一张图像。它就是ImageMagick中的“paged text”输入操作选项。

换言之,其目的就是将大量格式化的文本文件转换为分页的图像,原来非常像使用打印机将纯文本打印到单独的不同的页面中。


不要混淆“text:”文件输入格式和相似的“txt:”输入格式。后者首先将会试图读取一个IM指定的“ASCII文本图像”格式。

这并不意味着一个使用“.txt”的纯文本文件操作就会失败。实际上这样的文件也可能会被转换为你所期望的样子,因为“txt:”文件格式会自动退回到“text:”格式,如果IM判断它并不是“IM像素枚举”格式的图像。

但是这种处理文本的方式也存在很多问题。首先,文本是绘制再一张大的画布上,如果你不需要某个区域,那么将这块没用的区域移除画布时就会给你造成问题。另外,文本行并不会进行自动换行操作,如果它们太长了就可能会溢出画布并被截断。最后,对于很长的文本文件,将会生成很多页图像,除非采取一些额外的预防措施。

另一方面,“text:”命令将会处理几乎任何文本文件,而不会修改最终产生图像的尺寸,或对于很长的行文本进行自动换行操作。同时,如果命令行中使用这些文本,你也并不需要对它们进行预处理和你希望的特殊字符。最后,更重要的是,如果你使用了固定宽度的字体(如Courier),文件中数据中出现的空白列,将仍然会有数据在空白列中。

基本上“text:”命令将会把输入文件转换为这样。


从文件中读取的输入文字数据实质上是直接传递给字体库进行UTF文本绘制操作的。正因为如此,一些控制字符可能会使用不同寻常的“字形”绘制。这其中包括TAB符和分页符,在编辑时“freetype”库就将它们弄错了。

如果对此非常在意的话,你可能更希望使用过滤程序预先处理你的文本文件,例如“expand”,将TAB字符转换成适当数量的空格符。

当绘制文本时,将会根据当前的分辨率(-density)创建一个大型的“字母”尺寸的页面(或使用-page指定的页面尺寸或类型)。默认情况下(72 dpi),它的尺寸将设置为“612×792”像素,这对于大多数的用途来说都是足够大的了。

例如,这里是使用“convert”命令手动将一个纯文本文件直接转换为一张图像。

      man convert | col -b | expand | \
          convert -font CourierNew  text:- -delete 1--1 text_manpage.gif


但是上面手动进行的图像转换操作生成了多个页面(图像),所以我删除了第二张及后面的图像,只留下了第一张图像,而不是包含所有图像的GIF动画。

我也可以在输入文件名前面添加一个只读修饰符“[0]”,如“text:-‘[0]’”,告诉IM只读取产生出的第一张图像。虽然“text:”命令仍然会产生出所有的图像。

我在上面故意使用了固定宽度的字体“CourierNew”,以保留打印出的页面中呈现出的字符间隔格式。

请注意这种输出方式和上面介绍的“caption:”方法的不同之处。这张图像的整体外观,同样可以使用后面介绍的相同Postscript技术进行改进。

如果你只是想知道在100 dpi设置下的一张“A5”页面到底有多大,那么该命令可以生成一张这个尺寸的空白页,并返回它的大小(以像素为单位)。文件名为“/dev/null”的文件,是一个非常特殊的UNIX文件,它始终是空的。

      convert -page A5 -density 100 -units PixelsPerInch  text:/dev/null \
                -format 'Page Size at %x = %w x %h pixels'  info:

修剪文字页

因为文本被绘制到一张大的画布上,你可能会想要删除生成的所有未使用的空白区域。这可以通过使用图像操作选项“-trim”和“+repage”来实现,使它看起来更加合理,并使用“-border”选项重新添加一些边缘空间。当然,你同时也需要使用与背景颜色(-background)相匹配的边缘颜色(-bordercolor)在进行重新添加操作时。

这听起来很复杂吗?其实并不是真的,例如。

      echo "    Hello Cruel World    " |\
          convert -background  lightblue  -fill blue  -pointsize 18 \
                  text:-    -trim +repage  -bordercolor lightblue  -border 3 \
                  text_trimmed.gif

在上面的例子中,-trim选项是用来删除“text:”生成的图像中的多余空白区域。否则,由此生成的图像将会填满整个页面。然而,这种方法也将删除该行文本前面的所有前导空格!

但是,还有一个非常有趣的技术,它将允许你使用-trim选项把图像调节到页面中绘制文本的实际尺寸,包括输入的任何前导和尾随空格。这种方法使用了一个特殊的“-undercolor”设置,我们将在后面介绍它。

      echo "    Hello Cruel World    " |\
          convert -background  white -undercolor lightblue  -fill blue \
                  -pointsize 18   text:-  -trim +repage \
                  -bordercolor white  -border 3    text_boxed.gif

在结果图像底部的额外空间是输入文本最后的换行字符,它在图像中生成了一个额外的空白行。但是你可以看到,输入文本的开头和结尾的空格都被完整的保留下来了。

如果你在上面使用一个透明的背景颜色,那么你就可以将剪裁后的图像未绘制的区域转换成与“-undercolor”相同的颜色了。

      echo "    Hello Cruel World    " |\
          convert -background  none  -undercolor lightblue  -fill blue \
                  -pointsize 18   text:-  -trim +repage  \
                  -bordercolor lightblue  -border 3 \
                  -background lightblue  -flatten    text_box_trimmed.gif

上述结果(除了“-border”增加边缘)实际上和在IM中使用“label:”命令从添加了“@”文件名前缀的文件中读取文字生成的结果几乎一模一样。但是,使用“label:”命令来进行这个操作的确是更快、更干净的方式(通过“freetype”库,而不是PostScript转换)。

你可以使用-page指定一个较小的尺寸,无论是使用像素(参见下一个例子)或者使用标准页面尺寸名称(如“A5”),并且使用-density设置像素分辨率。你还可以在页面上指定开始绘图处的偏移量,相对于图像的左上角。例如。

      echo "This is a long line that shows that 'text:' does not word wrap." |\
           convert -background  lightblue  -pointsize 18 \
                   -fill blue  -page 320x95+50+10  text:-'[0]' +repage  text_page.gif


几乎所有其它的图像创建选项都会使用-page设置来设置一张更大的虚拟画布和画布上图像的偏移量,以实现图像分层或生成动画的目的。正因为如此,所有这可能是个好主意,在使用“text:”或“ps:”选项时都用“+page”对你的页面进行重置。否则,你可能会在同一行命令里后读入的图像中得到不符合预期的结果。

这也是为什么我在上面的例子中添加了一个“+repage”选项,否则文本会产生偏移,生成的图像也会产生偏移!

更多使用偏移量的详细信息请参阅Page Image Attributes。

注意在最后一个例子中,任何太长并无法适应页面宽度的以行文字,都会溢出页面,而不是进行自动换行。这将有效地裁剪和丢弃行文字。此外,如果文本中有太多行,那么“text:”命令会生成多个页面,也就是多张图像,每张对应PostScript从文本文件中提取出的一个页面。

如果你只对其中的第一页文字感兴趣,或者仅仅是想避免生成多张图像的可能性,那么在“text:”命令的文件名中加上“[0]”,告诉IM在文本文件被转换为图像后,只读取其中的第一页图像(见前面的例子)。

Postscript / PDF——预格式化文本和图形输入(或其它矢量图像格式)

下面给出了一个标准的矢量图像处理技术,它不仅可以被用于“ps:”(postscript)图像,而且还有其它所有使用矢量图形处理的图像。包括的图像格式有:“PDF:”(便携式文件格式)、“TEXT:”(分页纯文本格式)、甚至“SVG:”(缩放矢量图形)和“WMF:”。

这种方法可以进一步扩展,让你对图像中文字的样式进行更加精确和良好的控制。例如,如果具有文本到postscript(text to postscript)过滤器的权限,你就可以控制文字的自动换行、对齐、多种字体处理、加粗、边框、标题、文件名、日期和其它PostScript图像中的设置参数。

但是本节主要是关于文字转换为图像,这意味着你首先需要将文本转换成格式化的PostScript文件。有大量的第三方程序,可以用来完成这个任务。例如“a2ps”、“enscript”或“pstext”。

从本质上讲,你可以使用一个文本处理器(如“OpenOffice”或“Word”,甚至记事本“Notepad”),或者如果你想使用文本批处理系统,也可以使用“TEX”和“LaTeX”来产生预格式化文本(参看下面介绍的完整文字处理系统)。这些方案都是设计用来将复杂的文本、加粗、不同尺寸和字体的文本混合在一起进行处理,并且包括自动换行、对齐和分段控制。这些方案的输出,可以直接传递给IM将它们转换成你希望的尺寸和质量的图像。

所以让我们先生成一些postscript。

      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:”的例子),从生成的默认页/画布中删除多余的空白区域。

      convert ps_version.ps'[0]' \
                -trim +repage   -bordercolor white -border 3  ps_version_raw.gif

注意,使用“[0]”是为了限制只输入第一页图像。如果你的PostScript图像生成了多个页面,它仍然会完全被“ghostscript”委托处理,但是IM将只读取返回的第一张图像,而不是生成出的多张图像,每张图像表示一页。如果你的PostScript非常大,你可能就会希望使用其它实用的PostScript工具在IM和“ghostscript”处理它之前进行页数限制。

正如你所看到的,将PostScript转换为默认72dpi的密度(-density),往往没有想象的好看,因为它只有最少量的抗锯齿像素。当处理并不是在如此低分辨率的情况下工作的PostScript字体时,这种情况尤为突出。

为了改善这一点,你可以使用超级采样技术(Super Sampling),以产生更好的图像。在这种情况下,你可以设置“ghostscript”在一个更高的分辨率下(或图像的密度-density)绘制图像。然后,你可以进行重采样(-resample)把这张较大的图像重置到一个更加正常的屏幕分辨率密度。

      convert -density 196   ps_version.ps'[0]' -resample 72 \
                -trim +repage   -bordercolor white -border 3  ps_version.gif

“196”这个值是默认设置72dpi的3倍,这意味着当进行重采样(-resample)操作时,将把约3×3像素的图像合并到一个像素中。这将在文本的边缘产生更好的抗锯齿像素,以改善结果的整体外观。

另外还要注意,使用一个较大的密度或分辨率,和对字体进行放大实现的效果并不完全一样。字体定义可以进行调整来处理低分辨率的情况。例如,让我们比较两个图像中字母“e”的孔。原来的版本由于对字体进行了特殊处理,所以看着比较清晰,而第二种采取超级采样的版本则更加清晰。要了解更多信息,请参看下面的分辨率、pointsize和实际字体尺寸。

你不必使用与上面相同的值,因为有时采用略有不同的值可能会产生更好或更理想的结果。当然,使用“ghostscript”来生成一张图像的2、3甚至4倍的尺寸将意味着IM会消耗4、9或16倍更长的时间才能生成这张图像!它还会使用更多的内存和临时磁盘空间!但结果通常是值得的。最好的办法就是使用你自己的文档进行尝试,看看什么参数可以给你最好的结果。

如果你还需要更多的抗锯齿像素,而不是使用更大的输入分辨率,你可以尝试使用子像素数量对图像进行模糊操作(如“-blur 0x0.7”)在减少其尺寸之前。我有时也发现,在调整图像尺寸之后进行非常少量的钝化(unsharp)处理(一种常见的Photoshop技术),可以改善最终图像的整体效果。

      convert -density 196   ps_version.ps'[0]' \
                -blur 0x0.7 -resample 72 -unsharp 0x0.7 \
                -trim +repage   -bordercolor white -border 3  ps_unsharp.gif

不过,我们必须更加小心的进行调整,因为它可能使事情变得更糟。

如果你希望使用透明背景来替代白色的,你可以将通道(-channel)设置指定为“RGBA”,来将阿尔法通道包含到图像中去。当然,你必须要使用一种可以处理半透明颜色的图像格式。

      convert -channel RGBA -density 196  ps_version.ps'[0]' -resample 72 \
                -trim +repage   -bordercolor none -border 3  ps_transparent.png

请注意,标语的部分仍然使用灰白色作为它的背景色,而不是被设置成同样的透明或半透明色。这是因为实际上是生成的postscript绘制的这个背景,来替换掉默认页的背景(成为白色或透明的)。

让背景呈现出这样的透明色,将允许你将你的PostScript图像叠加在一个指定颜色的背景图像中。

      convert ps_transparent.png -background skyblue -flatten  ps_bgnd_color.gif

使用阿尔法通道合并的方法,你甚至可以将它覆盖到一张特定的背景图像上,或平铺背景图像。

      composite -tile bg.gif  ps_transparent.png  -compose DstOver \
                  ps_bgnd_tiled.gif

因为几乎所有的postscript打印机只能让纸张或者胶片变暗(包括彩色打印机),所以上面的标语将会自动成为半透明颜色的。

如果你也想要IM完成同样的事情,就像一台打印机可以完成的工作,那么你可以使用一个特殊的阿尔法通道合并的“Multiply”方法,将白色背景的图像覆盖到所需的“纸张”背景上。

      composite -tile bg.gif  ps_version.gif  -compose Multiply  ps_multiply.gif

如果你有一张彩色的postscript图像,你也可以模拟在彩色的纸张上使用纯黑白打印机的效果,通过使用特殊的“BumpMap”合并方法。这将使源叠加图像进行灰度化处理,在使用“Multiply”方法将图像合并在一起之前。

你还可以生成灰度图像就和胶片、幻灯片的效果一样。这基本上是使用不透明和白色的背景图像(从上面)来作为一个蒙版,它可以通过使用阿尔法通道形状选项来设置透明图像的形状

      convert ps_version.gif -negate -background black -alpha shape  ps_overhead.png

就像上面的“text:”转换器一样,“ps:”转换器同样也使用“-page”选项来设置画布尺寸,也就是供“媒体”图像页面进行绘制的区域。虽然提供的偏移量将被忽略。然而,因为大多数postscript文件在内部定义了绘图介质的尺寸,所以这通常是没有必要的。


大多数其它的图像创建操作都使用-page选项来设置虚拟画布,并在虚拟画布上设置偏移量(例如生成GIF动画)。因此这可能是一个好主意,在“text:”或“ps:”图像读取选项使用它之后,再使用“+page”命令进行重置,否则你可能会在后面的图像中得到与期望不符合的结果。

使用这个偏移设置的更多细节请参阅Page Image Attributes。

作为最后一个实际例子,让我们来看看我的射线路径四面体的图像(Ray Traced Tetrahedron)。其它类似的图像可以在多面体研究Studies into Polyhedra中看到。

后台页面生成采用的与产生三维数学对象显示完全相同的数据。文本数据使用“a2ps”进行的转换,然后IM将其转换为图像。在这张图像上,其它提前准备的与数学对象相同的绘制线条被添加到页面中。这张最后的图像(保存为“targa”或TGA格式)接着传递给“PovRay”光线追踪器,来包含到最终的图像或者光线追踪场景中。

直接使用Ghostscript

虽然这不是严格的IM,Richard Bollinger报告说直接运行“ghostscript”将会更加高效,处理速度会有一个量级的提高,由于通过IM进行了较少的文件处理。

例如,除了运行

 convert -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”选项来绘制字体,我们将可以获得更多的控制权,尤其是字体的精确坐标位置,以及它绘制的图像尺寸。

      convert -size 320x100 xc:lightblue  -font Candice -pointsize 72 \
                -fill blue  -draw "text 25,65 'Anthony'" text_draw.gif

但是为了使用它,我们需要生成一张适当尺寸的背景图像来绘制字体,在绘制一些未知的字体时,这个要求可能会非常棘手。可以参考自动调整字体图像尺寸的方法介绍来解决这个问题。

很多额外的选项(超越了标准文本选项)同样可以影响实际情况中如何将文本绘制(-draw)到一张图像上。你不仅可以指定填充颜色(-fill),而且还可以指定一个底色(-undercolor),以及边缘效果或边缘颜色(-stroke),它们在默认设置中都是关闭的(颜色设置为“none”)。

填充(-fill)颜色同样也可以被替换为平铺拼接(-tile)图像块,同时,边缘宽度也可以使用“-strokewidth”选项进行改变。然后,绘制文本的相对位置可以通过-gravity设置来进行改变。

例如在这里,我使用了我刚才提到的许多额外的属性。

      convert -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 v6.2.4版本中,“-draw text”命令不再支持将“\n”解释为换行符,或使用“%”来获得图像的附属信息。(参见Drawing a Percent Bug)。

这些功能和问题,在新版本IM v6中的“-annotate”选项中仍然可以使用。参看下面的注释(-annotate)文本绘制操作。

上述所有的设置也可以用于-draw选项的设置(MVG – Magick矢量图形)。但是,如果你将上面的设置选项和-draw选项一起使用,那么这些选项只会应用于具体绘制的MVG字符串。

在此之上,绘制MVG格式可以完成更多的操作,比如文字旋转和字体“装饰”,当然你也可以绘制各种形状,比如在图像上绘制一个圆圈。

例如,在这里我们绘制一个添加下划线和旋转过的文字,并且覆盖到背景中的圆圈上。

      convert -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选项来创建你的图像,我建议你看看绘图的例子(Drawing Examples Page)。

底色区域

“-undercolor”底色设置选项,如上面和后面展示的那样,将会对字符和字体定义的绘图区域进行着色。一般它只匹配绘制字符的一部分。特别是在绘制字体的左边和右边最容易出现这种情况,而顶部和底部边缘通常有较大的空间,足够容纳所有字符。绘图区域基本上代表包围字符“细胞”的界限,字体就绘制在这个区域中。

使用“-undercolor”选项的主要用途就是作为一个简单而快速的方法,来清除文本周围不美观的背景。可以参看Annotating on Top Images中的例子。但是,同时我们还建议,你应该在需要绘制底色的字符串的开始和结束处各添加一个额外的空格字符。

溢出边界区域

当你进行文本操作或者只是一般的字体处理时,可能遇到的最大问题之一就是,并不是所有的字体都遵守一般规则。

字体设计者可以绘制单个字符(或“字形”)在相对于当前文本的任何位置(也被称为插入符)。字体的位置甚至可以不向前,在一些国际字体中它甚至可以向后移动。

这种自由设计的结果是,一些“字形”就并没有匹配到字体定义的字符绘图区域中,尤其是倾斜或者非脚本样式的字体,它们字母的某些部分远远超出了自己的边界,并延伸到后面(或前面)字符所使用的区域中了。

我见过的在这方面最糟糕的字体就是“LokiCola”字体,其中超过一半的大写字母具有波浪状的长尾巴,会远远超出它自己所属区域的界限。这种字体通常假定每个大写字母后面都会跟随3个或更多的小写字母。

为了说明这一点,我会分开绘制几个这种字体的大写字母,让你可以看到字母到底可以延长到底色框(-undercolor)或者绘制边界外多远的地方。同时,我也使用好几个这种字体来组成它的字体名称,这样你就可以看到它们被设计出来的真正用途,和为什么它们会溢出自己的底色区域了。

      convert -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只是在它进行字体处理时使用这些结果,但是这并不一定是用户所期望的效果。因此,建议小心使用不同寻常的字体。

注释——文本绘制选项

在IM v6版本中,提供了一个新加入的字体绘制选项-annotate(注释)。这个选项在很多方面都比使用传统的“-draw text”命令更加简单,并且因为它使用了“annotate()”API(应用程序接口),它也变得更加强大。

同时,这个选项还利用了原始的绘制(-draw)选项,并且使用了一种更加复杂的方式,例如可以扩展支持特殊的转义字符来加入额外的图像信息甚至多行文本,并对绘制文本应用一个坐标转换系统,来产生倾斜和旋转。

正因为如此,这个操作选项已经成为了现在所有ImageMagick文本绘制和图像标注处理的首选命令了,这也体现在了这些例子介绍中。

下面是使用这个选项的一个基本例子。

      convert -size 320x100 xc:lightblue -font Candice -pointsize 72 \
                -fill blue  -annotate +25+70 'Anthony'    annotate.gif

-annotate选项的额外功能之一是它可以完全独立地旋转绘制文本的X和Y轴。这是通过在操作参数中提供需要旋转坐标轴的角度(geometry参数中的size部分)来实现的。

为了显示单一的注释-annotate操作可以是多么的复杂,下面我们生成一个拥有底色区域、边线并且倾斜的图像。

      convert -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

在这个例子中,注释选项给定了所有四个参数。具体来说是:X轴旋转、Y轴旋转、字体在背景图像上的X和Y坐标位置。

同时还注意到,填充底色区域(使用“-tile”设置的)也和字体一样倾斜。这是因为它在绘制时使用了倾斜/旋转坐标系统,该系统让填充的底色区域和绘制文本一起进行了倾斜操作。

这种倾斜功能的另一个更高级的例子是Sheared Shadow Font。可以用这种方法与“-draw” MVG字符串创建相同倾斜字体(Slanted Font)的方法进行比较。

另一个总结注释-annotate选项进行倾斜操作效果的列表,请参看-annotate参数用法。

例如,下面是一些轻微旋转的文字。

      convert -size 320x100 xc:lightblue -font Candice -pointsize 72 \
                -annotate 350x350+20+90 'Anthony' annotate_rotated.gif


请注意,-annotate选项中指定的角度参数必须是正数,这样IM才能正确的理解。唯一的例外是,如果使用geometry设置用逗号来分隔4个参数的形式。例如“-annotate ‘-10,-10,20,90’ ‘Anthony’”命令可以在最后一个例子中使用。

这可以用来生成具有角度效果的标签。例如

      convert -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

你还可以通过使用转义字符将关于当前图像的其它信息添加到注释字符串中。例如我们可以使用玫瑰“rose:”图像的尺寸信息覆盖到图像中。并且使用-gravity选项将文字放置在图像的中央,然后关闭其中的任何旋转和偏移设置,通过将-annotate选项的参数都设置为0。

      convert rose:  -fill white -stroke black  -font Candice -pointsize 20 \
                -gravity center   -annotate 0 '%wx%h\nPixels'    annotate_rose.gif

了解更多的信息,请参阅下文中介绍的文本参数中的特殊转义字符。

如果需要其它的例子,使用各种不同的方法将注释文字添加到更大的图片中(例如放置在一边的中心处,或在右下角进行旋转)请参看Practical Text Annotation Examples。

自动调整注释文字的画布尺寸

通常情况下,你需要得到比“label:”命令所能提供还多的控制选项。例如,你想使用拼接平铺或渐变的图像,在上面注释需要的文本。但不幸的是,你需要事先知道所要注释文本的画布的尺寸。下面是这个问题的一个典型例子。当我第一次使用这个命令时,我设置了希望的最终结果图像的尺寸,并且在第一次做得很不错。但后来我得到这个结果。

      convert -size 480x80   gradient:yellow-green \
                -font ArialBkI -pointsize 70 -tile gradient:blue-red \
                -annotate +10+65 'Gradient Fun'    funfont_gradients.jpg

不幸的是,当我估算渐变图像的画布尺寸时,我把上面的单词拼写错了(忘记了里面的“i”字母)。当然,我现在把单词的拼写改正过来了,但是生成图像的尺寸就出错了,上面显示的就是产生出的不正确的结果图像。

我们需要的功能是能够在使用-annotate选项时,让画布尺寸自动适应注释文本。

其中一种解决方案是使用一张更大的画布上,然后修剪(-trim)背景图像到正确的尺寸。同时,我还添加了一个边框(border),在字体周围和最终图像的边缘处增加了一些空间,使图像看起来更加美观。

      convert -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

这种方法比你事先估算最终图像应该需要的尺寸更好一些,但是画布剪裁并不会修剪平铺拼接的彩色背景图像。

一种更好的解决办法就是使用“label:”命令来创建画布,让画布生成合适的尺寸。然后使用“-draw color”命令将图像平铺拼接到画布中(和标签文本),最后我们使用另一个平铺图像将文本注释到图像中。

      convert -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:”命令生成的居中的文字位置可能无法完全匹配一个居中的“-annotate”选项生成的文字。这两种方法遵循完全不同的处理算法,因此可能不会匹配。尤其是当涉及到不常见的字体时。

使用“底色区域(Undercolor Box)”自动调整尺寸

除了使用“label:”生成图像,你还可以使用底色区域和一个很大的边框宽度来在一张大画布上绘制字体,然后修剪画布以适应它的尺寸。

例如。

      convert -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”设置进行调整。唯一最重要的要求就是,原始的画布颜色应该和背景颜色不同,(在这个例子中使用的浅蓝色“lightblue”)并且要比最终的结果更大。

另外要警告的是,某些字体绘制的字符会大大超出它们自己的绘图区域(例如,参看上面的底色区域介绍)。在这种情况下,上述结果可以工作,但可能会要求你使用一张透明画布,然后覆盖白色的结果(例如,使用类似“-background white -flatten”这样的命令),将未使用并且是透明的区域转换为白色。但是,这种字符在图像边缘处可能会被上色。基本上在任何情况下你都无法真正的解决这个问题,只要尽你所能的去操作就行了。

对灰度文本图像进行着色

我刻意将上面的图像生成一张黑白色的灰度图像,因为这样它就可以用来作为一张蒙版图像了。从这样一张纯净的图像出发,你可以对图像的背景和前景进行着色操作,不管是单独或者同时进行。

Here for example I use the Level by Colors Operator, “+level-color”, to globally modify the image colors so as to assign the foreground and background colors with specific values.

convert invitation_box.jpg -colorspace sRGB \
          +level-colors navy,lightblue invitation_colored.jpg

例如。

      convert invitation_box.jpg \
                \( +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

上面的逆序(-reverse)选项被用来对图像进行重新排列,所以第一张图片就成为了合并操作中的第三张蒙版图像。前景图像(“plasma:”)这时成为了第一张,背景图像位于中间。

其它类似这样的灰度图像着色技术,请参阅Using a Mask to Limit the Composed Area。更普遍的例子Using Masks with Images。

生成平铺拼接梯度图像的方法,请看Gradients of Color和Randomized Canvases。

文本参数中的特殊转义字符

我们在上面已经介绍了在各种文本参数中使用特殊转义字符的方法。具体来说,你可以使用特殊转义字符例如反斜杠“\”表示换行,或者使用百分号“%”将图像属性页中定义的附加信息插入字符串中。同时,还有一个特殊的“@”转义字符,如果在一行的开头使用它,那么将会使用其余的文本参数作为文件名,并从指定的文件中读取数据。

这些转义字符不仅可以影响“identify”命令中(或者“-identify”和“info:”命令)使用的-format选项,而且它们还可以影响“label:”和“caption:”这些从文本生成图像的命令,并且还能控制图像元数据的设置选项,如“-label”、“-comment”、“-caption”。最后它们也可以在“-annotate”中使用。


由于反斜杠“\”可以在“-draw text”方法中使用,但是百分号“%”转义符并不能像它一样影响ImageMagick的SVG图像处理。这也是“-annotate”选项在IM v6版本中才加入的原因之一。

关于转义字符的其它重要点是,虽然它们在命令行的文本参数中使用。但是它们在任何情况下都不会从一个文本文件(通常使用“@”转移字符读取)读取的数据中应用转义字符。

这就意味着你不必担心文本文件数据中的转义字符会影响结果,但这也意味着如果你需要插入文件数据之外的附属信息,那么就必须在IM的外面对文件进行修改。


不对输入文本文件中的数据应用转移字符处理的最后定稿是在IM v6.3.3版本中。

例如,在这里我设置并打印了图像的标签(label)和评论(comment)元数据,我使用了两种方法来在这些源文本文件中设置信息。“info.txt”文件中包含了“@ \n %wx%h”字符串(没有最后的换行符)。

      convert -label   @info.txt  rose:      -format '%l label'   info:
        convert -comment @info.txt  rose:      -format '%c set "'   info:
        convert  rose: -set label   @info.txt  -format '%l caption' info:
        convert  rose: -set comment @info.txt  -format '%c set "'   info:

请注意,IM并没有展开通过“@”从文件中读取的任何转义字符序列。这是非常重要的,因为它意味着,IM在任何时候从文件中读取一段文本,它永远不会对该文件中的任何特殊转义字符进行处理。

IM读取文本文件时,只是视为文本,并没有任何转移字符。

不幸的是,这其中还包括了目前正在读取的文件(或流)中任何的换行符!这将导致结果图像中出现一个额外的空白行。例如

      echo "Anthony" | convert label:@-  label_stdin.gif

正如你所看到的,标签中不仅包含了输入字符串,而且还有一个额外的空行,由于echo命令在字符串的末尾处添加了一个换行符。

如果你不想要这最后的换行符,那么将需要你自己把它删除掉。但是,这可能是一个棘手的问题,它依赖于文本源文件在哪里以及如何被创建的,另外,你正在运行的IM又是使用的什么API。

最好的办法是从开始就尽量不要产生这个最后的换行符。例如,在echo命令中添加“-n”标志。

      echo -n "Anthony" | convert label:@-  label_stdin_2.gif

或者使用不会在最后生成换行符的命令,除非你有意设定它添加换行符。

      printf "Anthony" | convert label:@-  label_stdin_3.gif

或者你可以使用一行perl命令将最后的换行符都删除。

      echo "Anthony" | perl -0777 -pe 's/\n$//' |\
           convert label:@-  label_stdin_4.gif

在其它的API中,你可以查询最后的这个换行符,在将文本通过开放的管道输入到IM命令中之前。

用户定义的转义字符选项

其中一个主要问题就是试图将其它一些图像的扩展信息输入到另外的图像中使用,例如希望将其它图像的信息提取到“label:”或“caption:”命令单独生成的图像中使用。

这是一个非常棘手的问题,当前的解决方案(对单一的图像)是创建一个特殊的“用户设置”,然后附加到正在处理的图像中。这个“用户设置”可以在接下来的操作中使用“label:”、“caption:”或“-annotate”命令在需要的时候进行查找,来作为百分号的转义序列。

例如,我在这里创建了一个完全不同的标签图像,并使用了内置的玫瑰图像的信息。提供这些信息的源图像使用后被删除了,虽然如此,我还是可以轻松地将新建的标签附加到原始图像中去。

      convert rose: \
                -set option:roseinfo 'rose image\nsize = %w x %h\nwith %k colors' \
                label:'%[roseinfo]'  -delete 0   label_escape.gif

确实上面这种方法比较麻烦,但是由于受到一些涉及IM内部核心库的限制。参看从其它图像中获得更多信息的介绍Accessing Data from other images。

避免转义

如果你必须要把一个字符串作为参数输入IM中(尤其是作为一个API调用)而不希望IM进行转义处理,你可以简单地避免所有三种转义字符,通过在字符前使用一个额外的反斜杠“\”。请注意只有当“@”为第一个字符时,它才需要进行避免转义。因为向后兼容性,百分号也可以通过重复写两次来避免转义。也就是说“%%”的写法只会产生一个“%”。例如。

      convert -background lightblue -fill blue -font Candice -pointsize 48 \
                label:'\@ \\n \% 5%% '    label_escapes.gif


c121在IM v6.3.2版本之前,你不能使用反斜杠来避免最前面的“@”进行转义,而关闭它从文件读取的功能。在这种情况下,只有从文件中读取的最前面的“@”才能避免被转义。这在API中不是很实用。

下面有一个类似的例子来避免“-annotate”选项的转义操作。

      convert rose:  -fill white -stroke black  -font Candice  -pointsize 20 \
                -gravity center   -annotate 0 '\@ \\n 5%%'  annotate_escapes.gif

当然,正如先前介绍的,从文件中读取的文本(使用“@”转义字符)将永远作为文字进行处理,而没有任何特殊的含义。这就避免了对文字进行任何预处理的需求了,只是注意任何最终的换行符就可以了。

      echo -n '@ \n 5%' |\
          convert rose:  -fill white -stroke black  -font Candice  -pointsize 20 \
                  -gravity center    -annotate 0 '@-'   annotate_escapes_file.gif

换句话说,从文件中读取数据时,你不用担心转义字符的事情,完全可以专心撰写你想要IM使用的文本。

旧版本IM中的转义操作和标签

上述定义在IM v6.3.3版本中才定稿。在此之前,转义操作有时在一些选项中使用,而有时又不能使用,这决定于IM用户发送的要求、问题和投诉。

特别是“label:”和“caption:”选项中的百分比转义字符就是这种情况,在一段时间里它被设置为了非转义字符。

例如,你是否可以在下面生成的标签图像中看到“%c”,是非常依赖版本的(至少在IM v6.3.3之前的版本)。

      convert -background lightblue -fill blue -font Candice -pointsize 48 \
                label:'ab%cde'    label_percent.gif

不论你看到“abde”(支持百分号转义)或“ab%cde”(不支持百分号转义),这都取决于你使用的是什么版本的IM。


IM v6.2.4版本中,百分号转义字符被从“label:”和“caption:”选项中移除了。

但是,在IM v6.3.2版本中又重新加入了它们,作为一个新的“%[fx:…]”结构,并且可以引用任何图像,让百分号转义字符在文本到图像生成器中又可以使用了。参看FX Expression Escapes。

上面讨论的“什么需要转义”对于文件中的转义字符处理仍然是一个问题。在IM v6.3.3版本之前,下面的命令将会产生两行文字而不是一行。

      echo -n ' Love \n/ Hate ' |\
          convert -background lightblue -fill blue -font Ravie -pointsize 18 \
                  label:@-    label_escapes_file.gif

由于转义字符处理在每个版本中的差别非常大,所以,对于IM v6.3.2之前的老版本,我建议使用脚本测试一下其中转义字符的处理情况,如果这对正确进行工作具有重要的影响,那么请手工进行调整。

如果有人想创建一个IM自动测试脚本,那么希望可以分享给大家。或者如果你发现了这样的测试工具,请告诉我们。

确定字体尺寸,而不使用API

一种特定的字体和其中的单个字符都包含了大量信息。这种信息可能是非常有用的,尤其是如果你想使用IM将许多不同字体的文本拼凑到一起。

要记住,同样重要的是大多数字体都是比例字体,这意味着每个字符都会有不同的宽度,和前面不同的固有插入符(或来源)。正因为如此,每一个具体的字符都会被渲染(绘制)为一个不同的长度,而不会受到字符串中实际字符数量的影响。

唯一的例外是“固定宽度的字体”,例如“Courier”、“Typewriter”或“Terminal”字体,其中所有的字符都具有相同的宽度,让你可以轻松地生成文本列。

调试设置“-debug annotate”可以用来让IM直接报告一个特定字符串的TTF字体度量。例如。

      convert xc: -font Candice -pointsize 24 \
                -debug annotate -annotate 0 'Test' null: 2>&1 |\
          grep Metrics: | fmt -w80

正如你所看到的,你得到了一些鱼龙混杂的信息,其中包括:绘制字符串相对于原点的边界范围(这并不是字符串的实际边界),以及绘制下一个字符串时,前面需要的插入符的宽度。

完整的调试输出(将会相当冗长,并不是上面所示那样)还报告了实际使用的字体文件(两次),所以你也可以利用它来检查是否使用了正确的字体。


“-debug annotate”方法是在IM v6.3.9-2版本中加入的。

老版本的技术

由于你可能不得不使用比这个版本老的IM程序,因此这个调试输出可能也不会很方便。以下是老版本的例子,这里文本实际上使用了不同的方式被绘制成了不同的颜色,然后从产生的图像中提取信息。

例如,我们可以找出“Ravie”字体相对固定基准线的尺寸为72点。

这里我们要研究的图像将作为参考。你实际上并不需要绘制,并将它保存为图像,因为我们只是提取数据,而不是图像。这个图片的颜色将被修改,所以我们可以使用“-trim”选项将白色和黑色部分分开来看,以提取使用的指标。

      convert -size 100x150 xc:lightblue -font Ravie -pointsize 72 \
                -fill black -undercolor white  -annotate +20+100 'A' font_drawn.gif

为了获得字体的基本指标,我们先将字体本身涂上透明的颜色(“None”),来让我们可以测量边界框的大小和位置,以及这个特定字体字符的绘图区域。请注意,获得了高度信息,你就可以绘制任何东西了。

      convert -size 100x150 xc:lightblue -font Ravie -pointsize 72 \
                -fill none -undercolor white  -annotate +20+100 'A' -trim  info:

从上述的结果中我们可以看到,尺寸为72点的“Ravie”字体,将有一个高度为74像素的边界框。边界框的顶部距离图像的上边缘是42像素,因为上边缘定位于y坐标基准线上100像素的位置,边界框从基准线上100-42或58像素的地方开始。并且边界框有74-58或16个像素延伸到基准线的下方了。


请注意,并非所有的字体都将它们的绘图限制在其定义的绘制边界框之内!并且一些字母可以延伸到远远超出这些界限的地方。这就是为什么上面的例子将填充“-fill”颜色设置为无色“None”。这种方式就可以避免那些特殊的字体影响上述的测量结果。

还要注意到,行与行(实际上是基准线)之间的距离应该完全由字体的尺寸来决定,并与绘制字体的方式无关。例如,如果字体的尺寸为72点,并且一个点被定义为了1/72英寸,那么基准线之间的距离应该是1英寸。因为,现在的输出分辨率都是72像素每英寸(密度),这意味着基准线相距72个像素。

有趣的是,这意味着这种拥有74像素的边界框的字体,如果文本行之间使用单倍间距,那么两个绘图区之间会出现两个像素的重叠!

从上面的测量,我们也可以看到,对于使用这个字体和这个尺寸绘制的字符“A”,它的下一个字符应该绘制的起始点(称为插入符)在右侧66像素处。这就是字符串的逻辑长度。也就是说,下一个字符的插入符或起始点应该从20+66或+86+100(基线垂直方向不会改变)处开始。但是一些阿拉伯字体实际上需要从右到左进行绘制,这样插入符的偏移量将是负数。

上面给出了字符“A”的字体指标的用法,但是怎样获取绘制的字符“A”相对于插入符,或起始点的物理尺寸呢。只需要交换两种颜色设置。

      convert -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”设置也会让从字体中提取的尺寸产生变化。如果你增加边缘轮廓的尺寸,然后绘制字体所需要的尺寸(和边界框大小)同样会有相应尺寸的扩展,以适应较粗的轮廓。


尺寸同样也随着操作系统而变化(类型和版本),因为IM使用的字体绘图库的版本就来自于该系统。即使在完全相同的字体库和完全相同的IM版本的情况下,仍然建议你在使用不同的计算机进行文本绘制操作时更加小心,产生的结果可能有所不同甚至对于相同的字体。

更多相关的详细信息,请参阅文档TrueType Fundamentals (PDF)。这表明,即使我上述的概括可能并不总是成立的,但是这种情况仍然很普遍。

请注意,上面的例子只返回全体像素的尺寸,字体使用的所有尺寸都是浮点数。事实上,字体是否从全体像素的起始点(插入符)开始绘制的,可能与应用程序的关系很大,并且会影响字体最后产生的效果。

使用混合字体样式创建一行文本

使用多种字体、不同的点尺寸和样式来创建一行文本其实并不是IM的设计初衷。如果你还需要考虑比如文本对齐、自动换行并对图像和其它东西进行环绕排列时,它可能会变得更加糟糕。

诸如此类的事情,使用文字处理器、网页浏览器、文档打印机来做效果会非常的好,通常需要用户自己来完成,很少有程序可以将它们控制得很好。

但是其中的一个例外就是“TEX”和这一类的方案(参看下面的完整文字处理系统),所以如果你需要认真处理这些文字图形,我建议你看看这一系列的程序。

另一种方案是采用可以打印各种美观文件的程序,如HTML转换器。你可以使用这些转换器将程序生成的文件转换成PostScript,接下来IM就可以轻松的将它们处理为任何你希望的图像格式或样式。

“El Supremo”(从IM论坛)已经创建了一个API解决方案(使用C MagickWand API接口)在他的FontMetrics程序中。这里是示例输出Example Output of “FontMetrics”。

虽然现在IM命令行并不是为“文字处理”而设计的,但是这并不意味着你不能使用它完成这个任务。只是会更加困难一些。在这里,我会给出一些使用不同字体和风格创建混合文本的例子,给大家一点启发。

人们通常认为最简单的办法就是将很多个“label:”图像连接在一起。

      convert -font LokiCola     -pointsize 36  label:'LokiCola ' \
                -font Candice      -pointsize 24  label:'Candice ' \
                -font SheerBeauty  -pointsize 48  label:'SheerBeauty' \
                +append   wp_label_append.jpg

但是,你可以看到所有的图像都是垂直对齐于图像的顶部,除非你使用类似的字体,否则效果不会很好。

另外,你还可以使用一些技巧来调整连接方式,让它们沿着底部对齐。

      convert -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

这种方法就是在每个标签的顶部都添加一些额外的填充,然后将它们都剪切到统一的高度再进行连接操作。之后又使用简单的“-trim”和“-flatten”选项将行高度设置到最高的标签,并填充背景色。

正如你所看到的,这种方法产生了一个更好的效果,但是较小的字体往往会产生类似脚标的样式,而不是正确对齐的文本。

我们真正需要做的是,使用它们的基准线来对齐所有文本字符串,但是如果没有获得更多的文本信息,这将会非常困难。我们可以使用一个API程序很容易的获得这些信息,但在命令行中就变得更加困难了。前面的示例章节介绍了一种方法。

但是,没有实际取得基准线的信息而让它们根据基准线进行对齐操作,其实也是可能的。虽然“label:”命令生成的标签图像没有提供任何关于图像基准线的线索,但是你可以将图像上一个固定的位置指定为基准线。

如果没有API,你同样不能直接找出绘制文本的长度和高度。所以你首先需要使用一张足够大的画布,以确保我们不会丢失任何有关文字图像的信息。然后,为了保留尾随的空格和文本的高度,你还需要很好的利用文本注释(-annotate)中的可选设置(-undercolor),并为图像修剪操作提供一个边缘。

下面让我们看看你在命令行中应该如何完成这一切。

      convert -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

像前面一样,图像的修剪操作是分两个步骤进行的。

首先在包含一条垂直蓝线的基础图像中绘制文本。因此当我们修剪文字时,只有文字图像的宽度会被修剪,让所有的单词都位于相同的基准线高度。

在将它们连接在一起后,我们现在可以通过把蓝色构造线设置为完全透明来删除。如果正在生成的只是一张黑白图像,那么更好的办法就是提取一个非蓝色通道来替代,这将确保你去除了所有的施工线。第二次修剪操作,将删除图像顶部和底部的部分,缩小到文字需要的边界框范围。

最后的-flatten操作使用与边界框相同的颜色,会删除所有使用施工线留下的痕迹。

正如你所看到的,现在所有的文字都是根据基准线来对齐了,无论它使用的什么字体或尺寸。

当然,在这些例子中我只使用了白底黑字。你也可以使用其它的颜色,只要它们不会干扰用于文本对齐的构造线和透明背景。

使用这种技术,你现在可以生成混合字体的文本行了,并且可以垂直连接到一个更大的图像文件中。

你还可以看到,完成这一切需要进行很多操作,而这些操作在文字处理器和网络浏览器中通常都对用户进行了隐藏。如果你计划进行很多类似的操作,我建议你认真研究一下我前面提到的替代程序方案。

填充表格

你有一张表格形式的标准填充图像,并且你需要在固定的区域中填充数据。因此,你有一个像下面显示一样的数据文件如“text_data.txt”。

现在你可以使用一个简单的循环shell脚本,来生成一个像上面描述那样的文本标签,在表格图像中查找位置。

      cat text_data.txt |
        while read width gravity color  pointsize x y text
        do
          convert -size ${width}x -gravity $gravity -fill $color -background wheat \
                      -pointsize $pointsize  -page +${x}+${y}  label:"${text}"  miff:-
        done |
          convert -size 200x100 xc:   -   -flatten    text_layered.jpg

在这种情况下的表格图像仅仅是一张空白图像,但它可以成为任何东西。我还将标签的背景色设置为了“wheat”,所以填充的区域是可见的,但你也可以将这它设置为透明的。

上面并没有使用临时文件,而是使用管道来获取MIFF格式图像的数据流。这是因为你可以串连MIFF图像生成一个多图像文件。使用这种技术的另一个例子可以参看Image Layering Examples。

以上我们只是抛砖引玉。你还可以加入其它的属性,包括使用的字体、旋转等。也可以包括宽度和高度,或者如果文本需要进行自动换行,那么应该使用“caption:”命令,而不是“label:”命令。

文字处理替代方案

最理想的生成完全格式化的文本文件和文档的方式,是使用ImageMagick作为较大图像和文字处理系统中的一部分。

工具 用途

Imagemagick  图像批量处理和准备

Gimp  对一个固定的问题进行GUI图像编辑

Pango  文本处理库(见Pango视图命令)

LyX  GUI文字处理,内置生成

LaTeX  文档和书籍的文字处理器

TeX 基础文本格式(确定网页上符号和字体的位置)

Metafont  TeX字体生成器

基本上,ImageMagick可以做很多事情,但是这并不意味着它是完成这些工作的最佳工具。对于较大文件的准备,你最好把它当作一个更大整体中的一部分。

上述给出的各种“TeX”工具通常为大多数Linux系统的默认安装软件,并且可以将文本和图像组合成统一的整体。更重要的是它会将文本仍然保存为文本类型,根据你的设置对文本进行适当的格式化处理,完成几乎所有困难的文字和页面包装工作,和在图像中的布局。并且不会在“doc”文件中存入很多没用的格式化信息。你具有完全的控制权,或者也可以让程序自己作出决定。

它们提供了一种产生任何类型文件的方式,可以从一个简单的页面、实时通讯甚至是一本完整的书,如果你对文档的生成非常关注的话,那么这些工具是很值得观看和学习的。

Pango(只适用于Linux和MacOSX)也可以作为一种替代方案。它提供了许多ImageMagick不具备的从文字转换为图像的处理功能。例如制表符、对齐、空白和页眉等等。它甚至还可以使用一种标记语言来允许在文本中间进行字体变换。

例如,在这里我使用“Pango视图”来对一个小的文本文件进行布局,其中包括了制表符来实现列对齐。

      printf "col1\tcol2\nabc\txyz\n123\t789" > pango_text.txt
        pango-view -q  -o pango_text.png  pango_text.txt

其它的解决方案还包括很多文本到postscript的转换程序,例如“a2ps”,我在前面的postscript处理示例中展示了如何使用它来生成postscript文件。这个工具还可以转换和格式化不同类型的文本文件,并且能自动换行、加粗和制表位控制,以及相当不错的页眉、页脚、边框和分页选项。当然,这是间接的图像处理方法,需要通过PostScript或PDF等中间语言。

另一种是使用SVG对文本进行布局,或者使用imagemagick的绘图命令,虽然接下来你还是需要处理布局问题。有大量的工具可以将文本转换成图像,并且大多数可以结合imagemagick将处理后的文字图像合并到你的图像中。这些才是ImageMagick最擅长的图像处理工作。

阅读余下内容
 

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注


京ICP备12002735号