为何你不应该再用 “Hei”

我很久没有在这里谈字体学的问题了…. 今天忍不住提一点,可能不少朋友先前没有注意的。

不要在你的样式表里给 font-family 加上 Hei,应该保留系统的默认值!

下面是一个简单的对比,前一个是 Hei,后一个是华文细黑 (系统的默认值)。

为什么 Hei 这么糟糕?因为它是 Mac OS Classic 时代的字体,还是 dfont 时代的遗物,在 Mac OS X 提供了华文细黑和华文黑体这两个优秀的 TrueType/Mac 字体并将其作为系统默认值之后,已经没有什么继续用 Hei 的理由了,因为从上面的对比图已经很明显的说明后者要比前者清晰得多。可是仍然有不少不明就理的网页开发人员在样式表中这么书写,直接造成的结果就是我这样的 type freak 对这类网站就一点兴趣也没有了。

不管怎么说,Hei 已经严重过时了,为了你的网站着想,千万不要用它。

关于几个我参与的项目

我的一个习惯是做随机的 hacking — 也就是看心情如何,抓起一个软件的代码来改上一阵子,等问题解决了,再封起来好几个月不理。有许多朋友肯定都认为这是一个坏习惯,可惜这几乎已经成了我工作的固定方式,好像已经改不了了。

但是不少朋友仍然关注我以前参与的项目 — 它们是不是已经“死了”,所以,我觉得是时候来个小结了,以后这样的小结大概还是会继续做:

  • vim-cocoa: 很健康!我近期没有很大规模的计划,因为它对于我每天的使用已经足够了,但是我肯定会找时间把 google code 上一些积压的 issue 统一 fix 掉,或者至少 fix 掉一部分。
  • MacVim: 将 ATSUI 文本渲染器移植到 MacVim 的工作仍然在继续,下一个阶段的任务是解决特定字体的兼容性问题 — 之前我基本上只用 Monaco 测试过。当然我在 MacVim 上做的许多工作和 vim-cocoa 的是重叠的,代码也可以很大程度上复用。
  • Nally: 由于出现了 Welly 这个出色的 fork,至少在国内用户中,对 Nally 的用户需求没有那么多了,不过继续开发 Nally 的意愿仍然没有放弃,我自己如此,相信 yllan 也是如此,只不过下一步的计划想想总有点大,抽不出整块的时间来做。对我自己而言 Nally 下一步的计划是设计并实现一个足够灵活的插件架构,第一步会支持 Objective-C,然后会支持 Python, Ruby 等等脚本语言来编写 Nally 扩展。
  • Chmox: 说实话,Chmox 的代码改得并不舒服,可在我决定动手修改它的当时,那是 Mac 下唯一拿得出手的一个 CHM 阅读软件 (似乎也是当时唯一提供了源代码的 native CHM 软件),可在现在 archmockiCHM 这样出色的阅读器已经出现,而且它们要么本来就是开源软件,要么即将发布代码,所以我找不到理由再继续这个 Chmox 修正的项目。不过毫无疑问,在这个项目上花的时间一点也没有浪费,找到的一些技巧完全可以继续用于这两个新的阅读器中。
  • Mac Dictionary Kit: 是的,我知道 0.3 发布之后还是有不少问题,google code 的 issues 页面也积压了不少问题,可是最近始终提不起兴趣来修复 — 嗯,我很诚实地说,最近不想改它。

嗯,似乎单子已经够长了,本来要列下去应该还能继续列,不过想想还是算了,先做好这些吧…

How to crash your iTunes

刚刚发现了一个保证 iTunes 能 crash 的方法,屡试不爽,如下。

创建一个叫做 proxy.pac 的文件 (名字随便起),内容是:

function FindProxyForURL(url, host)
{
    if (isInNet(host, "72.32.231.8", "255.255.255.0")) return "SOCKS 127.0.0.1:7777";
    return "DIRECT";
}

这是一个完全合法的代理服务器自动配置文件

打开 System Preferences -> Network -> Advanced -> Proxies,选择 Configure Proxies: Using a PAC file, 然后在 PAC file url 中填入上面创建的这个 proxy.pac 文件的路径。

打开 iTunes,访问 iTunes Store,每次 iTunes 都会这样崩溃掉:

