技术

Apple/Microsoft 的字体问题

研究这件事起因于希望在 Windows 的浏览器上使用 Apple 原生的 Emoji 表情,但是从 Mac 的 /System/Library/Fonts/Apple Color Emoji.ttf 拷贝到 Windows 上后无法被安装。。。于是看了一下究竟怎么回事,虽然问题最终没有彻底解决,但记录一下相关的信息:

  1. 不同于矢量轮廓描绘,Emoji 在实际应用中是以彩色图案的形态存在,尤其是Unicode后来引入皮肤颜色变化,就需要一套新的字体方案

  2. Apple 自己定义了 ttf 中的 sbix 扩展,用嵌入 PNG 图片的方案来取代轮廓描绘;后来 Google、Microsoft、Adobe/Mozilla 也先后定义了自己的 ttf 扩展方案,最后似乎都成了标准的一部分,探索在Android中使用Emoji Font的方法
  3. Adobe/Mozilla 的方案叫 SVG,或者 SVGinOT(opentype);上文提到 Google 的方案类似 Apple,也是 PNG内嵌
  4. 相对来说 Microsoft 的方案更有特色一些,它是用轮廓+调色板渲染的方式实现了 Color Emoji,思路清奇
  5. 按照微软的说法,Windows 10的最新版应该是能支持上述所有的4种字体的,https://msdn.microsoft.com/en-us/library/windows/desktop/mt765165%28v=vs.85%29.aspx#what_kinds_of_color_fonts_does_windows_support_
  6. 但是浏览器有一点点特殊的地方,它还有 webfont 的支持问题。于是有一个网站可以用来检查浏览器对 webfont 的支持情况:https://pixelambacht.nl/chromacheck/ Windows10上的Edge是4种都支持,但 Chrome Windows 版本就仅仅支持微软自家的Color Emoji
  7. 怎么样替换 Windows 缺省的 Color Emoji 字体呢? http://superuser.com/questions/1062418/how-do-i-replace-windows-10s-emoji
  8. 怎么把 Apple Color Emoji 转换成 Google 格式呢(这样在 Android 和 Chrome/Linux 上就都可用了)?上面中文文档里链接了一个 xda 上的文章,提到了方案:http://forum.xda-developers.com/showthread.php?t=2563757 , 现在再看这个方案,作者说最新的 Android 系统可以直接使用 Apple 的 sbix 扩展了。。。所以我猜最新的 Linux Fontconfig 或许也能支持 Apple Color Emoji.ttf 了
  9. 但如何在 Windows 上直接把 Apple Color Emoji 转换来用上述信息还是没有解决,最终找到了两个平台上ttf格式兼容的另外一篇文章:CMap 表相关修改技术简要指南。。。看似说的很有道理,但是按里面的方法去修改重新合版仍然没有得到想要的结果
  10. 最后找到一个商业的解决方案,fontlab.com 的TransType,在 Windows 下还确实把原字体转成了两个能正常打开的字体,但是限于时间,暂时没有进一步去测试 Windows 上安装了字体后浏览器上的表现...
Topic: 技术

查看服务器端的 RSA 公钥指纹

话说以前碰到过一次 ssh 劫持,访问某个缓存过对方公钥机器的时候,仍然弹出一个公钥指纹确认的提示,当时没多想,就直接确认,输入用户名/口令,认证失败;猛地反省过来,急忙换了个渠道登录,把所有相关的用户名/口令对都更改了一遍。。。真是惊出一身冷汗

今天换了一台新机器登录,想到这个茬,于是乎特意找了个命令:

# ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub

在服务端先查看一下指纹是啥,然后客户端这里首次连接的时候再确认一下

====
更新,新版本的 ssh-keygen -lf 缺省输出的是 sha256 格式的指纹,如果想看 md5 的输出需要加上 -E md5

另外,也许算法不是 rsa ,而是 ecdsa 之类,寻找对应公钥文件的时候注意一下

Topic: 技术

django 1.9 i18n 国际化和本地化

网上一堆讲 django 国际化与本地化的文档。不说太多。

django 1.9 以后(也有可能是 1.8 以后),zh_CN/zh_TW 不推荐用了,改为 zh_Hans/zh_Hant,这个作为 locale 下的文件夹名。

在 settings.py 文件里,用 LANGUAGE_CODE = 'zh-hans' 或 LANGUAGE_CODE = 'zh-hant' 似乎是官方推荐的。(注意这里是连字符,文件夹是下划线)

