制作印刷版或 PDF 版的口译员

2021-07-29 23:43:25

我的关于编程语言的《Crafting Interpreters》一书已经完成。好的,好的。我知道我说过十五个月前就完成了。不过现在真的大功告成了。我的意思是,印刷版、电子书和 PDF 版本都完成了。你可以买。你可以把它握在手中。我的意思是“手”复数。因为这本小“手册”比我预期的要大得多:看看那个东西。 640 页,八英寸宽,十英寸高。如果您厌倦了阅读它,它可以用作门挡或保护您免受轻武器的伤害。还记得罗杰先生的邻居带你去工厂向你展示铅笔或雨伞是如何制造的吗?我喜欢那些东西,所以我想也许你可能想看看我过去一年都在做什么。你可以在幕后偷看这篇文章,或者为为什么花了这么长时间而道歉。我在上一篇文章中说的是,Crafting Interpreters 已经完成,我的意思是我已经编写了所有代码,在最后一句话加了点,并扫描了最后一个插图。书的内容是完整的。但这还不是一本书。只是一堆 Markdown 和 PNG 文件,我那糟糕透顶的 Python 代码会不情愿地把它们变成一个网站。我最初的目标始终是制作一本真正的书,有页面和所有内容。完成内容只是那段旅程中的一个(当然,最重要的)步骤。完成最后一章并将其放到网上后,我休息了大约一个月。近四年来,我每天都在写作,我感到很累。另外,如果您不记得了,2020 年初在世界历史上并不是一个完全好的时期。几周后,我完全完成这个项目的愿望又回来了,我又重新开始工作。我修复了一大堆错别字和其他读者提交错误的错误(谢谢!)。然后,完全没有充分的理由,我决定用 Dart 重写这本书的整个构建系统。我为我的第一本书编写的构建脚本非常简单。字面意思是一个单独的 Python 脚本,它为每一本书的章节获取一个 Markdownfile,并在编入代码片段的同时将其呈现为 HTML。世界上最笨的静态站点生成器。

我从制作口译员开始,但后来它越来越大。我的第二本书包括两个完整解释器的每一行代码,它在三十章中逐步构建。我不仅需要为这本书的网站构建 HTML,还要确保代码确实有效。我让构建系统不仅能够为本书生成站点,还能够对代码进行切片和切块。给定一个章节,甚至一个章节中的一个点,它可以输出一个程序,其中包含解释器到该点的所有代码。然后我可以获取该代码,编译它,并通过我的自动化测试套件运行它,以确保我向您展示的代码执行它应该做的事情。很有用,但确实限制了我想在像 Python 这样的动态类型语言中维护多少代码的限制,至少在我(低)水平的 Python 专业知识的情况下。此外,坦率地说,它真的很慢。所以在几周的时间里,我用 Dart 重写了整个内容。我在 Google 的 Dart 语言团队工作是我的日常工作,所以选择 Dart 并不是一个公正的选择。但它是我的书的构建系统,我知道 Dart 及其许多核心库和包就像我的手背一样。见鬼,我个人编写了规范 markdown 包的初始版本。我发现了一个非常不错的小胡子模板包,所以我将这本书的旧 Liquid 模板转换为那个包。我没有找到一个好的语法高亮器。但也不是我看起来很努力。从头开始实现似乎是一种有趣的事情,所以我根据 Pygments 松散地制作了一个。最终结果是一个新的构建系统,它可以准确地生成我想要的 HTML 和语法突出显示的代码。此外,它实际上比旧的 Python 快十倍。正如您将看到的,事实证明,我可以更好地控制 Markdown 处理,这很方便,但当时我这样做基本上只是为了好玩并拖延实际工作。一旦我的新构建系统输出了漂亮干净的 HTML 并且我删除了旧的 Python 内容,就该开始了。做一个像书一样的大型平面设计项目很像网络开发或游戏编程,它有两个层次。首先你设置你的“框架”或“引擎”。在 Web 上,这是您的 Web 框架以及您的所有 CSS 和 HTML 模板。在游戏中,它是您的游戏引擎。然后您将内容倒入该结构中。使用正确的框架,添加内容很容易。

