[发布] CJK与拉丁系语言像素级混合排版实现

终于搞定了 knuth-plass 排版算法的 emacs 实现,同时对算法进行了进一步的改进,支持 CJK 和拉丁系语言的混合排版,详细设置和用法可以看 github 的 readme。下面是效果图:

ekp-demo-with-cache

22 个赞

很帅!

就是用起来会不会有点麻烦,好像要主动调用才能够进行排版?我有没理解错误?

让 AI 帮我写了一个简单的配置:

(defun my-justify-with-emacs-kp ()
  (interactive)
  (let ((text (buffer-string))  ; 获取缓冲区中的所有文本
        (line-pixel 500))         ; 设置每行像素宽度
    (delete-region (point-min) (point-max))  ; 清空缓冲区
    (insert (ekp-pixel-justify text line-pixel)))) ; 插入排版后的文本

(global-set-key (kbd "C-c j") 'my-justify-with-emacs-kp) ; 将命令绑定到 "C-c j" 快捷键

对,是要主动调用函数,因为这是提供排版功能的基础包,主要提供两个核心函数来给其他开发者使用。至于其他的更加交互式的功能应该由上层的包自己实现。

我不打算去实现这些上层的包,因为我主要的应用场景是将排版后的文本应用到文本块的布局中去。就是你之前问我的,可以设置文本块边框、padding、margin、背景色、对齐等功能的包,以及可以对多个文本块进行任意的组合布局。这个包完善的也差不多了,后续会发布出来。

上面的这个包也是ETAF文本布局系统中的一部分,一环套一环。

我记得你之前研究过 ewoc,所以 emacs widget 和你现在所提供的排版引擎有什么不同?

手动点赞大佬

这几个东西不是一个层面的:

  1. emacs-kp 专注排版,是基础功能;
  2. emacs widget 是emacs内置的提供组件的库,可能部分组件涉及到长文本展示会用到排版,当然如果不追求美观,直接在行尾暴力断行也不是不行;
  3. ewoc 更类似于现代前端框架(vue,react…)中的“数据驱动”的概念,即把文本界面的渲染绑定到数据中,实现界面的增量更新。

这些概念统统结合起来,其实就是要在 emacs 中实现一个基于文本的应用程序的前端框架,这就是 ETAF 要做的事情。之所以要做这个,就是因为内置的 widget 不太好用,而且功能很局限。ETAF要做的是在emacs中实现一整个基于现代前端理念的文本框架。

1 个赞

@Kinney 如果按照 @yibie 的代码重复 justify 一个 buffer 对齐就会乱掉。

第一次 justify , 正常

再来一次: 继续:

是这样的,因为 justify 是基于原始文本的。这个包只提供了两个函数用来对一个字符串进行对齐这个基础功能,就相当于一个api,任何其他的我认为都是业务功能的包需要自己来实现的。

了解,大佬的文本布局系统赶紧出一个 POC 版本让小弟们测试起来,急须。 :face_blowing_a_kiss: :face_blowing_a_kiss: :face_blowing_a_kiss:

急需 +1

哈哈,我抓紧~

@Kinney

(ekp-pixel-justify TEXT line-pixel)

会清除 TEXT 里原有的 properties。大佬有没有考虑在 ekp-pixel-justify 方法内部保留原 TEXT 中的 properties。

(因为ekp-pixel-justify 会修改 TEXT 中的空格数目,比较难 justify 之后还原之前的 properties)

(defun emacs-kp-render ()
  (interactive)
  (let ((text (buffer-string))
        (line-pixel 800)
        buffer-read-only)
    (save-excursion
      (delete-region (point-min) (point-max))
      (insert (ekp-pixel-justify text line-pixel)))))

(add-hook 'eww-after-render-hook #'emacs-kp-render)

用 ekp-pixel-justify 来重排 eww, 排版的挺好。 目前的两个问题是:

  1. 会在中文词语中间插入一些零宽空格,在文字间移动,或者复制文本需要额外的操作
  2. 无法保留原本的 eww face。

原版

重排

(ekp-param-fmtstr) 打印一下参数,看一下最后三位是什么,比如是 “8-4-3-6-3-2-0-2-0”,最后三位表示 CJK 的理想宽度,可拉伸宽度,可压缩宽度,如果不希望CJK字符之间有空格,可以使用 ekp-param-set 函数设置CJK拉伸宽度为 0。(我默认设置为了2)。

但是对于以中文为主的排版,设置CJK之间不允许拉伸可能会出现行尾不对齐直接断行的情况,动态规划会优先寻找最优排版,但如果出现很长的单词或者恰巧在某些特殊的像素位置,文本中没有足够的拉伸或压缩空间时,就会出现不对齐的情况,这种情况无法避免。

因此,相较于随机指定一个像素可能带来的上诉情况,使用 range 函数在一个小范围内寻找最优排版可能是一个比较好的方式。range函数的问题在于计算的效率和像素范围是成正比的,我打算后面用 rust 动态模块的多线程解决这个问题。

保留原本样式的问题,我空了想办法优化一下。

BTW:并非零宽空格,是有像素宽度的,可以 C-h p 看到设置的宽度。

1 个赞

测试了一波,还是保持 CJK 拉伸比较好。好看最重要。复制粘贴的问题,手动写一个函数清除掉多余的空格。

如果后续可以实现保留原本样式就完美了。

1 个赞