我碰到的问题是,用 django-admin makemessages -l zh_Hans 后,能够在 app_name/locale 下面生成 zh_Hans 文件夹及对应的 po 文件。编辑文件后,用 django-admin compilemessages,能把 po 文件编译出对应的 mo 文件,但是网页上死活出不来中文。

settings.py 里 'django.middleware.locale.LocaleMiddleware', 这句也加在 MIDDLEWARE_CLASSES 里了,USE_I18N、USE_L10N 默认就设为 True,也没改过。折腾半天,有个网页说有可能是 django 找不到 locale 的位置,需要在 settings.py 里写出来,于是加上:

LOCALE_PATHS = [
    os.path.join(BASE_DIR, 'app_name/locale'),
]

搞定!

另外目前 Django REST Framework 好像还不支持 zh-hans 这样写。

Topic: 技术

Django REST framwork 报 NOT NULL constraint failed 的处理

我碰到这个问题由来是,在某个 model 里,foo_id、bar_id 两个字段都是别的表里的外键,但是在 serializer 里我又不想显示 foo_id,而想显示 foo_id 所代表的真正的意义

  1. class FooBarRelationshipSerializer(serializers.HyperlinkedModelSerializer):
  2.     foo = serializers.SerializerMethodField()
  3.     bar = serializers.CharField(write_only=True, required=False)
  4.     def get_foo(self, obj):
  5.         return Foo.objects.get(pk=obj.foo_id).name  # foo_id 来自 urls.py 里的相关 router 的定义
  6.     def validate(self, data):
  7.         del data['bar']
  8.         return data
  9.     class Meta:
  10.         model = FooBarRelationship
  11.         fields = ('foo', 'bar')

通过上面这段代码能很好地达到我的目的。好,问题来了,我想把 foo_id 和 bar_id 的关系存入表中,如果用 foo_id 和 bar_id 的话,request.data 里必然存在输入的 bar_id,但是我的实际输入是 bar,我必须要重载 validate 方法,把 bar 去掉,那么我在 views.py 里手动把 bar 对应的 bar_id 取到,然后塞进 request.data 里好了。然后

  1. serializer = self.get_serializer(data=request.data)
  2. if serializer.is_valid(raise_exception=True):
  3.         serializer.save(foo_id=foo_id)

想得挺好,运行时却报了 NOT NULL constraint failed 的错!!!

网上搜了半天,没有什么结果,提供的答案都是告诉你检查一下是不是有必填的字段没有给数据。但真正碰到这个问题的,而且还寄望通过搜索解决问题的,基本上都会先保证必填的字段里有数据,可现在数据都有了,为什么还会报这个错???

我打印了 FooBarRelationshipSerializer 里用 foo_id 和 bar_id 时的 request.data 和现在的 request.data,里面的数据一模一样!!!但是就是前者的 serializer.save 能成功,后者报错。。。

思来想去,可能同样的 request.data,但经过 serializer = self.get_serializer(data=request.data) 后,serializer 会不同,谁知道 get_serializer 里有什么鬼(不看源码了)。尝试直接把 bar_id 作为参数传给 save 方法,就像这样 serializer.save(foo_id=foo_id, bar_id=bar_id),结果当然是成功存入数据库。哈哈。

Topic: 技术

Django REST framework serializer 跟 model 无关的只写(write_only) field 的处理

有时候想在 DRF 的 api 里塞一些跟 model 无关的,但又是必须要处理的数据,比如设密码时,为了保证输入无误第二次输入的 password,这种信息一般设为只写。
这个时候 serializers.py 里,把 validate 方法重载一下,在里面删掉 request.data 里实际上 model 不需要的数据就 ok 了。

  1. class AccountListSerializer(serializers.HyperlinkedModelSerializer):
  2.     password = serializers.CharField(write_only=True, style={'input_type': 'password'})
  3.     confirm_password = serializers.CharField(write_only=True, style={'input_type': 'password'})
  4.     def validate(self, data):
  5.         del data['confirm_password']
  6.         return data
  7.     class Meta:
  8.         model = Account
  9.         fields = ('password', 'confirm_password')