对于使用 Adob​​e InDesign 之类的程序进行图形设计,其工作方式是设置样式和母版。母版定义页面的边距和网格线。围栏将所有文本围起来,以防止那些野蛮的字母四处乱跑,吃掉所有的空白。样式就像 CSS:它们让您获取语义类型的文本或对象,并为其关联特定的字体、样式和颜色。从理论上讲,您获得了正确的主人和样式,然后排版非常简单和机械。现在,在书籍设计方面,我并没有让我的生活变得轻松。书籍设计实际上是一种二维空间练习,我在横向和纵向上都让我的工作更加困难。如果您在网上阅读过其中的任何内容,您就会知道我的书有: 很多旁白需要紧挨着它们所引用的某些文本、代码或插图。其中一些可能会很长。大量代码。此外,每个代码片段旁边都有一个小位置简介,告诉您代码在结果程序中的位置。在水平方向上,主文本列需要足够宽以适合最长的代码行。我可以使用更短的行并更多地包装片段,但这会使它们更难阅读。它还使它们在垂直方向上更高,这会导致其自身的问题。然后我需要在旁白旁边留出空间,因为他们经常直接评论特定的句子。当然,我也可以使它们变窄。但后来他们包裹起来并变得更高。一些边线相当长,如果它们太高,它们就会开始相互碰撞或重叠位置片段。哦,因为我最终写了一本超过 20 万字的书,所以它的页数会很高。这意味着一本厚书。厚书需要更宽的内边距,这样文本就不会消失在书脊中。

所有这些都指向一个相当宽的页面。大多数 CS 教科书——至少是我书架上的那些——都是 7.5 英寸宽。我努力想出一种适合该宽度的代码、边距和健康边距的设计,同时仍然提供不需要放大镜的文本大小。最终,Iconceded失败了。一旦我尝试为 8 英寸宽的页面设计一组指标,一切就都到位了。我可以在文本周围留出足够的喘息空间,让阅读更愉快,代码片段的长度合适,并且留出足够的空间。 (对旁白使用较窄的字体也有帮助)。这离开了另一个维度。如果我要与真正的出版商一起进行全胶印印刷,我可以选择我想要的任何页面大小。但是因为我自己出版,这意味着要预先支付数千份要印刷的费用,而且我不知道,把所有的盒子都放在我的车库里什么的。按需印刷对我的上一本书效果很好,我计划为这本书做同样的事情。这意味着坚持 KDP 和 IngramSpark 支持的有限页面大小集。唯一合理的 8 英寸宽是 8"×10",所以这就是我选择的。最终的结果是一本感觉很大的书,但希望不要笨拙地巨大。抱歉。如果我再写一本,我保证会写一本更小的书。在我选择这些边距和指标的整个过程中,我也在选择字体和构建样式。你不能在真空中做平面设计,所以我挑了一个测试章节,把它排版,一遍又一遍地调整字体。最终我得到了一套我喜欢的字体和样式,一些看起来可行的大师,我准备好了去。框架已经完成,现在是时候打开内容软管了。当然,问题 #1 是没有内容软管。我必须构建一个。InDesign 不知道 Markdown 或我奇怪的屁股构建系统是什么。我当然不想,比如,从我的浏览器复制每一章,粘贴到 InDesign,然后手动应用所有样式。我有很高的油漆耐受性,但我不是受虐狂。 InDesign 可以做的是导入 XML。更好的是,您可以将其设置为自动将特定段落或字符样式应用于特定标签名称。唉,自从我将其描述为由一名嗜睡症实习生实施以来,InDesign 的 XML 支持还没有成熟太多。例如,在 HTML 中,您可以通过采用斜体标记并将其嵌套在标题标记中来将标题中的单词设为斜体。 InDesign 无法理解这种高级数据建模。它需要一系列非嵌套的标签,如果你需要在你的标题中使用斜体,你最好有一个独特的 <italics-header> 标签。

