- 图形学
- 图像处理
【免费下载链接】mupdf
mupdf mirror
MuPDF 提供了一套统一的“选项字符串(option string)”机制来定制文档搜索行为,docs/reference/common/search-options.md即是对这套搜索选项的官方说明。本文将完整继承该文档中的全部选项定义,并结合仓库内source/fitz/stext-search.c的真实实现,逐项剖析每个选项的底层原理与适用场景,同时给出可在mutool grep与 C API 中直接使用的组合示例。读完本文,你将能够精准配置 MuPDF 的文本搜索行为,处理大小写、变音符、正则、断行与连字符等真实文档中的检索难题。
搜索选项的载体:Option String
搜索选项并不是通过单独的 API 参数逐个传入,而是通过一个由 key-value 对构成的选项字符串统一描述。该字符串支持三种等价语法(详见 docs/reference/common/option-strings.md):
逗号分隔语法(经典形式):
ignore-case,keep-linesURL 查询字符串语法(以
?开头,特殊字符用%HH转义):?ignore-case=true&keep-lines=trueJSON 子集语法(单个 JSON 对象,值仅限布尔、数字、字符串与数字数组):
{"ignore-case":true,"keep-lines":true}
布尔值可以用多种等价写法表达:true / yes / on / enable / 1表示开启,false / no / off / disable / 0表示关闭;一个不带值的空选项同样视为true。这三个选项(exact、ignore-case、ignore-diacritics等)彼此之间通过逻辑或组合生效。
搜索选项总览
docs/reference/common/search-options.md定义了以下全部选项,它们在 MuPDF 中作为位掩码(bitmask)被解析,每个选项对应一位标志位:
| 选项 | 含义 | 源码标志位(见 include/mupdf/fitz/structured-text.h) |
|---|---|---|
exact | 按给定字符串原样精确匹配(默认行为) | FZ_SEARCH_EXACT = 0 |
regexp | 将搜索串解释为 JS 风格正则表达式 | FZ_SEARCH_REGEXP = 4 |
ignore-case | 忽略搜索串与页面文本的大小写差异 | FZ_SEARCH_IGNORE_CASE = 1 |
ignore-diacritics | 忽略搜索串与页面文本中的变音符(如 é 与 e)差异 | FZ_SEARCH_IGNORE_DIACRITICS = 2 |
keep-lines | 保留行尾为\n,否则行尾被转换为空格 | FZ_SEARCH_KEEP_LINES = 8 |
keep-paragraphs | 保留段落结尾为\n,否则段尾被转换为空格 | FZ_SEARCH_KEEP_PARAGRAPHS = 16 |
keep-hyphens | 保留连字符原样;默认会合并行尾的连字符断词 | FZ_SEARCH_KEEP_HYPHENS = 32 |
需要特别说明的组合规则(原文档明确给出):同时开启keep-lines与keep-paragraphs时,行以单个\n结尾、段落以\n\n结尾——这意味着你可以用正则精确匹配跨行、跨段落的文本结构。
选项解析的源码实现
在source/fitz/stext-search.c中,选项字符串首先由fz_parse_search_options()借助通用fz_new_options()解析成键值对集合,再由fz_apply_search_options()逐个匹配并置位:
const char *fz_search_options_usage = "Search options:\n" "\texact: match exact, case sensitive pattern\n" "\tignore-case: case insensitive search\n" "\tignore-diacritics: ignore character diacritics\n" "\tregexp: interpret search pattern as regular expression\n" "\tkeep-lines: preserve line breaks so pattern can match them\n" "\tkeep-paragraphs: preserve paragraph breaks so pattern can match them\n" "\tkeep-hyphens: preserve hyphens, avoiding joining lines\n"; void fz_apply_search_options(fz_context *ctx, fz_search_options *opts, fz_options *args) { fz_search_options mask = *opts; if (fz_lookup_option_yes(ctx, args, "exact")) mask |= FZ_SEARCH_EXACT; if (fz_lookup_option_yes(ctx, args, "ignore-case")) mask |= FZ_SEARCH_IGNORE_CASE; if (fz_lookup_option_yes(ctx, args, "ignore-diacritics")) mask |= FZ_SEARCH_IGNORE_DIACRITICS; if (fz_lookup_option_yes(ctx, args, "regexp")) mask |= FZ_SEARCH_REGEXP; if (fz_lookup_option_yes(ctx, args, "keep-lines")) mask |= FZ_SEARCH_KEEP_LINES; if (fz_lookup_option_yes(ctx, args, "keep-paragraphs")) mask |= FZ_SEARCH_KEEP_PARAGRAPHS; if (fz_lookup_option_yes(ctx, args, "keep-hyphens")) mask |= FZ_SEARCH_KEEP_HYPHENS; ... }解析完成后,fz_new_search()会根据选项位掩码调用init_transform_and_finder()(source/fitz/stext-search.c),把“选项”拆解为两条独立的执行路径:
- 文本变换(text transform):决定 haystack(页面文本)与 needle(搜索串)在比对前被如何归一化;
- 匹配器(finder):决定使用精确匹配还是正则匹配引擎。
精确匹配 vs 正则匹配:exact 与 regexp
exact:默认的逐字符匹配
FZ_SEARCH_EXACT的值为0,即不设置任何额外标志位时,搜索串被当作普通文本进行完全匹配。匹配器是simple_finder(source/fitz/stext-search.c),其核心match_exact()(source/fitz/stext-search.c)在 UTF-8 层面逐字符比对,且要求匹配结束后不能剩余“Marking, Nonspacing”类字符,避免命中一个不完整的组合字符序列。
regexp:JS 风格正则引擎
设置regexp后,匹配器切换为regexp_finder(source/fitz/stext-search.c)。搜索串在初始化阶段通过fz_regcomp()编译(带REG_NEWLINE标志,\n参与^/$锚点匹配),匹配过程由fz_regexec()驱动,并支持REG_NOTBOL(非行首)、REG_RUNAWAY等标志(source/fitz/stext-search.c)。注意正则引擎的语法遵循 MuPDF 自带的 JS 风格正则实现(见 source/fitz/regexp.c)。
一个典型的组合用法:regexp,keep-paragraphs可以让你写出跨段落的模式,例如匹配“以任意内容结尾的章节标题”。
归一化魔法:ignore-case 与 ignore-diacritics 的底层变换
这两个选项并非简单地“改大小写/删符号”,而是通过一组精心设计的 Unicode 变换标志组合实现的。init_transform_and_finder()依据选项组合选择对应的变换集(source/fitz/stext-search.c):
- 默认(exact):
FZ_TEXT_TRANSFORM__NORMAL= 兼容分解(NFKD)+ 连字符归一化 + 全角 ASCII 映射 + 组合(compose)。这意味着即使页面中同一字符存在不同的编码形式(例如组合序列e+ 重音符号 vs 预组合字符é),也能被当作同一个字符匹配。 ignore-case:在 NORMAL 基础上追加FZ_TEXT_TRANSFORM_UPPERCASE,即先做 Unicode 兼容分解、再统一转大写后比对,从而忽略大小写差异(source/fitz/stext-search.c)。ignore-diacritics:追加FZ_TEXT_TRANSFORM_STRIP_MARKING_NONSPACING,即把“Marking, Nonspacing”(UCDN_GENERAL_CATEGORY_MN)类字符从文本中剥离后再比对(source/fitz/stext-search.c),因此ä与a、é与e视为相同。ignore-case,ignore-diacritics同时开启:两套变换叠加(先剥离变音符、再统一大写),形成最宽松的匹配模式。
变换过程在do_transform()/transform_char()(source/fitz/stext-search.c)中执行,包含组合缓存(最多缓存 32 个字符)、按 Unicode 组合类(combining class)冒泡排序、连续空格压缩等细节。页面上任意字符都会经过同一套变换,因此命中结果能够通过索引数组精确映射回原始文本中的位置(fz_stext_position),供上层高亮定位使用。
断行与段落:keep-lines 与 keep-paragraphs
MuPDF 搜索的默认行为是:把换行、回车、制表符、行分隔符、段落分隔符以及不换行空格统一归一化为普通空格,并将连续多个空格压缩为单个空格(source/fitz/stext-search.c)。这保证了一个在 PDF 中因排版被拆成多行的词或短语,仍能作为连续文本被找到。
但当你希望正则表达式能够跨越行边界(例如匹配“以句号结尾的一行”),就需要保留这些分隔符:
keep-lines:行分隔符被保留为单个\n(源码注释明确说明“mainly for use with regexps”)。keep-paragraphs:段落分隔符被保留为单个\n,但注意与keep-lines组合时,段落边界呈现为\n\n(行间\n、段间\n\n)。
对应的变换标志为FZ_TEXT_TRANSFORM_KEEP_LINES = 256与FZ_TEXT_TRANSFORM_KEEP_PARAGRAPHS = 512(source/fitz/stext-search.c)。在transform_char()中,只有设置了这些标志时\n才不会被canon()规范化为空格(source/fitz/stext-search.c)。
实战示例:匹配文档中任意跨两行的连续短语
regexp,keep-lines模式:phrase\s+continues(\s可以命中保留的\n)。
连字符断词:keep-hyphens 的默认合并行为
纸质排版中常见“单词在行尾被连字符拆开”的情况(如hyphen-+ation)。MuPDF 的默认处理是:将行尾连字符去掉,并把断开的单词合并,从而让你能直接搜到完整的hyphenation一词。
keep-hyphens则禁用这一行为,让连字符按原样参与匹配。在底层,默认变换集里的FZ_TEXT_TRANSFORM_NORMALIZE_HYPHENS会把所有 Unicode 连字符等价物(通过fz_is_unicode_hyphen()判定,如-、‐、‑等)统一归一化为-(source/fitz/stext-search.c),而FZ_TEXT_TRANSFORM_KEEP_HYPHENS = 1024会阻止这一合并处理(source/fitz/stext-search.c)。
在真实工具中使用:mutool grep
搜索选项并非只能在 C API 中使用——命令行工具mutool grep通过-S参数直接接收搜索选项字符串。命令形式为(docs/tools/mutool-grep.md):
mutool grep [options] pattern file [ file2 ...]常用选项速览:
| 参数 | 说明 |
|---|---|
pattern file... | 要搜索的模式(正则或固定串)与目标文档 |
-F | 将 pattern 视为固定字符串(等价于关闭regexp) |
-a | 忽略变音符(等价于ignore-diacritics) |
-i | 忽略大小写(等价于ignore-case) |
-S search-options | 直接传入本文所述的搜索选项字符串 |
-O stext-options | 结构化文本提取选项(见 docs/reference/common/stext-options.md) |
-b | 从文档末尾向前搜索 |
-n/-H | 打印页码 / 文件名 |
-[ mark/-] mark | 自定义命中内容的前后标记 |
-p password | 加密文档密码 |
注意默认行为:mugrep.c中,若未指定-F,则工具会自动为options加上FZ_SEARCH_REGEXP | FZ_SEARCH_KEEP_PARAGRAPHS(source/tools/mugrep.c),也就是说 mutool grep 默认以正则模式工作、且保留段落边界。
实用命令示例:
# 跨段落正则搜索(默认行为已开启 regexp 与 keep-paragraphs) mutool grep 'chapter \d+\n\n' document.pdf # 固定字符串、忽略大小写与变音符 mutool grep -F -a -i 'naive' document.pdf # 精确组合:保留行边界以便跨行匹配 mutool grep -F -S 'keep-lines' 'state-of-the-art' document.pdf与选项字符串等价的短参数:-i等价于-S ignore-case,-a等价于-S ignore-diacritics,二者可按需混用。解析入口见 source/tools/mugrep.c 中的fz_getopt循环与fz_parse_search_options()调用。
C API 中的使用方式
对于 C/C++ 开发者,可直接使用fz_match_stext_page()系列接口并传入选项位掩码(include/mupdf/fitz/structured-text.h):
fz_search_options options = FZ_SEARCH_IGNORE_CASE | FZ_SEARCH_IGNORE_DIACRITICS; int hit_mark[MAX_HITS]; fz_quad hit_bbox[MAX_HITS]; int n = fz_match_stext_page(ctx, page, "cafe", hit_mark, hit_bbox, MAX_HITS, options);若希望从字符串形式解析,则调用:
fz_search_options options; fz_parse_search_options(ctx, &options, "ignore-case,regexp,keep-paragraphs");此外,fz_match_page()、fz_match_display_list()以及带 chapter 页码的fz_match_chapter_page_number()等封装接口(source/fitz/util.c)均接受同一套fz_search_options,因此在整篇文档、单页、display list 等不同层级的搜索中,选项语义完全一致。
选项组合速查表
| 需求 | 推荐组合 |
|---|---|
| 默认精确搜索 | (不设任何选项,即exact) |
| 不区分大小写 | ignore-case |
| 忽略重音/变音符(国际化检索) | ignore-diacritics |
| 宽松匹配(大小写 + 变音符都不敏感) | ignore-case,ignore-diacritics |
| 正则搜索 | regexp |
| 正则 + 跨行匹配 | regexp,keep-lines |
| 正则 + 跨段落匹配 | regexp,keep-paragraphs |
跨行且跨段落(行\n、段\n\n) | regexp,keep-lines,keep-paragraphs |
| 不合并行尾连字符断词 | keep-hyphens |
适用边界与注意事项
- 选项字符串语法三选一即可:逗号分隔、
?开头的 URL 查询串、JSON 子集,切勿混用(例如?a,b这种写法属于非法输入)。 exact与regexp互斥:二者是同一维度上的两种匹配模式;同时传入时以regexp生效(源码中exact的位值为 0,不会覆盖regexp位)。- 大小写/变音符归一化以 Unicode 标准为基础:变换基于 Unicode 分解与组合(TR15)与通用类别判定(
ucdn数据,见 source/fitz/ucdn.c),对拉丁字母、西欧重音字符效果显著;对某些不适用 Unicode 归一化的书写系统需自行验证。 keep-*系列主要面向正则场景:源码注释明确指出保留行/段落边界“mainly for use with regexps”;若同时开启精确匹配与keep-lines,换行会保留在比对文本中,可能使原本跨行的短语因含\n而无法命中。- 性能提示:正则模式需要对每个页面编译并执行引擎匹配(
fz_regcomp/fz_regexec),在超大文档上比精确匹配更重;可通过fz_cookie中断长时间搜索(fz_match_stext_page_with_cookie)。
总结
MuPDF 的搜索选项用一份简洁的选项字符串,覆盖了从“逐字精确匹配”到“忽略大小写与变音符的 Unicode 归一化匹配”,再到“正则跨行/跨段匹配”的完整检索需求。理解exact / regexp / ignore-case / ignore-diacritics / keep-lines / keep-paragraphs / keep-hyphens这七个选项的位掩码语义与底层变换管线(source/fitz/stext-search.c),你就能在 C API 与mutool grep中灵活组合出适合自己文档形态的检索方案。
- 图形学
- 图像处理
【免费下载链接】mupdf
mupdf mirror
相关推荐
SumatraPDF 内置 MuPDF 搜索选项(Search Options)完整指南:option string 语法、7 个选项语义与源码实现
SumatraPDF 内置 MuPDF 搜索选项(Search Options)完整指南:option string 语法、7 个选项语义与源码实现 本指南以仓
桌面应用文档SumatraPDF 内置 MuPDF 的 Document Writer Options 完全指南:从光栅输出到打印与矢量格式的选项字符串详解
SumatraPDF 内置 MuPDF 的 Document Writer Options 完全指南:从光栅输出到打印与矢量格式的选项字符串详解 导读 本文以仓
桌面应用文档三步上手 Video2X:AI 视频超分辨率放大与帧插值快速指南
三步上手 Video2X:AI 视频超分辨率放大与帧插值快速指南 Video2X 是一个基于机器学习的开源视频超分辨率与帧插值框架。把 240P 素材放大到 4
音视频视频处理图像处理深度学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考