问题起因
比如reStructuredText源文件里的这段文字:
除某些病毒的基因由RNA(核糖核酸)构成以外,多数生物的基因由DNA(脱氧核糖核酸)
构成,并在染色体上作线状排列。基因一词通常指染色体基因。在真核生物中,由于
染色体都在细胞核内,所以又称为核基因。位于线粒体和叶绿体等细胞器中的基因则称为
染色体外基因、核外基因或细胞质基因,也可以分别称为线粒体基因、基因质粒和
叶绿体基因。在核基因或细胞质基因中都储存着遗传信息。
Sphinx生成的HTML页面效果是这样的:
仔细看会发现一个问题,段落在原始文字换行的地方产生了一个空白(红色圈起来的地方):
为了解决这个问题,我在CSDN上找到了这篇文章:「reStructuredText/Sphinx中文段落内换行导致生成的HTML多出空格的问题」。
按照文章给出的代码我弄了个Sphinx扩展,虽然空白是去了,但是一旦遇到像这样的内容:
下面用C语言实现一个自定义的函数——计算给定两个整数的和:
.. code-block:: c
int add(int a, int b) {
return a + b;
}
输出的页面就会变成这样子了:
这种显示效果显然是有问题的——这里的 code-block
插入的代码是不能去掉换行的。
此外还有一个问题。如果遇到纯英文的段落一般会在英文单词和单词之间进行换行,这时换行去掉之后两个单词就直接连在一起了。
解决思路
原文是通过在Sphinx的生成过程中移除全部的 \r
和 \n
,从而在生成的HTML里去掉换行符号,最后令显示的时候不出现空白。
为了防止中英文混合场景下全部换行符被去除导致代码和英文排版出现问题,理想的去除换行逻辑应该是:中文字符和中文字符之间的换行符去除,其它情况下的换行符保留。
因此需要一个适应混合中英文字符组成的字符串的替换方法。考虑到英文字符宽度是一个字节,那么找到字符串里的换行符后判断左右两个字符的宽度是几个字节,如果都是大于一个字节的,去掉中间的换行符。这样做基本够用了。
代码实现
# coding=utf-8
# 文件 docs/source/replace_util.py 源码:
import os
def mix_replace(text):
new_text = text
pos = 0
while True:
pos = new_text.find(os.linesep, pos)
if pos < 0:
break
if pos - 1 < 0:
pos = pos + 1
continue
if pos + 1 > len(new_text) - 1:
break
before = new_text[pos - 1]
after = new_text[pos + 1]
if len(before.encode()) > 1 and len(after.encode()) > 1:
new_text = new_text[:pos] + new_text[(pos + 1):]
else:
pos = pos + 1
return new_text
原文的Sphinx扩展改动后:
# 文件 docs/source/remove_newline.py 源码:
import os
from docutils.nodes import NodeVisitor, TextElement, Text
from replace_util import mix_replace
def setup(app):
app.connect('doctree-resolved', process_chinese_paragraph)
class ParagraphVisitor(NodeVisitor):
def dispatch_visit(self, node):
if isinstance(node, TextElement):
for i in range(len(node.children)):
if type(node[i]) == Text:
# 下面注释掉的是原文实现方式
#node[i] = Text(node[i].astext().replace('\r', '').replace('\n', ''))
node[i] = Text(mix_replace(node[i].astext()))
def process_chinese_paragraph(app, doctree, fromdocname):
pv = ParagraphVisitor(doctree)
doctree.walk(pv)
在本地的Sphinx工程的docs
目录添加上面的两个文件后:
配置文件 docs/source/conf.py
调整,当前路径添加到模块搜索路径:
# 添加处理加载的模块路径:
import os
import sys
sys.path.insert(0, os.path.abspath('.'))
添加 remove_newline
模块到 extensions
:
extensions = [
'remove_newline'
]
完成后重新生成页面,效果: