我是如何把 iText 带到这个世界的

0) iText 点子的由来

开始时,我打算在 Klib 中支持从 PDF 导入标注,发现很难;在这个过程中,一些用户提出(发现需求):在看 PDF 文档时,有些扫描版不支持复制文字,不方便。于是,就从双 11 晚上开始,我花了一天,做了 iText 第一版最小原型),可以截图并从中识别文字。

之后,在微博等渠道做了简单推广,并限制 封闭内测(即,用户需要发邮件给我,才能收到 iText 的下载链接),发现还是有不少人对这个工具感兴趣。这也算是 验证了这个需求的真实性,于是就决定好好做成一款产品。

其实,在开发 iText 时,我已经有包括 KlibiPic 在内的 7 款产品。要开一个新坑,还是很慎重的。毕竟,麻雀虽小、五脏俱全;再小的产品,也需要 Logo、产品首页、提交上架、运维等一系列事情。很容易分散精力,做出一款自己都不满意的产品。不过,感觉 iText 应该是可以做的,虽然我当然压根就没做所谓的竞品分析、市场容量分析等等。

1) 我主要做了什么事

2017 年双 11 写下第一行代码,时月 29 号正式上架 Mac App Store,第一版花费将近 20 天的时间。

1.0) 开发最小原型,用了 1 天

虽说第一版最小原型,也已经包含了很多因素,主要是开发相关:

  • Logo、图标
  • 菜单栏图标基本程序
  • 集成百度 OCR 服务
  • 剪贴板操作
  • 截图功能
  • 拖拽
  • 界面
  • 日志
  • 收集反馈
  • 试用机制
  • 检测更新
  • 本地化
  • 测试用例、测试

1.1) 完成产品并发布,却用了 20 天

最小原型距离成熟的产品,还有很远的距离,尤其有非常多开发之外的工作:

  • Logo 设计
  • 集成多家 OCR 服务
    • 目前,技术上实现了对接百度、腾讯、Google 的 OCR 服务,最终使用的是后两种。
    • 其中,还要压缩图片以满足 OCR 服务对图片尺寸的限制。
  • 优化识别结果
    • 第一版只是把 OCR 服务返回的结果直接呈现给用户,但识别后的优化,还有大量的工作要做。
  • 优化交互
    • 第一版只是用 Popover 的方式呈现结果。但通常用户对识别结果有进一步的要求,比如拖拽至原始图片附近、显示原图方便校对等等。这就需要对识别窗口进行优化。
    • 另外,用户识别的内容有多有少,固定的窗口大小就显得不合适,这就需要根据内容多少来调整窗口大小。
  • 新用户引导
  • 实现免费 + 内购(订阅)模式
    • 单单是选择哪种付费模式,我就纠结了很久。
    • 在限制免费版功能时,还要防止被破解,哎…
  • 程序本地化
    • 目前支持英文和简体中文。
  • 错误处理和日志系统
  • 适配不同操作系统
    • 目前支持 macOS 10.10+;其中,10.10 的支持有些麻烦,因为有些 API 仅在 10.11 及更新的系统上都有。
    • 还要适配 Dark 模式、菜单栏隐藏模式。
  • 沙盒模式
    • 这里提一点,我是调用系统 /usr/sbin/screencapture 进行截图的,而 Mac App Store 默认是不允许的。这时,需要在沙盒配置文件中加 com.apple.security.temporary-exception.mach-register.global-name: com.apple.screencapture.interactive 的规则。
  • 上架 Mac App Store
    • 应用描述、关键字、截图等。其中,截图的制作很花时间,既需要设计、又需要具体实现。
    • 尤其是,与审核员斗智斗勇…
  • 各种文案
    • 尤其是设计教程中的操作动画,非常耗时。

下面,在这些环节中,我挑些重要的环节,掰开了、揉碎了说说

2) 文字识别的核心:OCR

2.0) OCR 是项活化石技术

