何谓 “文本块”

这里的文本块指的是“多行宽度相等的文本”。

当我们将一段文本(可能是多行)限定在宽度内时,根据文本原始宽度(text-width)和指定宽度(width)的不同会有下面两种情况:

  1. width >= text-width
    限定宽度超过了文本宽度,使用像素空格补齐剩余宽度。

  2. width < text-width
    限定宽度小于文本宽度,需要对文本换行以保证每行宽度都等于限定的宽度。

文本块模型

除了基础的文本展示块,文本块还支持一些额外的属性,下面是完整的文本块属性模型:

(defclass etml-block ()
  ((content :initarg :content :initform "")
   (width :initarg :width :initform nil)
   (justify :initarg :justify :initform 'left)
   (height :initarg :height :initform nil)
   (align :initarg :align :initform 'top)
   (padding :initarg :padding :initform nil)
   (bgcolor :initarg :bgcolor :initform nil)
   (border :initarg :border :initform nil)
   (margin :initarg :margin :initform nil)))

各个属性用法

content

原始文本内容,是一个字符串。

width

限定文本的宽度。支持两种格式的宽度:

  1. integer n 表示文本的前 n 个字符的像素宽度;
  2. cons-cell '(n) 表示 n 像素宽度

justify

文本在水平方向的对齐方式。仅当文本原始宽度(text-width)大于限定宽度(width)时生效。支持设置三种 symbol 值: 'left,'center,'right,分别表示文本靠左、居中和靠右。

height

限定文本的高度。Integer n 表示文本限定为从首行开始的 n 行。

align

文本在垂直方向的对齐方式。仅当已经限定了宽度之后的文本高度(text-height)小于限定高度(height)时生效。支持设置三种 symbol 值: 'top,'center,'bottom,分别表示文本靠上、居中和靠下。

padding

文本在四个方向的 padding 值。水平方向的指也支持两种格式,同 width 属性。垂直方向的值为正整数 或 小于1的浮点数:正整数表示 padding 的空白行数;浮点数表示 padding 小于1的行高(当该行有多个文本块时可能会失效),通过设置 line-height 属性来实现,与文本的首行或尾行实际上使用的同一行。

以上说的各个方向的 padding 值支持的数字类型和含义。对于 :padding 属性,支持以下丰富的形式进行设置:

  1. nil: 表示不设置 padding,会被解析为 '(:left 0 :right 0 :top 0 :bottom 0)
  2. t: 表示设置一个默认的 padding,会被解析为 '(:left (4) :right (4) :top 0 :bottom 0)
  3. cons-cell: car 部分的值表示水平方向的 padding,cdr 部分的值表示垂直方向的 padding。
  4. plist: 表示分别设置指定方向的 padding 的值。plist 的格式就是上面解析之后的形式。

bgcolor

文本块背景色。仅在 border 以内的区域设置背景色,即包含:padding 和 文本部分。

border

设置四个方向的边框。使用 face :overline 属性实现上边框,face :underline 属性实现下边框,颜色反转的1像素空格实现左右边框。由于实现原理限制,上下边框只支持设置颜色,不支持设置宽度。

支持以下丰富的格式:

  1. nil 表示不设置边框,会被解析为:

     '(:left (:width 0) :right (:width 0) :top (:width 0) :bottom (:width 0))
    
  2. t 表示四个方向边框都设置默认颜色(当前 frame 的前景色),会被解析为:

     `(:left (:color ,default-color :width 1)
       :right (:color ,default-color :width 1)
       :top (:color ,default-color :width 1)
       :bottom (:color ,default-color :width 1))
    
  3. color 表示四个方向边框都设置为 color 颜色,会被解析为

     `(:left (:color ,color :width 1)
       :right (:color ,color :width 1)
       :top (:color ,color :width 1)
       :bottom (:color ,color :width 1))
    
  4. cons-cell 的 car部分表示亮色背景下的边框颜色,cdr部分表示暗色背景下的边框颜色。会根据当前背景色解析边框为对应的颜色,格式同上。

  5. plist 用于分别设置各个方向的边框颜色。单个方向的边框也支持上面的四种格式。例如下面是一个融合的例子:

     '(:left nil :right t :top "grey" :bottom ("#111" . "#fff"))
    

margin

文本在四个方向的 margin 值。除了上下 margin 不支持小于1的设置,其余格式同 padding 的设置。

KP算法

当限定宽度小于文本宽度时,会使用 knuth-plass 断行算法对文本进行排版,以实现美观的两端对齐的多行文本。该算法的 emacs-lisp 在这里: https://github.com/Kinneyzhang/emacs-kp 。该算法可以实现 CJK 与拉丁系语言的混合排版。

文本块渲染

使用上面的文本块模型创建 block 之后,就可以使用 etml-block-render 函数生成渲染后的字符串了。

下面是一个例子及渲染后的文本块图片:

(defvar etml-block-basic-str1
  "作为神之编辑器(Editor of the Gods),Emacs 早已超越了普通文本编辑器的范畴。它是由​​Richard Stallman​​于1976年创建的​​GNU项目​核心组件,其名字源自 Editor MACroS​​。在过去的半个世纪里,Emacs演化成了一个​​self-documenting, customizable, extensible​​的生态系统,用户可通过​​Emacs Lisp (elisp)​​ 重新定义编辑行为。M-x 是每个Emacer的魔法咒语——按下Alt(或Meta键)加x即可召唤任意命令,比如M-x butterfly这样的复活节彩蛋。中国开发者常戏称其为“​​永远的操作系统​​”,因为你可以通过org-mode管理TODO list、用magit操作Git仓库、甚至用EMMS播放MP3音乐。在Unix哲学中,Emacs坚持“一个编辑器统治所有​​”(One Editor to Rule Them All)的理念,这与VS Code等现代编辑器形成鲜明对比。C-x C-f打开文件,C-x C-s保存文档,看似复杂的组合键一旦形成​​肌肉记忆​​,效率就会呈指数级飙升。著名Python库Black的开发者曾公开表示:​\"My .emacs is my second brain.\"​")

(etml-test-pop-buffer "*test-basic-block*"
  (insert
   (etml-block-render
    (etml-block
     :content etml-block-basic-str1
     :width 35
     :border '("#aaaaaa" . "lightGreen")
     :bgcolor '("#FFF9F0" . "#222222")
     :padding '(1 . 1)
     :margin '(1 . 1)))))
 

Github 仓库

https://github.com/Kinneyzhang/ETML