Thread 7 Crashed:
0   com.apple.JavaScriptCore        0x946f2940 WTF::fastRealloc(void*, unsigned long) + 3680
1   com.apple.JavaScriptCore        0x9476a2f2 KJS::ArrayInstance::put(KJS::ExecState*, unsigned int, KJS::JSValue*) + 1346
2   com.apple.JavaScriptCore        0x9470a827 KJS::stringProtoFuncSplit(KJS::ExecState*, KJS::JSObject*, KJS::List const&) + 1559
3   com.apple.JavaScriptCore        0x947c85a5 KJS::Machine::privateExecute(KJS::Machine::ExecutionFlag, 
KJS::ExecState*, KJS::RegisterFile*, KJS::Register*, 
KJS::ScopeChainNode*, KJS::CodeBlock*, KJS::JSValue**) + 28501
4   com.apple.JavaScriptCore        0x947c9098 KJS::Machine::execute(KJS::FunctionBodyNode*, 
KJS::ExecState*, KJS::FunctionImp*, KJS::JSObject*, 
KJS::List const&, KJS::RegisterFileStack*, KJS::ScopeChainNode*, KJS::JSValue**) + 760
5   com.apple.JavaScriptCore        0x947061ef KJS::FunctionImp::callAsFunction(KJS::ExecState*, KJS::JSObject*, KJS::List const&) + 127
6   com.apple.JavaScriptCore        0x94730af7 KJS::JSObject::call(KJS::ExecState*, KJS::JSObject*, KJS::List const&) + 135
7   com.apple.JavaScriptCore        0x9473ab0c JSObjectCallAsFunction + 220
8   com.apple.CFNetwork             0x91db848a CallFindProxyForURL + 297
9   com.apple.CFNetwork             0x91dba4d9 executionContextPerform + 177
10  com.apple.CoreFoundation        0x94062615 CFRunLoopRunSpecific + 3141
11  com.apple.CoreFoundation        0x94062d54 CFRunLoopRun + 84
12  com.apple.iTunes                0x00281521 0x1000 + 2622753
13  com.apple.iTunes                0x002818b4 0x1000 + 2623668
14  com.apple.iTunes                0x0020b47c 0x1000 + 2139260
15  libSystem.B.dylib               0x965f16f5 _pthread_start + 321
16  libSystem.B.dylib               0x965f15b2 thread_start + 34

看起来像是 Safari 4 Preview 带来的问题。

State of iPhone Open Application Development (1)

七月关于 iPhone 的消息一直围绕着 2.0 firmware, iPhone 3G, official SDK 1.0 这几个关键词,而非官方关心的也只是 Pwnage Tool 2.0,却对没有 iPhone Developer Program 的开发进展甚少介绍,我想在这里做一点记录。

随着 SDK 1.0 的正式发布 (遗憾的是,因为 NDA 的存在,甚至它都算不上发布..),iPhone 2.0 firmware 和 App Store 的上线,在 Apple 监视下的 iPhone OS/Cocoa Touch 程序开发的局限性暴露得越来越明显,saurik, NerveGas 等开发者坚持开发 Open Toolchain 的重要性也越来越明显。为什么在拥有一个如此完善的 SDK 的情况下我们还需要 Open Toolchain 和相关工具?因为:

  • Apple 严格限制了第三方应用对 API 的使用,非 Apple 自己开发的应用程序不能使用许多极为有用的 Private API,否则就是违反 SDK 的授权协议,所以第三方应用始终只能是“二等公民”。
  • App Store 的发行方式要求每次更新软件都要由 Apple 审核才能出现,而列出应用的页面对于用户 feedback 和开发者交流的功能也非常局限——毕竟这个模式根本就是 iTunes Music Store 改头换面了一点点,Album 到 Application 其实并不能做到一一映射。所以我们需要更自由的 iPhone OS 软件发行方式。
  • 软件分发要求所有只开发 Open Source/Free 应用程序的开发者都必须至少缴纳 $99 年费,这极大打击了 Open Source 开发/移植者的积极性,在 Pwnage Tool 的大环境支持下,我们完全可以跳过这个限制,自行分发软件。

因为上面这些原因,我一直非常关注 open toolchain 的开发,到了 4 月份的时候,有半个月的时间一直在跟踪 saurik 在这方面开发的结果,其结果是这篇 Upgrading the iPhone Toolchain,可惜的是因为 saurik 一直不满意用 git 来跟踪上游代码的修改,所以这篇文章其实相当难付诸实施,不过,考虑到 SDK 1.0 的发布后上游代码应该有一个相对稳定期,所以希望 saurik 能够尽快整理出更新的文档和代码来吧。

不过 saurik 在他的 Cydia repo 里提供了 iPhone 上完整的 toolchain,你可以在自己的 iPhone/iPod Touch 上用 gcc 编译,用 gdb 调试…. 当然考虑到有限的硬件资源,这种方式对很多人来说太 geeky 了,包括我。