OCR (Optical Character Recognition) 其实是非常「久远」历史的话题。说久远,是相对于软件的发展速度,OCR 已经存在了好多年。而且,由于一直也都有这样的需求,对应的方案也在不断变化。

  • 没几年之前,基本都是 SDK、离线库,卖高昂的售价,通常都是卖给大企业。
  • 后来,License 也变也了订阅式的。
  • 直至今日,又变成了云端服务化,按使用量收费。
  • 甚至,云端这个词都要过时了,慢慢套上了「深度学习」、「AI」这些闪耀的词汇。

随着这些形态的演进,站在台前引领风骚的主,也在不断更迭。比如,之前一直被「百度更懂中文」「微软这种外企肯定更懂英文」这种思想紧紧套住。可后来一试腾讯的 OCR,发现某些图片的识别,中文比百度强、英文比微软强。以及,在技术方面生猛突进的 Google,都让 OCR这个领域不断发生变化

2.1) 腾讯、Google 双引擎

如何选择 OCR 的技术方案呢?

首先,我排除了离线的识别库,因为离线注定了这些库的识别能力是死的,不会自己增强。

接下来,在一票在线 OCR 服务中,我对比了百度、腾讯、微软、Google 这些四家大厂的产品。最后,在少量测试和主观感受下(比如,Google 信仰的加持,使得即使不测试,我就相信 Google 的 OCR 是很牛的),我最终选择了腾讯、Google 的服务。简单的说,如果你在国内使用 iText,默认使用的是腾讯的服务;国外则使用 Google.

那么,到底识别准不准呢?这么说吧:

  • 对于一般的自然语言,比如书中的一段话、新闻稿,识别效果是惊人的准确,甚至可以达到 100%
  • 对于排版复杂、尤其有特殊字符的文字,比如程序代码、选择题,识别效果就不太理想,需要手动对识别后的结果进行修正
    • 比如,单纯地给一个竖线,机器是无法区分到底是小写的 l、还是大写的 I(顺便问一下,你看出二者的区别了吗?);相反,机器是需要根据上下文进行判断和优化的。而像程序代码这种非自然语言,目前机器是很难进行语义识别的。
  • 手写识别?还很遥远
    • 期间,还有件有意味的事:有个朋友拍了一张医院病历上的神仙字体,问 iText 能不能识别。看到那歪歪扭扭的线条,能称为是字吗?无奈,现在中文手写的 OCR 基本还处于不可用的状态。少量测试过微软英文的手写识别,够强可用,但也得看潦草的程度。

到底准不准?欢迎你来试试。

顺便说一句,在接入各大服务的 REST API 接口后,明显感觉 Google 的接口最合理:不限制图片分辨率、仅限制原图大小。百度则相对差一些:限制图片分辨率且有 Bug、根据 base64 编码来限定图片大小。

2.2) 永无止境的段落识别

OCR 服务识别后,可以得到这样的结果:各文字片断及其位置。

但,如果根据这些信息还原出自然语境下的段落?这是很难的。我自己试着列出常见的段落分布,分析其中的位置规律,实现相应的识别段落的算法。并且,根据自己和用户测试的图片,根据识别结果对算法进行优化,还是有些效果的。

不过,并不完美。主要是,一些场景下,除非结合语义识别,否则是无法仅根据位置信息来划分段落的。

期间,我还花了 1 天左右的时间 识别多栏

虽说最后也差不多实现了,但却增加了程序的复杂度,让其它一些原本很简单的问题,变得难以解决。经过一番思考后,我还是删除了这部分代码,仅保留了普通段落的识别。回过头来看,之前决定做多栏识别的决定并不正确。因为这明显优先级低,又需花费大量时间。毕竟,还有很多其他更基础、更重要的事要做。

2.3) 更加永无止境的文本优化

首先要知道 文本优化正确的规则 是什么,比如:

  • 空格
    • 中英文之间需要增加空格
    • 中文与数字之间需要增加空格
    • 数字与单位之间需要增加空格
    • 全角标点与其他字符之间不加空格
  • 标点符号
    • 不重复使用标点符号
    • 破折号前后需要增加一个空格
  • 全角和半角
    • 使用全角中文标点
    • 数字使用半角字符
    • 遇到完整的英文整句、特殊名词,其內容使用半角标点

