用电脑“自动生成”的图书
《优秀网页设计速查与赏析》是一本用电脑“自动生成”的图书。
怎么,你不相信吗?我现在就来讲讲这本书的编写过程。
收集优秀网页设计
之所以有编写这本书的想法,主要是因为出版社的策划编辑们知道我手头有一个收录了近2000个国外优秀网页设计的“数据库”。每逢设计网页时,我自己都会在这个数据库里检索那些可以“借鉴”的内容。如果把这个数据库转换为一本公开发行的图书,其他网页设计师不也可以拥有类似的便利了吗?
积累这个数据库并没有花太大的力气,这主要得力于我自己编写的一个IE插件程序。在这个插件程序的帮助下,我平时上网浏览时,一旦发现优秀的网页设计,点一下鼠标就可以把网页截图及其相关信息记录下来,非常方便。
所谓的IE插件,就是可以在IE的指挥下运行、可以访问IE的资源的小程序。我这个IE插件程序是用Visual C++/ATL编写的COM组件。组件对象需要实现IObjectWithSiteImpl接口,只有这样才能访问宿主程序也就是IE中的资源。
被IE激活后,组件主要执行以下代码逻辑:
// 从宿主对象获取IID_IServiceProvider接口
m_pSite->QueryInterface(IID_IServiceProvider, ...);
// 从IID_IServiceProvider接口获取浏览器对象的服务接口
isp->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, ...);
isp2->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, ...);
// 获取浏览器的当前视图窗口
m_pSite->QueryActiveShellView(&view);
// 获取当前URL
browser->get_LocationURL(&bURL);
// 将URL、当前系统时间等信息保存至XML数据库
...
// 截获当前视图窗口的像素信息
GetDIBits( hdc, hBmp, 0, bmpinfo.bmHeight, ... );
// 利用libpng库将截获的像素信息保存为PNG格式的图片文件
png_write_image(...);
// 显示对话框,提示用户输入网页关键词和点评文字
DialogBox(...);
// 将关键词和点评信息保存至XML数据库
...
将组件编译成DLL后,需要按照IE的要求在注册表中注册组件信息。此后,只要看到值得收藏的网页设计,我就点击IE工具栏上的按钮,IE会立即弹出如图 1所示的对话框。
我会在对话框中输入网页的关键词,心情好时也会输入一句我自己对该网页设计的评语,这些信息都会与网页截图、URL等信息一起被程序自动保存在XML数据库中。
如此日积月累,大概用了一年左右的时间,电脑里的网页设计数据库就已经初具规模了。当然,我还用Python/wxPython为这个数据库做了一个简单的查询系统——只要输入关键词就可以快速定位到相关网页。
自动分类和人工标引
为了符合书稿的要求,我必须按内容或主题将每个网页分入特定的类目,如“传媒?广告”、“建筑?园林”等等。这个分类工作可以由电脑自动完成。其实,我只是用XML创建了一张关键词与类目名称的对照表,然后用Python写了一个简单的程序,通过查表的方法将网页分入了特定的类目。
此外还需要按主色调将网页分入“红色”、“蓝色”等色彩类目。这个工作电脑一时还承担不了——我们当然可以用电脑自动统计出网页中分布面积最广的色彩,但分布面积最广并不一定意味着该色彩的视觉感染力最强,所以,我决定通过人工标引的方式,将每个网页分入不同的色彩类目中。
值得一提的是,为了方便人工标引工作,我还特意用Excel/VBA开发了一个基于图片的标引程序。我将网页数据库中的所有信息导入Excel工作表中,然后为该工作表的SelectionChange事件写了如下几行VBA代码:
Private LastRow As Long
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Row = LastRow Then
Exit Sub
End If
LastRow = Target.Row
s = Worksheets("index").Cells(Target.Row, 3).Text
If Left(s, 2) = "wp" Then
Worksheets("image").Pictures.Delete
s = "original_images" & "/" & s & ".png"
Set p = Worksheets("image").Pictures.Insert(s)
p.Height = p.Height / 3 * 2
p.Width = p.Width / 3 * 2
Else
Worksheets("image").Pictures.Delete
End If
End Sub
上述代码的意思是说,如果用户在Excel工作表中切换到了新的记录,Excel就会根据记录中存储的图片文件名调入相应的网页截图,并将截图显示在另一张工作表中。有了这样的代码,我将数据和图片两个工作表并列摆放在Excel窗口内。然后使用Excel提供的列表、汇总、排序、筛选等功能自由编辑和浏览数据,同时又可以在右侧窗口内看到当前数据对应的网页截图(如图 2所示)——这种感觉真是太好了!如果用C++或Java编写一个功能类似的程序,那该需要多少行代码呀!
自动提取网页的配色方案
书中每个网页设计旁边都附有该设计使用的配色方案(主要色彩的图样及其RGB值)。这些配色方案是从哪里来的呢?如果由人工标引,那该有多费事呀?!
像提取网页配色方案这种高强度、重复性的劳动当然要交给电脑来做了!
首先,每个网页截图都是真彩色图片,这不利于电脑自动分析和提取主要色彩。我用Photoshop将所有网页截图批量地转换成色彩数目较少的图片(比如16色或256色图片)。怎么,你不知道Photoshop提供了批量处理功能?没关系,只要在Photoshop中录制一个新的动作,该动作包含将图片转换为索引颜色的操作,然后从Photoshop菜单选取“文件/自动/批处理”即可对某文件夹中的所有图片重复播放已录制好的动作。需要注意的是,将图片转换为索引颜色时需要使用“局部可感知调板”、“强制黑白”、“保留实际颜色”等选项,以保证图片中的主要色彩不发生大的变化。
接下来,我用Java编写了一个小程序,自动提取出每个16色或256色图片中的色彩信息,并统计每种色彩的分布面积。这个程序的主要代码逻辑如下:
// 读如PNG格式的图片
Image image = toolkit.createImage(filename);
// 获取图片的所有像素信息
PixelGrabber pg = new PixelGrabber(image, 0, 0, ...);
// 遍历每个像素,将色彩置入色彩表中,并增加相应色彩的计数值
for (int c : pixels)
{
Color color = new Color(c);
Integer weight = colorTable.get(color);
if (weight == null) weight = new Integer(1);
else weight = new Integer(weight.intValue() + 1);
colorTable.put(color, weight);
}
// 将统计结果输出为XML格式的数据库文件
for (Map.Entry<Integer, Color> s : colorSet)
...
利用由此得到的色彩数据库,我们就可以直接编程序生成每个网页设计的配色方案了(生成结果也是一张PNG图片)。这时,我选用的编程语言是Python/wxPython。生成配色方案图片的主要代码逻辑如下:
# 读入网页数据库和配色数据库中的相关信息
imagesFile = file(IMAGES_FILE, "rU")
imagesLines = imagesFile.readlines()
...
# 在配色数据库中找出与当前网页截图匹配的记录
# 并根据色彩分布面积选出其中最重要的几种色彩来
matchlines = filter(lambda x: x.startswith(...), imagesLines)
...
# 在一个wx.App的派生类中创建DeviceContent
# 画出配色方案的色彩图样,以及表示RGB值的文字信息
# 然后将结果存入新的PNG图片文件中
class iDesignApp(wx.App):
def OnInit(self):
bmp = wx.EmptyBitmap(CANVAS_WIDTH, CANVAS_HEIGHT, 24)
dc = wx.MemoryDC()
dc.SelectObject(bmp)
DrawPalette(dc)
dc.SelectObject(wx.NullBitmap)
bmp.SaveFile(g_OutputFile, wx.BITMAP_TYPE_PNG)
return False
自动生成全部书稿
最后,只需要根据各类目的选取原则,从数据库中选择合适的网页设计,并生成全部书稿就可以了。此过程包含两个关键点,一是要保证每个类目中网页数量的相对均衡,二是要根据每个关键词的出现位置自动生成全书的关键词索引。
生成书稿的程序是用Python编写的,其主要代码逻辑如下:
# 读入XML格式的数据库文件
f = file(INDEX_FILE, "rU")
indexLines = f.readlines()
...
# 先将所有记录归入按内容划分的类目中
for line in indexLines:
rec = line.split(FIELD_SEP)
class_table[rec[CLASS_SEQ]].append(rec)
# 然后随机选取一半左右的记录,将它们移动到按主色调划分的类目里
# 移动的同时需要保证各类目中记录数量的均衡
for c in CLASSES:
num1 = len(class_table[c])
num2 = num1 - NUM_PER_CLASS
...
i = random.randint(0, len(class_table[c]) - 1)
rec = class_table[c][i]
color_table[rec[COLOR_SEQ]].append(rec)
del class_table[c][i]
num2 = num2 - 1
# 生成书稿正文,生成的同时记录每个关键词的出现位置
for c in CLASSES:
for rec in class_table[c]:
image_no = image_no + 1
output_item(bookFile, rec, image_no, logFile, 0)
for keyword in keywords:
keyword_table[keyword].append(image_no)
# 生成全书的关键词索引
for k in keywordsLines:
bookFile.write(k)
bookFile.write('/t')
bookFile.write(str(keyword_table[k]))
这样生成的书稿就可以交给出版社排版、编辑了。
需要说明一下,因为牵涉到全书的版式设计、纸张选择、印刷方式等问题,后期的排版和编辑工作必须由出版社来做。假如不考虑这些问题,我们也完全可以使用Word或PageMaker提供的自动化功能,直接由电脑自动生成全书最终的排版结果。这样,图书付印之前的绝大多数工序都可以实现“自动化”了。
写在最后的话
这篇后记简单记录了本书的制作过程,也为大家提供了一些相关的编程技巧和源码示例。这些东西看起来似乎与网页设计无关。但我想强调的一点是:无论是网页设计师还是软件工程师,大家都应该善于利用手边的电脑和软件工具,因为只有这样才能把所有重复性的劳动交给电脑,然后把所有创造性的工作留给自己!
拿这本书的制作过程来说,我既使用了Photoshop和Excel软件提供的自动化功能,也使用了C++、VBA、Java、Python这样的编程语言,还与IE的插件打了些交道——看起来有些驳杂,但仔细想想,我为每个环节选用的似乎还都是实现起来最简单、使用起来最便捷的技术方案。
我想,无论是编程序还是设计网页,我们都不必将自己禁锢在某种工作模式或某些平台、工具构成的小圈子里——在需求的指引下,选择那些最灵活、最简单、最便捷的解决方案,这才是一个聪明人应该做的事。
周虹,王咏刚
2005年9月