当然,在 views.py 里,需要校验一下 password 和 confirm_password 是否输入了,如果输入了,是否一致

  1. if not request.data.get('password') or not request.data.get('confirm_password'):
  2.     raise serializers.ValidationError("Please input password and confirm it!")
  3. if request.data.get('password') != request.data.get('confirm_password'):
  4.     raise serializers.ValidationError("Passwords do NOT match!")
Topic: 技术

Django REST framework serializer 跟 model 无关的只读(read_only)field 的处理

有时候想在 DRF 的 api 里塞一些跟 model 无关的信息,比如一些提示的信息,这种信息本身跟业务没有关系,设为只读。
这个时候 serializers.py 里:

  1. class FooSerializer(serializers.HyperlinkedModelSerializer):
  2.     foo = serializers.CharField(read_only=True)
  3.     tips = serializers.CharField(source='get_tips', read_only=True)
  4.     class Meta:
  5.         model = Foo
  6.         fields = ('foo', 'tips')

这个时候 models.py 里:

  1. class Foo(models.Model):
  2.     foo_id = models.AutoField(primary_key=True)
  3.     foo = models.CharField(max_length=128)
  4.     def get_tips(self):
  5.         return 'Please foo bar.’
  6.     class Meta:
  7.         db_table = 'foo'
Topic: 技术

obfuscated-openssh

ssh 对传输内容做了很好的加密;但是很容易被深度检测判断出通信正在走 ssh 协议;

有些公司或组织会阻碍对外的 ssh 连接,即使换了 22 端口,也可能会被深度检测判断出来阻断

总之最后有人给 openssh 增加了一层混淆的保护,以绕过深度检测的判断,这就是 obfuscated-openssh

Win32 客户端层面,Bitvise 也支持该协议;Putty 则有人搞了一个 Potty 的分支

最新的 OpenSSH patch 被收集在 https://github.com/zinglau/obfuscated-openssh-patches
维护者的说明在 https://zinglau.com/projects/ObfuscatedOpenSSHPatches.html

他也维护了一个 Ubuntu ppa 的源 https://launchpad.net/~zinglau/+archive/ubuntu/obfuscated-openssh

我注意到这个项目是因为最近自用的 ssh 也有些不正常,于是找到了这个方案;而且顺手把 7.0_7.1 的 patch 移植到了 7.2 上,不晓得 pull-request 是否会被接受

Update: 俺的补丁已经被作者合并,7.2 的握手协议部分比之前还有点点变化,稍微思考了一下才得到一个比较完美的patch

Topic: 技术

IMAP 服务器对 XAPPLEPUSHSERVICE 扩展的支持

翻去年邮件的时候,无意中看到 XAPPLEPUSHSERVICE 这个关键词,搜索一下加深知识记忆:

2011 年的时候 Apple OS X Server 的 dovecot 修改部分就开源了

http://www.opensource.apple.com/source/dovecot/dovecot-239.8/dovecot/src/imap/cmd-x-apple-push-service.c

2012 年 dovecot 就讨论是否增加支持,但显然除了 IMAP 之外,还得有一个 APSd 的东东

http://www.dovecot.org/list/dovecot/2012-August/067682.html

...到了2014年,还在讨论

http://dovecot.org/pipermail/dovecot/2014-April/095653.html

但也就是在 2014 年,出现了两个项目让 dovecot 可以支持 Apple Push 了
https://github.com/st3fan/dovecot-xaps-plugin

https://github.com/st3fan/dovecot-xaps-daemon

2015 年,cyrus imapd 支持了这个扩展

https://git.cyrus.foundation/rIa4470bf0f79de49030b3d66d2b385a1345c1a040

尝试一下免费的 Let's Encrypt 证书

从知乎上看到相关的讨论:https://www.zhihu.com/topic/19568370,周末晚上尝试了一下

按照文档上(https://letsencrypt.readthedocs.org/en/latest/using.html)所描述,Apache服务器是可以用脚本自动完成证书申请和配置全过程的。但我最终参考的还是一个通用的方案,https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-14-04,手工完成申请和创建,然后再自己配置

非常简单,除了一开始我填写了5个域名反馈DNS超时;最后改成2个域名(dup2.org www.dup2.org)就平稳完成,得到了4个相关的文件

然后配置证书,包括参考 http://stackoverflow.com/questions/16200501/http-to-https-apache-redirection 设置了把 HTTP 自动重定向到 HTTPS.

最后看看 google 会不会给我加权了.. 以及三个月后再重新延长证书

Topic: 技术
订阅 RSS - 技术 | BT的花