这里主要参考的是 稀土所翻译的文章排版的规则,在此致谢。

如果说段落识别很麻烦,文本优化则更多麻烦,根本的原因在于:需要对内容进行识别。比如:

  • 英文段落首字母大写,这规则没错吧,但对于 “iPhone is a good phone.” 这样的情况,iPhone 是特定词汇,i 不应该大写。
  • 中文文字后的 ‘.’ 应该使用全角的 “。”,这规则也没错吧,但对于『今天天气真热啊…』,明显又不应该替换;亦或是 JSON 数据『”name”: “张飞”』中的 “ 也不应该被替换。
  • 以及其他无数多的例子。

没办法,只能有所取舍。目前,iText 主要完成了这些部分的优化:

  • 自动识别段落
  • 中文环境使用全角标点符号
  • 中文与英文字母、数字间增加空格
  • 删除中文字符间、英文字符与标点符号间的多余空格
  • 英文首字母大写

技术方面,主要是使用 正则表达式 来识别并替换。不过,规则越多,正则表达式也越复杂,稍微改动下某条表达式,可能会破坏其他的。甚至,正则表达式之间的顺序,对优化结果也有影响。且表达式多了,也会降低执行效率。还得再想想如何优化。

这里要提一句 单元测试 了。我每实现一条规则的优化,就会补全对应的测试用例。接着再实现下一条则,这些单元测试可以忠实地保证新的规则没有破坏旧的规律,很是有用。

3) 开发过程中的一些感想

下面是一些零散的、我觉得值得拿出来跟大家分享的一些点。

一提 Logo 就扎心…

在我已发布的产品中,只有 Klib 的 Logo 是朋友 Allen 设计的,也只是这个没被骂过丑。其它均是我自己用设计,都被吐槽过…

这次,我找了位设计师帮忙,前后也设计了好几版:

不过,可能时间有限,没找到打动自己的那款,只能沿用 i 系列的风格。开始时,是这样的:

后来,「精心」设计了颜色,便是文章开头的样子。先这样吧,之后会考虑 i 系列家族统一更新。

至于名字,你一看我之前的产品,应该也会很自然地想到:iText(顺便说一句:Klib 之前其实是叫 iKindle,可惜苹果不允许名字中包含 Kindle 这个商标,只能被迫改名 Klib)

3.1) 新用户引导

这事的矛盾之处在于:新用户和老用户的矛盾

进一步说,我追求程序的简洁。尤其是在用户对 iText 熟悉之后,并不需要程序有过多的描述。比如,文字按钮可以被图标按钮取代,用界面上根本看不到的快捷键进行操作,等等。可问题是,熟悉是需要一个过程,新用户需要多次操作后,才能掌握这些甚至有些晦涩的技巧。没有新用户,又何来老用户?

这二者的矛盾,怎么解决呢?

我能想到的办法是:保持界面的简洁(照顾老用户),同时在首次运行时增加提示(照顾新用户)。提示包括:

  • 首次通过菜单截图时,提示可以通过快捷键截图。
  • 首次展示识别结果时,提示识别内容已复制到剪贴板。
  • 首次通过菜单展示识别结果时,提示可通过快捷键展示。

比如:

3.2) 用户越多,公约数越少

比如,对于工具型软件,有人偏好效率,有人喜欢美观。想要二者兼得,怕是很难的事,我自认为没有这样的能力。

可是,转念一想,何必二者兼得?如果我只找到偏好效率的用户,全心全意为他们服务,把他们伺候爽了,不就可以了?至于喜欢美观的用户,简单,让他们选择美观的同类产品不就可以了。弱水三千,只取一瓢饮。

3.3) 要解决的,不是问题本身

在处理 macOS 兼容性时,遇到过一个头痛的问题。

