通过Org做公众号排版

创建于:发布于:文集:车轮滚滚

我的博客基本上都会同步到微信公众号,但是我从来不用公众号后台提供的编辑器,一个原因是它不好用,另一个原因是我已经写好了markdown文本格式,不想再在富文本编辑器里做一次排版工作了。过去我一直在使用 https://github.com/doocs/md ,但因为一些问题,我想从Markdown格式迁移到Org格式,找了一下没发现有可以将Org转换到公众号格式的工具,于是打算自己写一个工具用于转换。

界面预览

preview

代码仓库

目前的UI和功能都还比较简单,只是将Org文本贴进去,预览一下样式,再复制粘贴到公众号后台。

原理

微信公众号编辑器支持直接粘贴富文本内容。所谓富文本,顾名思义就是除了文字以外,还包含字体、大小、颜色、对齐方式等样式信息。如果可以从Org文本生成一个富文本内容,那么就可以做到编辑Org格式文本,再一键复制到公众号发布了。

DSL转换

首先需要做的,是两种不同DSL之间的转换。什么是DSL?

DSL(Domain-specific language,领域专用语言),是一种专门为了某个特定领域或应用场景而定制的计算机语言。相比通用编程语言,DSL更加小巧、简单、高度专注于特定问题域。它旨在提高开发效率,让程序员或最终用户能够用更自然的方式表达意图。标记语言是DSL的一种典型形式,主要用于对文本内容进行注释和赋予含义。标记语言使用一组预先定义好的结构化标记,将文档划分为不同的逻辑区块,并对每个区块的类型和语义做出约定,最经典的一种标记语言就是HTML(HyperText Markup Language 超文本标记语言)。

Org也是一种标记语言,举个例子,Org格式规定将一段文本用星号包起来,例如 *bold* ,就表示这段文本应当加粗,支持Org格式的应用,如Emacs就可以通过解析Org文档,获取这个信息,从而在界面上将这段文本加粗显示。

那为什么要从Org格式转换到HTML格式呢?因为微信支持HTML格式的富文本,并且HTML可以和另一个DSL——CSS结合,描述更丰富的样式信息。

具体的做法是通过一种叫做Parser的程序,将Org转换成一种树形结构,称为AST(Abstract syntax tree,抽象语法树)。例如一段Org文本:

* Example
some text
- item1
- item2

可以解析成这样一种结构:

{
    "type": "org-data",
    "children": [
    {
        "type": "section",
        "children": [
        {
            "type": "headline",
            "level": 1,
            "children": [
            {
                "type": "text",
                "value": "Example"
            }
            ]
        },
        {
            "type": "paragraph",
            "children": [
            {
                "type": "text",
                "value": "some text\n"
            }
            ]
        },
        {
            "type": "plain-list",
            "children": [
            {
                "type": "list-item",
                "children": [
                {
                    "type": "paragraph",
                    "children": [
                    {
                        "type": "text",
                        "value": "item1\n"
                    }
                    ]
                }
                ]
            },
            {
                "type": "list-item",
                "children": [
                {
                    "type": "paragraph",
                    "children": [
                    {
                        "type": "text",
                        "value": "item2"
                    }
                    ]
                }
                ]
            }
            ]
        }
        ]
    }
    ]
}

通过遍历这棵树的节点,可以递归地将其转换成HTML。

<h1>Example</h1>
<p>some text</p>
<ul>
    <li>
    <p>item1</p>
    </li>
    <li>
    <p>item2</p>
    </li>
</ul>

CSS内联

只是用HTML表示一个节点是标题还是无序列表,所携带的样式信息还是太少了。要想描述文本的字体、大小、颜色、间距等细节,还需要借助CSS(Cascading Style Sheets,层叠样式表)进行渲染控制。但是注意,我们不能单独写一个CSS文件上传到公众号后台,要想直接将使用CSS定义了样式的HTML复制到公众号后台,需要使用HTML的内联CSS语法:

<p style="color: red;">hello</p>

在从Org转换到HTML的过程中,可以直接为要生成的元素写死内联样式,但是为了扩展性(也许用户需要自定义CSS样式),在实际的代码中是在转换过程完成后,再将一个单独的CSS文本解析注入到已有的HTML文本中去的。

拷贝富文本

生成了内联样式的HTML后,怎么把它复制到公众号编辑器里呢?如果你直接打开一个网页,右键查看源代码,把这个源代码复制到公众号,会发现只是复制了HTML的源码文本,而不是最终呈现的富文本样式。怎么把富文本复制到系统的剪切板?其实只需要很短的代码就可以实现:

const type = "text/html";
const blob = new Blob([htmlStr], { type });
const data = [new ClipboardItem({ [type]: blob })];
navigator.clipboard.write(data);

Roadmap

目前这个工具的功能还是非常简单的,后续打算把之前用过的markdown转换的工具的功能都移植过来:

EOF
Github
Copyright © 2020-2024 Elliot