Preserving Extended Attributes on OS X

When codesigning a Mach-O file (OS X executables or libraries), the signature information will be stored in the file itself through some Mach-O extension. When codesiging a bundle (.app or .framework), _CodeSignature directory will be created. But what happens when you codesigning a plain text file? Signature information will be stored in extended attributes. Because of that, when packaging or copying files like those, you would expect the tools to preserve extended attributes. Not all of them do that by default.

tar on OS X preserves extended attributes by default, both archive and unarchive. But zip doesn’t, a better replacement is ditto -k, ditto can be used as a replacement for cp as well, though cp in OS X preserves extended attributes by default.

When using rsync, -E or --extended-attributes will make sure it copies extended attributes.

When creating a dmg with hdiutil, keep in mind the makehybrid command will lost extended attributes, so you will have to use alternative ways.

codesigning

Codesigning is one of the worst issues we had been having since we started working on the new Opera for Mac. How Apple managed to screw this up never ceased to amaze us.

Since yesterday morning our build servers started to get CSSMERR_TP_NOT_TRUSTED error while code signing the Mac builds. Well, we didn’t notice until trying to release the new Opera Next build in the afternoon, which is obviously a bad timing. When it happened, immediate reaction was search for it in Google, unfortunately, when it happened words haven’t been spread yet so all results we got were from early 2009 ~ 2011, about some intermediate certificates missing, which completely mislead us. We spent a couple of hours inspecting certificates on all 3 of our Mac buildbot servers, none of them seemed wrong. One of my colleagues tried to resign a package locally with certificates/keys installed, got the same error as well.

Fortunately our build server didn’t get the same error every time so we managed to get a build for release.

When I later did a search for the same keyword but limit the results in last 24 hours, we finally found the real answer to the problem this time. According to this discussion:

Apple timestamp server(s) after all that is the problem here. If I add the --timestamp=none option, codesign always succeeds.

I have exactly the same problem. Probably Apple got two timeservers, with one broken, and a 50% chance for us to reach the working one.

And it worked for us perfectly as well. The only thing I didn’t know was whether it’s safe to release a build without requesting a timestamp (or where can we find other trusted timestamp servers).

This morning I woke up and saw this summary about yesterday’s incident.

According to Allan Odgaard (the author of TextMate):

As long as the key hasn’t expired, there should be no issue with shipping an app without a date stamp, and quite sure I have shipped a few builds without the signed date stamp.

That at least give us some confidence that if such incident happen again, it shouldn’t be a big issue to turn timestamp off.

Update: More explanations from Apple:

The point of cryptographic timestamps is to assist with situations where your key is compromised. You recover from key compromise by asking Apple to revoke your certificate, which will invalidate (as far as code signing and Gatekeeper are concerned) every signature ever made with it unless it has a cryptographic timestamp that proves it was made before you lost control of your key. Every signature that does not have such a timestamp will become invalid upon revocation.

opf-cc: epub 和 mobi 的自动简繁转换工具

写一个自动转换 epub 或者 mobi 格式文件的中文简繁体的工具是我一直想做的事情,因为有不少格式内容精美的书籍资源都只有繁体中文版本,而我又不习惯长篇阅读繁体,所以常常要手工转换再用 calibre 封装,不胜其烦,相信其他人也有类似需求。

上周末有空,就用 Python 写了 opf-cc 这个项目,是 Open Packaging Format Chinese Conversion 的缩写,因为 epub 和 Amazon 采用的 mobi 都只是封装方式,具体的文件布局都是按照 OPF 规范的。这里说说实现的思路。

简繁转换这个问题相对好解决,有现成的 OpenCC 在处理多繁一简或者多简一繁的问题上已经很完善了,所以就稍微修改了一下 OpenCC 的代码直接拿来用了,修改都作为 pull request 已经提交到上游了。

解包 epub 比较简单,因为 epub 实际上就是 zip 压缩包,所以用 Python 的 zipfile 模块直接就可以解压。mobi 的解包稍微麻烦一些,如果不用 calibre 那一套庞大的库,mobiunpack 就是最好的选择。

解包后需要找到应该转换的文件,比较麻烦的地方是有的目录中 href 到的文件名本身就是繁体,如果直接整个目录文件一起转换,就得把文件也对应改名,比较麻烦,这里我尝试用 lxml 来解析目录文件,挑出文本来调用 OpenCC 的 Python 模块进行转换,对于 href 属性的内容则不转换。

重新打包 epub 也简单,用 OS X 的 zip 工具一压就可以了 (更新: fishy 提供了不依赖单独 zip 工具而是直接用 Python 的 zipfile 的实现),但 mobi 的打包比较麻烦,要么用 calibre 要么用 Amazon 提供的 KindleGen,好处是下载安装一个二进制程序就可以了,坏处是生成的文件大小要比原来的文件大一倍有余,calibre 就没这个问题 (更新: 经 byelims 推荐使用 kindlestrip 来处理 KindleGen 生成的文件,可以去掉冗余的数据)。