目前,iText 支持 macOS 10.10+,我日常自然是在最新的 10.13 中开发的。发布前,在 10.10 环境下测试时,发现了识别结果窗口布局有严重的问题。定睛一看,原来 Xocde 中早有警告:我使用了只在 10.11+ 平台才支持的布局方式,在 10.10 中自然会有问题。

怎么解决呢?

这个问题本身,是指界面布局有问题。OK,那我就解决布局问题呗。问题是,既然 10.11 引入了新的布局方式,自然是这种方式很有用、很方便。而我要在 10.10 模拟出这么布局,相当于开发了 10.11 的部分新功能,这并不是件容易的事。尤其,事实上 10.10 的支持在我这边优先级很低,只是希望顺带支持,并不希望就此花费大量时间。在已经有些复杂的布局基础上继续加补丁,不是好的办法。

跳出问题本身一想,问题的本质是:为 10.10 的用户展现布局良好的窗口。那,为什么一定要在 10.11+ 已有的窗口基础上解决问题呢,为什么不新做一个窗口呢?既然 10.10 的优先级低,那我就做个简化版的窗口,仅保留最重要的展示结果、复制内容等功能,放弃展示图片预览、钉住窗口等高级功能。界面元素少了,布局就是件很简单的事了。

这就是我的感想:要解决的不是问题本身(布局的问题),而是问题的本质(为 10.10 的用户提供布局良好的界面)。跳出问题本身,往往能发现解决问题本质的更简便的方法。

4) iText 是要赚钱的

iText 一定是款需要付费才能完全使用的产品:

  • 在线 OCR 服务是需要付费的,通常是按次收费
    • 注:腾讯 OCR 服务目前在内测期,免费;不过收费只是时间问题。
  • iText 是商业产品,不是业余爱好
    • 作为独立开发者,开发 iText 使用的并不是业余时间,而是我全部的工作时间;不是业余爱好,而是想要养活自己的产品。我需要为花费的时间买单,并支撑自己继续做独立开发者。

4.0) 上架 Mac App Store 时遇到的坑

由于开发和推广方面的考虑,我并未构建自己的序列号及付费机制,目前的产品均未在 Mac App Store 外销售;iText 亦如此。

提交 iText 上架 Mac App Store 被拒了多次,最开始上架时的版本号是 1.0.1,最后成功上架的是 1.1.0…

下面是被拒的一些原因及解决方式:

  • 没有在上传用户数据前提示用户

    • 其实是这样的,iText 需要上传图片至腾讯、Google 服务器进行识别。而苹果认识这是在上传用户个人数据,需要充分告知用户
    • 解决方式:在用户首次上传图片前,简单粗暴地弹个对话框提示:
  • 订阅后没有展示剩余有限期

    • 恩,这听着也很有道理。可问题是,我之前 Klib、iPic 中的订阅,也并未提示,为什么审核就能过呢?并且,用户其实是可以在 Mac App Store 中查看这一信息的。哎,这道理没法讲,审核是大爷,想起一出是一出
    • 解决方式:显示剩余时间呗
  • 产品不适合「自动」续订的订阅

    • 审核员说,「自动」续订的订阅适用于杂志等类型的应用,不适用于 iText 这种功能型的应用。可,同理,为什么之前 Klib、iPic 就可以?依然同理,不可说。
    • 解决方式:有点搞笑,我啥都没改。再次提交相同模式的订阅,通过了。也许是换了个审核员、或者审核员的心情变好了。

其实,审核前我还一直担心的一个问题,反而没有被卡:在程序描述及截图中,出现「Google」的字眼。印象中,之前因为出现「Android」被拒,这次倒是侥幸过关了。

最郁闷的是,审核员不会一次告诉我所有问题,而是分批…

  • 先用问题 1 把我拒掉
  • OK,我改;然后提交新版
  • 再用问题 2 把我拒掉
  • 如此循环…

另外就是,如果你说:别人家的 App 就是这么干的。没用,在审核员眼里,这并不是你也可以这么干的理由。