但现在我有一张王牌。由于我对我的构建系统和它的 Markdown 处理有微观控制,我可以编写自己的自定义 XML 导出器,它生成的标签准确地使 InDesign 不会哭泣并尽可能避免 InDesign XML 导入错误。即便如此,XML 导入也只能让您走到这一步。具体来说,它为您提供了 InDesign 术语中的“故事”:填充主文本框并跨越多个页面的单一连续文本叙述:主要故事是散文和代码片段所在的位置,但需要拉出旁白和位置标记出来漂浮到一边。在我的上一本书中,我手动将它们拉出。我从字面上将每一个从主文本列中删除并将其粘贴到一个新文本框中。它花了很长时间,而那本书的长度不到这本书的一半,而且代码片段更少。 Crafting Interpreters 中有 1,133 个代码片段。如果我必须为每个位置手动剪切和粘贴位置标记,我会失去理智。更糟糕的是,我会犯很多错误,这只会为我自己创造更多的工作。我是一名工程师,所以我在道德上反对可以自动化的容易出错的手动工作。您知道 InDesign 可以使用 JavaScript 编写脚本吗?好吧,你现在这样做了,这显然使你成为少数人,因为那里没有文档。您可以找到一些自动生成的参考,一些来自平面设计师的悲伤呼喊声,这些呼声显然超出了他们的元素而没有回应,仅此而已。 JavaScript InDesign 是一种特殊的痛苦。没有调试器。没有堆栈跟踪。甚至没有调试打印。字面上只有alert(),你只能调用一次。它会停止你的脚本。幸运的是,当所有浏览器都给你的时候,我实际上学习了 JavaScript,所以我可以破解它。我设法拼凑了一个可怕的脚本,它可以找到所有的旁白和位置标记,将它们从主要文本流中拉出来,然后将它们放在一边:

我无法让脚本做的是正确定位框。但是 InDesign 有一个叫做“锚点”的东西,您可以在其中锁定一个元素相对于另一个元素的位置。一些精心设计的对象样式甚至可以正确设置水平度量并将文本与正确的基线对齐。我所要做的就是锚定每个文本框,它工作得很好!等等,我说“完美”了吗?我的意思是它大约有一半时间正常工作,而另一半时间 InDesign 会莫名其妙地关闭附近代码片段的他妈的边界。我因为这个愚蠢的错误而失去了生命中的几个小时。最终,我意识到必须手动定位一些完全随机的位置标签子集,因为锚定总是会打破一些边界。以上所有内容都花了我一个月左右的时间,然后是时候停止摆弄并开始处理实际内容了。我做的第一件事是对整本书进行另一次编辑,从前到后。我在写每一章的时候已经做了三个草稿,但现在我想再做一个,这样我就可以更好地感受到连续性。原来我重复了很多相同的愚蠢笑话。我修复了(大部分)那些。这花了五个月的时间。这没什么好说的,这只是个苦差事。接下来,我聘请了一位真正的专业文案编辑卡里·萨默顿 (Kari Somerton) 来完成并做同样的事情。她很棒。大多数编辑世界使用 Microsoft Word 和“跟踪更改”来处理编辑过程。像大多数软件工程师一样,我生活和呼吸纯文本和 Git。这样我就可以看到变化的差异,并回顾历史。我不想放弃我的工作流程,所以我要求 Kari 使用 Git 和 myweird 完全定制的构建系统。她沉着应对,很快就翻阅了这本书。她发现了成百上千的错误。尽管我做了四份草稿,读者已经提交了数百期。专业​​的文案编辑值得每一分钱。

一旦这些词达到了他们想要的效果,就该把它们放到页面上了。过程是这样的:前五个步骤是小菜一碟。我会在早上醒来,冲泡一杯咖啡,蹒跚上楼到 iMac,然后开始。我可以在半睡半醒的情况下完成这些步骤,并在半小时左右的时间内完成一章。很平静。几乎是冥想。然后第六步。你看,这就是排版一本书的难点。排版这本书真的很困难。因为事实证明,内容在页面中垂直放置的方式有很多限制。显然,我们不能将插图切成两半并将上半部分放在一页上,将下半部分放在下一页。旁白也确实需要放在一页上,否则很难跟踪它们所指的内容。只要有可能,代码片段最好不要跨页面拆分。其中一些可能长达十几行。 (这是为什么更宽的水平指标有帮助的另一个原因。因为如果我让代码片段更窄,它们最终会更高,这会使它们更难适应页面。)而且你不希望在一个单独的标题的末尾之后没有内容的页面。避免寡妇和孤儿是件好事……考虑到所有这些规则和限制,并在完全固定的页面高度中混合,您就会得到一个真正的约束解决问题。或者,在我的案例中,其中 640 个相互交织。因为,您看,InDesign 很乐意通过将内容推送到后续页面来为您解决所有这些问题。代码片段太长?将其移至下一页。标题下没有散文的空间?将其全部移至下一页。这给你的是页面底部的大量死白空间。它看起来很糟糕并且浪费空间,就像这样:在一个层面上,插图很容易。我特别选择了黑白笔和墨水,因为它适合打印。当我在写每一章时第一次扫描图像时,我把它们带到了 1200 DPI。这是一个作物:太详细了!将这些导出为打印良好的高分辨率位图非常简单,而且它们看起来很棒。 (嗯,我猜,和我的笔迹看起来一样好。)