然而,目前用官方 SDK 提供的编译器和调试器来给编写在 pwn 过的 iPhone 上免费分发的应用程序已经完全可行了,详情可以参考 saurik 的 Bypassing iPhone Code Signatures246tNt 的说明。未 pwn 的 iPhone 因为目前事实上只有通过 App Store 一条路安装应用程序,所以谈怎么分发也是没有意义的。

简单的说,这种方式是因为 pwn 过的 iPhone 的内核已经被打上了 patch,弱化了签名校验——不再要求非要有 Apple 的签名了,可是签名校验依然存在,要完全去掉不大现实,所以现在你可以通过 Apple 自己提供的 codesign 工具来给应用自行签名,或者在别的平台下用 saurik 开发的 ldid 签名工具来保证通过 iPhone 的签名检查。对于大部分 Mac 开发者而言,这也只是在 Xcode 的项目中,新增一个 Build Phase 的事情,所以不会增加什么工作量。

而 246tNt 进一步延展了 saurik 的工作,分析了 entitlements 文件在签名后的 iPhone App 中对安全控制的作用,使得这种自签名的应用程序也能像有 Dev Program 的应用一样,可以用 Xcode 自带的 gdb 进行远程调试。此外,246tNt 还找出了办法对 iPhone OS 中的 SpringBoard 和 MobileInstallation 打上补丁,使得我们可以直接把应用程序用 Xcode Organizer (或者 Build and Go!) 上传到 iPhone 上,并自动开始调试。

感谢这些开发者的工作,目前我们获得的开发环境和支付了 $99 美元的 Dev Program 开发者毫无二致了。可是,分发环境还是有区别,假定我们的程序并没有做任何逾越 SDK 授权的事情,如果还希望通过 App Store 分发,那还是只能去购买 Dev Program。不过毫无疑问,对于开放的应用程序而言,Cydia 应该是更好的分发方式,考虑到因为 Pwnage Tool 2.0 的包含,Cydia 几乎已经成为了标准的应用分发方式。

(待续)

最近在写的两个东西

这两天写了两个简单的小程序,主要是满足朋友和个人需求的,还在修改中。

第一个叫 apn 的程序是给 Mac OS X 中 Address Book 中的联系人自动生成姓名发音 (Phonetic Name) 的脚本,所以它的名字就是 Assign Phonetic Name 的缩写。

这是一个 Python 写的程序,直接用到了 pyzh 项目提供的汉字拼音转换代码,通过 Scripting Bridge 提供的 API 来访问 Address Book,这样虽然相比直接调用 Objective-C 的 API 有点慢,但好处在于能充分利用 Python 语言的灵活性。