上架过程还 吃了几次暗亏,也说出来大伙听听:

  • 程序和内购需要分别提交
    • 也即,在首次上架时,在提交二进制应用进行审核的同时,也需要「手动」提交所包含的内购项目进行审核。而我遗漏了这一点,导致被拒一次。
    • 虽然,我在道理上理解这一逻辑,可苹果明显在提示等方面做得不够好。想我之前也提交过,可这次依然忘了,说明这并没有很强的因果关系,确实是需要提示的。
  • 订阅模式改价格,需要提交一天
    • 本来,我在 29 号凌晨就审核通过、并自动上架了。可在我将价格改为 6 折时,却发现要到 30 号才能生效。这迫使我不能在 29 号开始宣传,而只能等到 30 号新的价格生效后才行(因为宣传中要提到「首发限时 6 折」这样的字眼),白白浪费了一天,也让首发时间从周三推迟到周四,效果上打了折扣。

另外有一点,大家可能要注意下:新上架、或新版发布、或价格变更时,App Store 的更新需要时间。如果一发布就立马开始推广,用户可能会遇到搜索不到、无法下载、价格未更新、无法升级等奇怪的问题。建议过段时间再推广,比如头天晚上发布,第二天早上开始推广。

4.1) 免费功能到底该如何限制

这是个艰难的决定。

简单的说,如果免费的功能多了,用户就没有付费的必要;如果免费的功能少了,用户可能就要开骂,甚至直接离开,也就谈不上付费了。要把握其中的平衡,着实不易。

另外,除了考虑到收入本身,还要考虑产品的影响力。比如,产品刚发布时,知名度比较小。这时候,用户数量相对更重要些,比较适合 放水养鱼:适当放弃些收入,通过放宽免费功能的限制,来增加用户数量、留住用户。等产品有足够的影响力,就有定价话语权了。

比如,微软对于盗版 Windows、Office 的做法就是典型案例。开始时睁只眼、闭只眼,等大家已经离不开时,就开始向政府、上市企业寄律师函了。

说回 iText,目前免费版未对功能有任何限制,仅限制识别次数。且,每月都有一定的免费额度,使得用户不会刚开始没用几次、一遇到付费就卸载的情况。之后看运行的情况再调整,比如可以调整为每天都有免费次数。

4.2) 说说付费模式

接下来的问题,便是如何确定收费模式。有这几种方式可考虑:

  • 买断式
    • 也即,你付一次钱,就可终身使用。
    • 考虑到 OCR 服务的按次收费,个人觉得这并不科学,至少不具可持续性。
  • 流量包式
    • 比如,¥12 元买 250 次识别次数。
    • 对用户而言,这种方式 对低频使用的用户友好,但对于高频使用的用户,并不太划算。
    • 另外,最关键的是,这种方式会给使用上带来很大的心理压力。比如,我在截图时不小心区域没选对,结果白白浪费了一次识别次数,这会让我在每次截图时都会有不小的心理压力,生怕截错了。虽然,从理性的角度,一次真没多少钱。但没办法,这种心里上的事情是很难用理性压住的。
  • 包月式
    • 也即订阅式,即一个月出固定费用,即可不限次识别。
    • 对用户而言,优缺点几乎和流量包式相反:对低频用户不友好、对高频用户友好
    • 最终,我选择的是这种模式:每月可免费识别 20 次,充分体验 iText 的功能;订阅高级版即可不限次识别;这样,可以 同时照顾到低频用户和高频用户。首发时 6 折优惠:¥3 每月、或¥30 每年,续费时恢复原价。

其实,这跟手机流量非常像:

  • 如果你用量很少,1 MB 流量 0.1 元的方式就很适合你。
  • 如果你用是很大、或者就想用得爽,包月得到固定流量、甚至是不限量的套餐,更适合你。
  • 不过,我还没见过付一次钱,就可终身不限量使用的套餐。

5) iText 上架后的宣传

其实,这方面我也一直在学习,这里谈不上经验,只是把自己做的事罗列出来。

5.0) 宣传时涉及的渠道

从渠道的角度,大致有这样:

时间有限,我基本上是写通稿,然后针对各个渠道做简单的调整。上面有各渠道对应的文章链接,感兴趣可以点进去看看。其中可见,我做的海外推广很少,这方面求帮带。

其中有个玩法就是:送码。通过这种方式,增加与用户的互动,如留言区的评论、微博的转发等等。

另外,如果你的应用有自己的序列号体系,也可以上「数码荔枝」这样的平台分销,据说效果也不错。可惜我没有序列号,无法触及这些平台。

5.1) 写文案是体力活,也是技术活

上面的每个平台,都需要针对性地写文案。

虽说,好的产品自己会说话、简洁的程序不应该有说明书。可无奈,酒香也怕巷子深,大家在真正使用产品之前,总需要一些方式来了解到产品。而在目前的传播方式中,文案则是必须的了。

这里说的文案,包括:

  • 产品本身的说明书、教程
  • 发布在媒体上的文章
  • 发布在朋友圈等 SNS 上的信息
  • 以及其他所有对外爆光的东西
  • 除了中文,还有英文…

当然,除了文字,也包括配图、甚至是视频。而这两者,通常比文字更花时间。

比如,在做上面的图时,首先要明确表达什么、构思如何表达、确定涉及到的素材、录屏、制作 GIF,整个过程流程很多,非常耗时。且一旦需要修改,几乎是全部重来的成本。顺便说一句,如果你想知道上面的 Gif 如何制作,看这里

比如 标题,如果纯粹是反映文章内容,很可能不够吸引人;如果起个抓眼球的标题,又极容易被骂标题党。另外,除了考虑一时一地的点击量,还得从长线上考虑搜索引擎的收录。绝对是技术活。

5.2) 如何量化追踪每个渠道的推广效果

比如,发了一条微博,有多少用户因为这条微博下载安装 iText;进而,这些用户中有多少人付费购买。有了这些量化数据,才能反推出每个渠道的获客成本,进而决定是否要在一个渠道推广、制定推广预算。

可见,量化追踪每个渠道的推广效果,非常重要,因为直接跟钱相关。这里,推广效果最直接的考量是转化率,但又不仅于此。比如品牌的露出、品牌形象的建立,都是要考虑的因素。那么,如何量化转化率呢?可考虑使用 iTunes 联盟

和其他淘宝客、亚马逊联盟等联盟一样,iTunes 联盟也是苹果为了推动第三方来替自己推广音乐、App 的下载付费,而推出的奖励机制。简单的说,就是你帮苹果销售其音乐、App,苹果会给你一定的的提成。具体到 App,提成比例大约是 0.4%

当然,如此低的提成比例,自然不是我们关心的;我们关心的是渠道转化。比如,在下面的链接中:

https://itunes.apple.com/app/id1314980676?ls=1&mt=12&at=1000lv11&ct=iText_Weibo

其中,1000lv11 是 iTunes 联盟 ID,iText_Weibo 是 需要追踪的事件,也即 iText 在微博上的推广;类似的,也可以有 iText_Twitter,iPic_Facebook 之类的事件。

如果用户通过这个链接安装付费,就可以得到类似这样的数据:1000 人点击微博中的链接、600 人真正安装、20 人付费内购

授之以渔,我就不在这列 iText 具体的数据了。大家如果需要,可以到 这里 申请 iTunes 联盟。

尾巴

终于,iText 还是顽强上架了(点此下载)。至此,我已集齐七颗龙珠:

iText 接下来会怎样?估计我会根据上架后用户的反馈,在短期内再出个版本。之后,会按住一段时间,直到对更远些的发展想清楚后,再出新版。

我本人接下来会怎样?估计还是会坚持做独立开发者,开发自己的产品,开发世上本没有的产品,用自己的能力,改进大家的效率,哪怕只是一点点。

计划跟不上变化。你明天看到的我,已是不一样的我。