总的说来这个项目还有不少可以改进的地方,除了上述两点以外,还有可以加入简体转繁体的功能,也就是给 OpenCC 传一个不同的参数的事而已。不过我设想该有的功能都已经有了,具体应用的时候遇到什么问题再拿来改进。

另外有兴趣的朋友可以提供更方便的封装,比如用 Automator 或者 ThisService 做成 OS X 的 Service,就可以直接在 Finder 里选中文件右键点击转换了。

Tools for a typography hacker

一直想写篇 blog 介绍一下常用的、跟字体技术相关的开发调试工具,我一般用 Mac OS X 或者 Linux 开发,所以工具也集中在这两个平台下,也有的是跨平台的。这里只说我自己常用的,欢迎补充。

* [UnicodeChecker](http://earthlingsoft.net/UnicodeChecker/)

Mac OS X 下完美的 Unicode 字符查看工具,可以根据 UTF-16 编码 (10 进制、10 进制)、UTF-8 编码来查找,或者直接复制粘贴字符进去,可以选择不同的字体查看该字符对应的字形,包含完整的 Unicode 字符属性数据库,可以自动下载安装 Unihan 数据库。几乎是每次开发和调试问题的必备。Linux 下有 [gucharmap](http://live.gnome.org/Gucharmap) 实现类似的功能,但要弱很多。

* [ttx](http://www.letterror.com/code/ttx/)

将 TrueType/OpenType 文件按照指定的表 dump 成 XML 格式,或者反过来,所以既可以查看也可以修改。非常方便分析 OpenType 的 GPOS/GSUB 特性查找表。这是一个命令行工具。更简单一点的 TTF/OTF 分析命令行工具还有 [lcdftypetools](http://www.lcdf.org/type/) 里的 [otfinfo](http://www.lcdf.org/type/otfinfo.1.html),可以直接列出字体的特性,但没有细节显示。

* [FontForge](http://fontforge.sourceforge.net/)

大部分 TTX 的功能也都可以用 FontForge 实现,虽然界面是基于 Xlib 的相对老旧,但它的功能实在是强大,不过我一般也就用来编辑字体的 name table 和 OpenType feature。

* [hb-view](http://cgit.freedesktop.org/harfbuzz/tree/util)

[harfbuzz-ng](http://freedesktop.org/wiki/Software/HarfBuzz) 提供的工具,可以用指定的字体、指定的 OpenType 特性,将 HarfBuzz 排版好的内容以 FreeType 渲染出来,方便对比测试特性字符串的布局正确性。当然,通常我还会用常见的浏览器、文本编辑器等来比较,尤其现在 Firefox 和 IE10 TestDrive 支持 OpenType 特性指定了,测试起来就更方便。

* [fc-list](http://ftp.x.org/pub/X11R7.0/doc/html/fc-list.1.html), [fc-match](http://linux.die.net/man/1/fc-match)

[fontconfig](http://www.freedesktop.org/wiki/Software/fontconfig) 提供的工具,主要用来分析 Linux 下的字体匹配,在阅读它的[用户文档](http://www.freedesktop.org/software/fontconfig/fontconfig-user.html)之后,善用 `-v` 和 `-a` 参数,可以直接获得不少字体的信息。

* Pixie

Xcode 自带的屏幕放大镜,用来分析 subpixel antialiasing 非常给力。别的平台下当然也有类似的工具,比如我在 Linux 下用 KDE 的 [kmag](http://www.kde.org/applications/utilities/kmag/)。

* [The Font Game](http://fontgameapp.com/), [Kerning Game](http://type.method.ac/) 和 [letter shaping game](http://shape.method.ac/)

三个制作非常精良的字体相关小游戏,第一个是 iOS 上的字体辨识,后两个则是体验对间距形状把握的 HTML5 在线游戏,适合在开发之余放松一下大脑 😉

用 XBMC 整理电影收藏

随着硬盘上收集的电影多了起来,怎么管理它们就成了问题,我之前采用的方法很简单:先把电影下载到内置的硬盘上,然后把看过之后觉得值得保留的那些,或者还没来得及看,但内置硬盘空间不够的时候,转移到外置硬盘 (一般是大容量的仓库盘) 上。一般这样的仓库盘就直接在根目录下分个 Movies 目录,然后所有的电影每个占一个目录,全部按它们原来的名字移动到这个 Movies 下面。类似这样:

$ cd /Volumes/Foo/Movies
$ ls
Apocalypto.2006.720p.BluRay.x264-ESiR
Gake.no.ue.no.Ponyo.2008.BluRay.720p.DTSES.3Audio.x264-CHD
Lock.Stock.and.Two.Smoking.Barrels.1998.720p.BluRay.x264-CYBERMEN
Mission.Impossible.1996.720p.BluRay.DD.x264-ESiR
Surrogates.2009.720p.BluRay.x264.DTS-WiKi
...

而自从 XBMC 被移植到 Mac 上以来,就开始关注这个软件内置的多媒体管理功能,毕竟大部分多媒体文件不是从 iTunes Store 上买的,所以 FrontRow 并不那么合用,而这个软件从功能全面和界面美观的程度上看都合适。不过之前一直都在用 Plex 这个 fork 版本,因为 Plex 确实对 Mac 相关的一些细节处理的比较好点,但也只把它当作一个单纯的媒体播放器来用,并没有用到它的库管理功能。主要是因为 XBMC / Plex 要求的命名方式是“标题 (年份)”这样的类 imdb 方式,而我又想保留原有的目录名 (方便找字幕什么的),所以一直没有实施。

更新:发现 XBMC 还能根据目录下放置的 nfo 文件中的 imdb 地址来查找,而且这样更精确,所以假如你每个目录都保存了 nfo 文件,可以不必修改目录的命名。

最近想到的取巧做法是,对每个这样的电影目录,都在本地目录建立一个符合 XBMC 命名规范符号链接,然后让 XBMC 对这个本地目录进行索引。于是写了这么一个脚本 试了一下,果然很方便,效果如图:

这里简单介绍一下步骤,供有兴趣的朋友参考:

  1. 推荐使用最新的 XBMC 9.11 版本,Plex 最新的 0.8.5 版本这方面支持得不好;

  2. 图中使用的是 Rapier 这个 skin,需要单独下载,推荐使用;(下载后将解压得到的目录移动到 ~/Library/Application Support/XBMC/skin 目录下,然后在 XBMC 的 Settings -> Skin 中选择 Rapier)

  3. 下载前面提到的这个 makelib.py 脚本,使用方法如下:

    $ chmod +x makelib.py
    $ ./makelib.py /Volumes/Megatron/Movies ~/Movies
    Apocalypto.2006.720p generic viagra 100mg.BluRay.x264-ESiR -> '/Users/jjgod/Movies/Apocalypto (2006)'
    Gake.no.ue.no.Ponyo.2008.BluRay.720p.DTSES.3Audio.x264-CHD -> '/Users/jjgod/Movies/Gake no ue no Ponyo (2008)'
    Mission.Impossible.1996.720p.BluRay.DD.x264-ESiR -> '/Users/jjgod/Movies/Mission Impossible (1996)'
    Surrogates.2009.720p.BluRay.x264.DTS-WiKi -> '/Users/jjgod/Movies/Surrogates (2009)'
    The.Cove.2009.LIMITED.DVDRip.XviD-AMIABLE -> '/Users/jjgod/Movies/The Cove (2009)'
    ...
    

    这么做的效果是扫描 /Volumes/Megatron/Movies 目录下所有匹配的目录,在自己的主目录下的 Movies 目录中建立对应的符号链接。

    需要注意的是,这个脚本会要求所有内容都是目录,而且命名都是 Movie.Title.Year.*.*.*.-Group 的格式,中间一段可以任意,但必须包含年份和小组后缀,如果你手头存的目录名称不规范,必须先修改为这种格式,有的来源目录名称就不包含年份,这时可以根据 themoviedb.org 或者 imdb.com 提供的年份来自行添加。如果要求不那么规范,可以修改脚本来忽略小组。另外还要注意的是,Director’s Cut, DC, Unrated, Limited 这样的词必须放到年份后面,要么自己改改脚本,过滤掉它们也行。

  4. 打开 XBMC,进入 Videos 部分,选择 Add Source,用 Browse 选择这个 ~/Movies 目录,在 Set Content 中,选择用 themoviedb.org 作为内容来源,选上 Use folder names for lookup,如图:

  5. 然后 XBMC 就会开始索引所有创建好符号链接的目录,如果能在 themoviedb.org 上找到对应记录,就会自动下载。这个时候你切换到 XBMC 的 Movies 部分,就能看到如前面截图一样的效果了。选择其中任何一个,按回车就会开始播放。按 c 键或者用 Apple Remote 的 menu 按钮则可以打开上下文菜单,这里可以编辑选定项目的信息、删除特定项目 (如果导入的内容不对)、设置已经看过的标志等等。注意在 XBMC 扫描的时候不要断开存储实际内容的硬盘,否则 XBMC 会认为本地存的是无效的链接而直接跳过。如果出现个别电影名称不规范导致找不到对应记录的,可以在更名后按 c 选择重新扫描。

  6. 如果你下载了新的内容,可以再次以同样的参数运行前面提到的脚本,这个脚本会跳过已经添加的内容。另外,还可以在 XBMC 的 Settings -> Videos -> Library 部分选上 Update Library on Startup,这样每次启动 XBMC 的时候就会自动扫描你添加为库的目录,检查是否有新的内容了。