将插图纳入页面布局是另一回事。排版进行到一半时,一个灯泡熄灭了,我终于明白为什么大多数书上都说“请参阅图 123 看看等等等等……”这让排版人员可以自由地将图 123 放在任何附近页面上适合它的任何地方。在我的哑书中,因为我是个白痴,所以散文只是直接引用插图。插图需要就在那里,否则文字没有意义。当每一章都是一个无限滚动的网页时,我没有想到这一点,当我意识到时,已经太晚了。凭借数百个插图和数千个代码片段,我给自己做了三十个巨大的相互关联的装箱练习。排版的困难部分是弄清楚如何调整东西以最小化死区。有时我会将代码片段一分为二。也许在一张图片周围添加一点额外的填充,以在页面上稍微散布一些东西。或者挤进另一个,使其正好适合页面。有时我会调整插图以使其更短以适合页面或更高以占用一些空白。这是排版这本书的真正挑战,也是为什么我花了两个月的时间来完成所有章节。你知道有专业的索引器吗?为书籍编写索引的人?他们甚至写了关于如何编写索引的书。 (人们会认为这些书确实有极好的索引。)我没有聘请那些技术娴熟的专业人士。取而代之的是,我花了两周的时间一遍又一遍地阅读每一章,尽我最大的努力假装我知道我在做什么。 InDesign 对索引的支持实际上非常好。您基本上可以只选择一些文本并说,“为此创建一个索引条目。”然后它会收集所有这些并为整本书生成一个索引。但实际上添加所有这些条目是一件令人麻木的苦差事。索引是“背景资料”的主要部分——一本书的主要内容之后的内容。还有“前沿”。你永远猜不到那会去哪里。我把标题页、版权页、奉献和致谢放在一起。然后我让 InDesign 为我生成一个目录。

许多作者都痴迷于他们的封面,并在他们写作的整个过程中都在设想它。不管这句格言怎么说,人们确实会根据封面来判断一本书,一个好的封面会产生很大的不同。至少,在虚构世界中是这样。在计算机科学领域,从我身边的其他书籍来看,封面的艺术价值似乎不那么重要。我猜当教授说你必须买书才能通过课程时,剪贴画封面就足够引人注目了。由于我不是一个可以通过法币获得销售的教授,我花了很多时间在封面设计上。我拍照片,所以我觉得在封面上放一些细节来让它活跃起来会很好看。我浏览了数千张照片,试图找到适合的东西。而且,虽然我有一些漂亮的图片,但没有一个觉得它们是封面。他们觉得随意。最终我意识到这本书的视觉语言就是那些笔和杀戮。所以我画了一个更大更详细的山图,我用它来比喻编译过程。我还手写了一个新标题:它是一种真正的字体(Acumin Pro Extra Condensed),但我手工绘制了打印输出以赋予它一些不完美的魅力。我选择了一个调色板,试图给它一种油印的 1950 年代球探手动氛围。现在我真的有一本书了。我将 PDF 导出文件上传到 KDP 并订购了一份校样。一周后,一个惊人的重箱子到了。这是我第一次真正明白我写的这本书有多大。到目前为止,它只是数据文件。但是看到它填满了亚马逊的盒子,我用我花费的时间从未完全做到过的方式澄清了该项目的规模。所以我有一本书,但它仍然没有完成。因为排版过程涉及大量的体力劳动。犯错是人,所以现在我必须校对——逐字逐句地通读校对并阅读它以找出错误。我用便利贴标记了它们:这就是压力大的地方。如果您是一名程序员,那么源代码控制和差异在您的工作流程中根深蒂固。每当我进行更改时,我都认为我可以在提交中看到差异以验证我只更改了我想要的内容而没有其他任何内容。

我确实将 InDesign 文件放在 Git 存储库中,但它们是巨大的不透明二进制文件。此外,InDesign 有改变它们的习惯,即使它们看起来不像......