datatables 行分组信息展开与折叠的功能实现_[LaTeX 尝试] fancyvrb - 修复行引用的超链接跳转位置
本文已加入专栏文章目录,归入「进阶使用」文章系列。
本文可以看作对这个发生于 2019 年 7 月中旬的 TeX-SX 上自问自答的展开说明。那个回答中避免了 python 的使用,而是利用 zref
宏包把位置信息以文本形式在 pdf 中呈现,好处是不用引入 python,坏处是如果写成文章,需要额外介绍 zref
的使用。
问题的引入
fancyvrb
宏包提供了高度可配置的抄录环境,功能大致上和 listings
相当。
有些配置项提供了「跳出抄录环境,回到一般 latex」的功能,例如 commandchars
。它接受一串三个符号组成的值,分别代表命令开始、左侧分组、右侧分组(实际可以归结到 catcode,此处略过)。
直接看 fancyvrb
文档 Sec. 4.1.12 的例子
文档截图中的第二个例子展示了一种使用方式,利用 commandchars
为抄录环境的某一行增加标签(label
),然后在正文中引用(ref
)它来获得行号。特别地,hyperref
包还会自动为行号添加超链接,点击行号就能跳转到抄录环境中的对应行。
上一段的最后一个分句,只是描述了我们期望的行为,实际的编译和测试结果不是这样的,点击引用(ref
)得到的行号后,无法跳转到对应行。
工具和示例准备
除了靠手去点超链接,然后根据阅读器跳转的位置来判断和分析,还可以借助工具直接读取 PDF 文件里的超链接跳转位置。例如,使用 Python 的 PyPDF2 库,
from PyPDF2 import PdfFileReaderfname = 'xxx.pdf'
pdf = PdfFileReader(fname)
named_dests = pdf.namedDestinations.items()print('Coordinates of named destinations')
for k, v in named_dests:print(k, [v['/Left'], v['/Top']])top = None
print('nVertical distances between labels of line numbers')
for k, v in named_dests:if 'FancyVerbLine' in k or 'lstnumber' in k:curr = v['/Top']if top is not None:print(k, float(top - curr) / 72 * 72.27, 'pt')top = curr
有关 PDF 格式的补充说明:
- 「超链接跳转位置」在 PDF 格式中称为 named destination
- 每个 named destination 拥有一个全文档唯一的名称
- 它的内容,在本文中我们关心的是横纵坐标信息,有时也关心它的目标页面
- 它的使用,是成为某个 annotation(例如
hyperref
自动添加的)的跳转目标
有关上述 python 脚本的说明:
- 第一组
print
,输出文档内所有 named destinations 的名称和坐标 - 第二组
print
,仅输出与fancyvrb
(和listings
,用于对照) 有关的相邻 named destinations 的纵坐标差值
同时,使用以下 latex 示例文档
(注意,示例中的 newpagenull
是特意添加的,为的是保证 pdf 阅读器有跳转,也就是把第一页往上翻,的空间)
documentclass{article}
usepackage{fancyvrb}
usepackage{hyperref}% <possible config appears here>begin{document}
begin{Verbatim}[numbers=left, commandchars={}]
firstlabel{vrb:1}
secondlabel{vrb:2}
thirdlabel{vrb:3}
forthlabel{vrb:4}
fifthlabel{vrb:5}
sixthlabel{vrb:6}
aend{Verbatim}ref{vrb:1}, ref{vrb:2}, ref{vrb:3}, ref{vrb:4}, ref{vrb:5}, and ref{vrb:6}
newpagenull
end{document}
最后,需要留意示例文档的编译方式
如果使用 xelatex
,因为默认情况下 xdvipdfmx
会去掉未使用的 named destinations,并简化所有 named destinations 的名称,所以需要通过选项让 xdvipdfmx
不对 named destinations 自动优化。
xelatex -no-pdf xxx
xelatex -no-pdf xxx
xdvipdfmx -C 0x0010 xxx
如果使用 pdflatex
或 lualatex
,直接使用即可。
不同引擎生成的 pdf 中,named destination 的信息有微小差异。本文默认使用 xelatex
。
初步尝试
编译 latex 示例文档生成 pdf,点击那六个超链接,可以发现它们都跳转到同一位置。
执行 python 脚本读取这个 pdf 里的信息,会获得如下输出
Coordinates of named destinations
Doc-Start [133.77, 667.2]
page.1 [132.77, 705.06]
page.2 [132.77, 705.06]Vertical distances between labels of line numbers
似乎六个 label
根本没有生成六个不同的跳转目标,连一个也没有生成。如果直接使用 xelatex xxx.tex
,生成的 pdf 里就只有一条记录
Coordinates of named destinations
0 [133.77, 667.2]
如果继续使用 PyPDF2 的功能去看第一页的所有 annotations 的跳转目标(此处略去代码),就可以完全确定:六个 label
完全没有生成新跳转目标,六个 ref
都跳转去了当前页的开始处(具体位置是 page.1
跳转目标标记的、页面版心的左上角)。
以上是从 pdf 一侧进行的分析和探索。如果从 latex 一侧进行,从相关宏包的源码入手,则能了解到以下事实:
- 在
fancyvrb
内部负责递增行号的宏FV@refstepcounter
的定义中,重写了一遍 latex2e 中refstepcounter
的原始定义,刻意避免了直接使用refstepcounter
hyperref
重定义后的refstepcounter
会在展开时插入新的跳转目的地, 并把该目的地储存在@currentHref
中以供label
在内部引用(这则「事实」的展开介绍,可能需要额外的一篇或多篇文章,此处略过)
这样,因为fancyvrb
在递增行号时没有使用 refstepcounter
,所以对应于新行号的跳转位置无法生成,@currentHref
得不到更新,label
关联的就变成了上一次更新过的 @currentHref
信息,也即 hyperref
在每一页开头默认插入的跳转目标。
第一步尝试很简单,让 FV@refstepcounter
成为 refstepcounter
letFV@refstepcounterrefstepcounter
继续尝试
修改保存、编译 tex 文件、执行 python,会发现问题没有完全解决。
Coordinates of named destinations
Doc-Start [133.77, 667.2]
FancyVerbLine.1 [133.77, 667.2]
FancyVerbLine.2 [133.77, 657.18]
FancyVerbLine.3 [133.77, 657.18]
FancyVerbLine.4 [133.77, 645.22]
FancyVerbLine.5 [133.77, 633.22]
FancyVerbLine.6 [133.77, 621.31]
page.1 [132.77, 705.06]
page.2 [132.77, 705.06]Vertical distances between labels of line numbers
FancyVerbLine.2 10.057574999999998 pt
FancyVerbLine.3 0.0 pt
FancyVerbLine.4 12.004850000000001 pt
FancyVerbLine.5 12.044999999999998 pt
FancyVerbLine.6 11.954662499999998 pt
从 python 脚本的输出可以看出,虽然现在每个 label
都对应了不同的跳转目标,但是目标之间的纵坐标差异并不一致。
- 预期输出是,每两个相邻目标,在纵坐标上都相差 12pt(对应 latex 中
baselineskip
储存的值,也即行距) - 实际得到的是,
- line 2 和 line 1 只差了 10pt(与字号有关,与行距无关,例如用
fontsize{10}{50}selectfont
修改行距后仍然是 10pt), - line 3 和 line 2 差 0pt,
- 后面的正常。
- line 2 和 line 1 只差了 10pt(与字号有关,与行距无关,例如用
推断,FV@refstepcounter
展开的位置有问题。
根据对类似示例代码的手动展开(见项目 muzimuzhi/latex-expansion 中以 fancyvrb 打头的文件),判断纵坐标差异应该源于 fancyvrb
对抄录环境前三行的特殊处理(可能是为了控制在环境中间换页的条件)具体涉及命令 FV@ListProcessLine@(i|ii|iii|iv)
。这几个宏的具体作用,限于时间和水平笔者还没能了解清楚。
笔者采取了一个讨巧(但可能带来其他未知问题)的解决方案:把 FV@refstepcounter
(具体是调用它的 FV@StepLineNo
宏 )的展开位置延迟到抄录行文本刚要输出之前,以保证通过 refstepcounter
递增行号并插入新跳转目标时,所处高度和抄录文本行一致。
这样,要做的修改就很简单:把 FV@StepLineNo
从原来的位置删掉,再在一个新的位置插入。
usepackage{etoolbox}% move FV@StepLineNo into FV@ListProcessLine
patchcmdFV@@PreProcessLine{FV@StepLineNo}{}{}{fail}patchcmdFV@ListProcessLine{kernleftmargin}{FV@StepLineNokernleftmargin}{}{fail}
从 pdf 阅读器里的点击跳转效果,和 python 脚本的输出看,问题似乎修好了。
其他
- 包含修改代码的 tex 文档,见项目 muzimuzhi/latex-examples 中的文件 fancyvrb-improvements.tex。文件中还包含修改行号引用风格的代码,会在后续文章里介绍。
- 最困难的部分可能是定位问题和知道可以把
FV@StepLineNo
挪到哪,笔者主要是通过手动展开来探索的。 fancyvrb
被其他一些宏包依赖,依赖关系比较深的是tcolorbox -> minted -> fvextra -> fancyvrb
,文中介绍的尝试,并未经过充分测试。
datatables 行分组信息展开与折叠的功能实现_[LaTeX 尝试] fancyvrb - 修复行引用的超链接跳转位置相关推荐
- uni-app - 文本展开 / 收起折叠功能,支持自定义样式(当文本内容超出规定行数后,展开收起折叠的功能)兼容 H5 / App / 小程序且易用更容易修改的插件组件源码,超详细的示例代码及注释
前言 网上的组件和教程代码都太乱了,根本无法按照自己的需求修改,而且基本上都有兼容性和功能性 BUG. 本文实现了 多行文本展开与折叠组件,灵活性非常高,只完成了核心功能,可随意自定义样式满足您的需求 ...
- JS点击进行展开和折叠的功能代码
最近开发网站需要用到 一些网页前台特效.需要打开网页时部分内容默认隐藏(折叠),然后点击相应的文字或图片进行显示(展开),再点击进行隐藏(折叠).网上查了资料,类似的代码很多,以下是三种可以实现此功能 ...
- Saiku设置展示table数据不隐藏空的行数据信息(二十六)
Saiku设置展示table数据不隐藏空的行数据信息 saiku有个 非空的字段 按钮,点击这个后,会自动的把空的行数据信息给隐藏掉,这里我们来设置一下让其行数据不隐藏,为空的就为空. 主要更改两个文 ...
- Java 报表Apache POI API与实现数据行分组折叠
官方地址:http://poi.apache.org/apidocs/ Apache POI - Javadocs Apache POI Javadocs 可以在此处在线访问 Apache POI的最 ...
- Vue - Element el-table 行的展开与折叠
GitHub Demo 地址 在线预览 效果图 方式一效果图 方式二效果图 要点 使用el-table的span-method方法配合css样式实现展开与折叠 方式一代码 <template&g ...
- Java折叠_[Java教程]Jquery中菜单的展开和折叠
[Java教程]Jquery中菜单的展开和折叠 0 2018-08-15 16:03:38 标签内容 您好:alee 宿舍管理员 密码管理 修改密码 宿舍管理 学生宿舍查询 学生宿舍新增 学生宿舍分配 ...
- 表格分组标签:表格行分组中的隐藏功能
在程序员的认知中,表格中存在行分组标签,也就是thead,tbody,tfoot三个行分组标签.也许你会认为我在这里还是为大家继续讲解thead,tbody,tfoot三个标签,那就大错特错了 今天除 ...
- POI设置和读取excel分组信息,多级分组设置
一.设置分组信息 1.一级分组信息设置 sheet.groupRow(1, 2); sheet.groupColumn(1, 2); 2.多级分组信息设置 poi中提供的方法是没有级别这个参数,通过查 ...
- 热图3:热图行列分组信息注释
这次我们要让热图更加复杂化. 实际上,有些情况下做热图可能只需要标注对照组.实验组即可.但是大多时候,可以在热图上体现更多信息,比如,除了分组,还有不同得处理.年龄.性别.疾病阶段等等,以及不同功能基 ...
最新文章
- python导入包库的两种语句import 和 from … import
- Content-type的说明即HTTP请求头的类型整理
- JAVAEE框架之Spring AOP
- 好程序员技术文档HTML5开发中的javascript闭包
- python list 实现原理,Python 列表(List)的底层实现原理分析
- 中专生计算机教案,[定稿]计算机基础教案中专V8.1(全文完整版)
- 月薪20k的web前端开发程序员,他们都会的这6招
- 通过chrome console 快速获取网页连接
- 利用3D转换实现旋转木马
- 离散数学计算机科学与技术答案,湘潭大学计算机科学与技术刘任任版离散数学课后习题答案---第二学期--图论与组合数学...
- 知行功夫为本,找对心中的英雄,过好平凡的生活
- SUBMAIL群发邮件API接口-Mail/send
- 不是HR,Leader你会面试应聘者吗(如何起好手中的扑克牌)
- 根据拼音首字母进行过滤的combobox
- 中文分词之维特比算法详解
- win10 64位系统 打开光盘出现339错误 缺少mscomctl.ocx组件 解决方案
- python实例豆瓣代码_Python实例:通过selenium模拟登陆豆瓣
- 怎么有的帖子发不了啊
- 【绝对管用】彻底的卸载干净oracle 11g
- Python之base64加密解密
热门文章
- python3-pandas 缺失数据的处理
- python3-numpy IO load()、save()、savez()、loadtxt()、savetxt()、tofile()、fromfile()
- mysql 体重 类型 身高_体重较轻,身高较高的身材怎样挑选单板?
- 造大专计算机学历,广昌县职业技术学校计算机应用专业助您 掌握一技之长获大专学历...
- C语言 数组排序 – 插入法排序 - C语言零基础入门教程
- android懒加载单实例,【 Android 10 设计模式 】系列 -- 单例
- seata执行闪退_Seata 1.2.0的配置以及踩坑记录
- python构建字典实现英文大写字母与ascii编码的转换_Python:将复杂的字符串字典从Unicode转换为ASCII...
- PHP即将退出,PHP4即将退出历史舞台
- 1个显示器分割2画面_我家房子100㎡,原始设计有2个卫生间,纠结保留1个还是2个...