使用起来很简单,从 github 上把代码抓下来 (你可以 git clone git://github.com/jjgod/apn.git 或者下载一个打包的版本),然后执行

$ python AssignPhoneticName.py

就会自动给你 Address Book 中没有分配过 Phonetic Name 的那些联系人分配一遍。注意因为汉字有多音字,这个程序做不到很智能,你最好在分配之后打开 Address Book 校对一遍。

第二个是给 Cocoa 程序员用的一个 NSView 的子类,叫 PYView,其作用很简单,就是在汉字上方同步的显示拼音,不过目前拼音还得自己提供。

调用的 API 很简单,比如这样:

#include "PYView.h"

NSRect viewRect = NSMakeRect(50, 250, 700, 80);
view = [[PYView alloc] initWithFrame: viewRect
                            fontName: @”FZKai-Z03″
                               color: [NSColor whiteColor]];

NSArray *pinyin1 = [NSArray arrayWithObjects: @"nǐ", @"hǎo", nil];
NSArray *pinyin2 = [NSArray arrayWithObjects: @"zhōng", @"huá",
                    @"rén", @"mín", @"gòng", @"hé", @"guó", nil];


[view appendMarkerItem: [PYMarkerItem itemWithHanzi: @"你好"
                                             pinyin: pinyin1
                                               type: 1]];
[view appendMarkerItem: [PYMarkerItem itemWithHanzi: @"中华人民共和国"
                                             pinyin: pinyin2
                                               type: 1]];

就能得到如图所示的输出结果:

PinyinView

可以从 github 上获取代码:

 git clone git://github.com/jjgod/pinyinview.git

然后参考提供的 PYViewTest 代码来使用。注意接口还在修改中。

Adium 一些工作与开源软件相关的思考

更新: 原来 MSN 群中使用 /showname 命令也可以控制这一点。

另外 Adium 其实是个非常好的开发群体,非常 active & helpful,只是项目庞大到了这样,bug tracker 里的 ticket 一多,开发者自己也很难保证代码结构足够好了。


因为最近开始使用 MSN 群 (1, 2),但我使用的 MSN 客户端 Adium 并不能将群内发言人的身份显示出来,只能全部统一显示为群本身的名称。

其实这是 Adium 所使用的 IM 协议支持库 libpurple 在支持 MSN 协议上固有的缺陷 — 不过也不算特别严重的缺陷,因为 Windows 版本的 MSN 也只在 Windows Live Messenger 8 以上才支持,而 Mac OS X 下的 Microsoft Messenger 干脆到现在也不支持。

所以,要修复这个问题,必须从 libpurple 上打主意,然而,因为 libpurple 编译不便 (后面我会解释为什么这么说),Adium 本身的代码仓库中只提供了编译好的 libpurple framework,编译这个 framework 的步骤则是分离出来的,要单独用 Utilities/dep-build-scripts 下面的脚本来完成

可是问题变得越来越 tricky 了:为了编译 libpurple 的代码,必须下载整个 pidgin 的代码,但 pidgin 的代码又是用臭名卓著的 Monotone 来管理的,这直接导致下载当前代码的步骤就变得复杂无比,更不用说后面的编译了。

这还没有完,崩溃的是,Adium 虽然其他协议的支持都是直接从 libpurple 来的,但偏偏 MSN 协议最近改用了从 libpurple 中 fork 出来的 msn-pecan 项目,而 msn-pecan 又是用 git 来管理代码的……

这么一来,为了修改 MSN 协议支持并编译出 Adium,我们必须至少涉及三套版本管理系统 (Subversion, Monotone 和 git),把 Adium 提供的一堆错综复杂的脚本找出来,让它先给 libpurple 打上 Adium 自己的 patch,然后分 ARCH 来生成 configure 并分别配置编译,最后合并成 Universal Binary 再 copy 回 Adium 的 Frameworks 目录去… 到这里我还没开始改一行代码呢!

虽然这个问题最终得到了解决,我提交的 patch 也将合并到 msn-pecan 官方的代码中去,可是这个经历仍然让我觉得颇有体会:

从 F/OSS 项目的贡献者来讲,要成功的参与项目,就必须掌握好常用的版本管理工具并了解基本的编译手段,才有机会参与到真正的代码修改中去。

而从 F/OSS 项目的发起和维护者来讲,要创造一个成功的项目,应该:

  1. 避免使用怪异的版本管理工具
  2. 编译步骤简单再简单,尽可能分解为可以单独执行调试的步骤,尽可能减少会在编译时出现的问题

哦对了,如果有愿意试用我修改后的 build 请在这儿 (21M, SHA1 = 16af86b349e49b35f836ed1492b52a3fe7d5d061) 下载。

用 MEncoder 编码 H. 264 流

MEncoder 是常用的离线编码器,属于 mplayer 项目的一部分,这里介绍了怎样用 MEncoder 编码常见的视频流。

首先,要安装 x264 编码库,mplayer 需要这个库才能加上 H. 264 编码支持:

$ git clone git://git.videolan.org/x264.git
$ cd x264
$ ./configure
$ make && sudo make install

注意 x264 库需要 yasm 汇编器,MacPorts 下可以用 sudo port install yasm 安装。

然后编译 mplayer (包括 mencoder)。

$ svn co svn://svn.mplayerhq.hu/mplayer/trunk mplayer
$ cd mplayer
$ ./configure # 注意输出中是否有 "Checking for x264 ... yes" 字样
$ make && sudo make install

然后就可以调用 MEncoder 了:

$ mencoder input.fmt -o output.fmt -ovc x264 -oac copy -x264encopts \
    bframes=4:b_pyramid:weight_b:pass=1:psnr:bitrate=1500:turbo=1

其中 input.fmt, output.fmt 分别是输入和输出文件,其调用格式见 MEncoder 的文档,而后面 -x264encopts 的参数制定的是 x264 编码参数,这是影响编码质量和速度的地方,文档中也有专门一节详细说明,这里选取的是一个中等偏上的效果。

在看过 Diablo 3 的 Trailer 之后

就完全不期待 Spore 了… 原因主要是:

  • Spore 是个 Cider port,不是真正的 native Mac Game — 今年的 Neverwinter Nights 2 Mac 版给我的印象很糟
  • 从 Creature Creator 来看,Spore 的角色太卡通太塑料了,让人提不起兴趣
  • Diablo 3 完全没有令我失望…

现在 Mac 下真是只有 Blizzard 一家还在认真做游戏了.. 其他所有的厂商基本上都是用 Cider 来糊弄一下,Apple 是不是应该直接把每年的 ADA Best Mac OS X Game 发给 Crossover?

终于用上实验室的打印机了

我们实验室的打印机是一台 HP LaserJet 1020,装在一台 Windows 2003 Server 上通过 Samba 共享的。我一直懒得了解 Mac 下怎么操作这台共享的打印机,所以有什么要打印的都是发给别人帮我打。

今天有空来试了一下解决这个问题。首先,打开 System Preferences 里的 Print & Fax,尝试添加打印机,结果发现 Windows 这里通过浏览工作组找不到打印机所在的那台计算机:

找不到打印机所在机器

怎么办呢,开始 google,找了一圈发现原来需要 Custom Toolbar 才能找到上图中那个 Advanced,提供原来 10.4 里 Printer Setup Utility 的功能,通过直接输入 IP 的方式来配置 Samba 共享的打印机。

因为头一次用,还专门用 smbclient //IP/PrinterName 试了一下确实可以连接。

另外打印的时候还是需要输入密码的,不过对于没有设置密码的共享,可以选 Guest。

配置好了,选择打印但是打印机还是没反应,怀疑是驱动不对,原先选的是 Generic PostScript 打印机的 PPD,可是 HP 压根没有提供给 LaserJet 1020 的官方 Mac 驱动… google 了一下,找到一个很不错的解决方案:foo2zjs — 这是一套开源的驱动,给 Linux 和 Mac OS X 提供了一些缺失的打印机支持,注意网上还能找到许多相关的讨论,和另一套 1022 的驱动据说也能用,可是似乎对于共享打印机不起作用。

按要求依次把驱动和其依赖的包装上之后,在 Driver 里选择 HP LaserJet 1020 Foomatic,终于成功打印,太感动了。

HP LaserJet 开源驱动

下一代 JavaScript 开发方向?

更新: Mozilla Labs 新出一篇 Next Generation Javascripting 也总结了近来出现的一些有趣的东西,包括 Tamarin, SqurirrelFish, Processing.js, ContextFree.jsParchment

其中 Tamarin 和 SqurirrelFish 代表了浏览器 JavaScript 引擎性能的提高 — Tamarin 虽然现在还不明显,因为它的 JIT 还在 tracing 优化阶段,SqurirrelFish 甚至还完全没用 JIT。不过这两者正式进入市场至少要到 2009 年 Mozilla 3 平台的初步发布和 Safari 4 随 Snow Leopard 一起发布时。

Processing.js 和 ContextFree.js 则代表了 Web-based Processing 创作的新方向,ProcessingNodeBox 的成功充分说明了在简单技术上提供更有效的表达方式会给图形生成带来多大的改进,现在能在 Web 上直接实时呈现这些优美的图形,毫无疑问 Flash, Silverlight 等将更不受欢迎了。

Parchment 则是一个非常漂亮的 Z-Machine 在 Web 上的呈现,适合 Text Adventure 爱好者们。


近来,两个新出现的 JavaScript 框架得到了许多关注: SproutCoreObjective-J。表面上,它们共同的特点就是大量使用了 HTML5 特性来改善用户体验,达到类似桌面应用的效果,然而从底层分析,它们其实代表了两种对富 JavaScript Web 应用的开发方式的革命。

为什么说是“两种”呢?虽然 SproutCore 是一套几乎完全由 Apple 工程师开发的框架 — 其设计目的就是给 MobileMe 提供类似桌面应用的效果 — 但其设计思维是 Web 化的,是和现下流行的服务器端开发框架一脉相承的。从我的观点看来,SproutCore 是受 Ruby on Rails 影响严重的一套框架,其提供的工具大多用 ruby 编写,创建的目录结构都带着 Rails 的痕迹 — 甚至开发人员也是典型的 TextMate 用户。

而 Objective-J 虽然也主要是由前 Apple 工程师领导开发,可是这些工程师却是不折不扣的 Objective-C/Cocoa 狂热爱好者,他们在 280slides 表现出来的是典型的 Keynote 风格,其代码组织则是典型的 NeXTSTEP 风格。

所以,可以想见的是,SproutCore 的设计思维更容易为大众接受,而 Objective-J 很可能始终保持小众 — 甚至我都很怀疑它到底会不会发布…