/document-previewer

南京大学 计算机科学与技术系 高级程序设计 课程项目

Primary LanguageC++

Project1

前置知识

  • C++
  • Qt

Markdown to HTML

Markdown 基本语法 | Markdown 指南中文版

试图语法简化:

  • 不检测换行
  • 列表中不允许添加另一种元素
  • 块引用中可以包含其他元素

后来觉得基本语法中有很多没有 well-defined 的地方,遂采用 CommonMark Spec

在虚拟机上研究一下其 C 的实现,似乎是 PA 级别的复杂度,但是一时半会儿看不懂

可视化:Markdown Tutorial

语法实现:

  • 标题:在一行的起始位置连用若干个 ## 和文本之间有一个空格
  • 代码:用额外的至少四个空格宽的缩进表示一个代码块,或使用一对 backquote 包裹一个代码块

backquote×3 后不可以加上代码类型

  • 段落与换行:使用一个空行来开启新的段落,在一行的末尾使用恰好两个空格来换行,其处理具有惰性
  • 引用:前瞻为 > 和一个空格,可以内嵌标题、段落、引用,暂不允许内嵌代码,其处理具有惰性(基于段落的惰性)

别忘了内联引用,其处理与链接和图片类似

  • 强调:只使用 *,富文本包括标题、段落,不包括代码

这里并没有完全遵循 CommonMark Spec 的语法,个人的处理是 * 的个数从三开始,进行最近匹配

  • 链接和图片:个人的处理是在内部,链接的文本部分视为富文本;在外部,链接或图片视为一个整体(未实现)

使用正则表达式,惰性匹配

  • 无序列表:

类似引用,但是行为有略微不同,当出现了前瞻字符时,列表总是试图创建一个新的段落,可以考虑下面的例子:

 - 123
 - 
 - 123
 
 > 123
 > 
 > 123

对于嵌套列表的惰性,则与引用类似:

- 123
- - 123
123
123
- - 123
  • 有序列表:简化为仅以 1. 为前瞻字符
  • HTML 标签:使用 QPlainTextEdit 似乎可以简单实现部分功能,但是外层有一对段落标签

Laziness

引用(段落)的惰性:最后一行会被解析成三层引用,与 CommonMark Spec 的语法不一致

> 123
> > 123
123
123
> > 123

中文编码

思考:中文编码后的特殊字符会不会干扰 scan 和 parse?

加上 <meta charset="UTF-8">

Rendering and displaying HTML pages

Qt WebEngine Widgets C++ Classes

然而 MinGW 并不支持 QtWebEngine,所以只能使用 QAxWidget

Release

首先以 release 方式编译生成 .exe 程序,然后使用 D:\Qt\5.15.2\mingw81_64\bin\windeployqt 进行 Deployment

然而 .exe.dll 不在一个文件夹仍然无法运行

Project2

Word

Based on Microsoft Word 2019

Open XML

欢迎使用 Open XML SDK 2.5 for Office | Microsoft Docs

最简单的 WordprocessingML 文档的主文档文章包括以下 XML 元素:

  • document - WordprocessingML 的主文档部件的根元素,用于定义主文档文章
  • body - 包含主文章的块级别结构集合的容器
  • p - 一个段落
  • r - 一段连续文本
  • t - 一个文本范围

Word 文件的 OpenXML 解析(以 Python3 为例)

Word OpenXML 常用标签

总体思路

  1. 设计文档数据结构
  2. 解析文档
  3. 写入 HTML

CSS

一些可能的解决方案:

Handling Document Formats - Qt Wiki

Handling Microsoft Word file format - Qt Wiki

  • 第三方库 DuckX

DuckX: C++ library for creating and updating Microsoft Word (.docx) files

只能实现简单的文字编辑,不支持格式

CMake 入门实战

  • 调用 Python,Python 使用第三方库

TODO

  • Qt QAxWidget

Microsoft.Office.Interop.Word

目前只能逐行读取,且无法获取 Style 信息

class WordEngine

  • unzip the Open XML file and process the XML file directly

docx -> xml

QXmlStreamReader: 线性

QDomDocument: 树形 qt += xml

最终选择这个作为解决方案

预览

  • 粗体、斜体、字体、字号、字体颜色、突出显示颜色(颜色需要注意不同的格式)

RunProperties

RunFonts

  • 段落对齐方式、段前后间距、行距(注意计算)、左右侧缩进、首行缩进(尚不知如何精确计算)

ParagraphProperties

添加功能,如 rPr 或 pPr,修改几处地方:

  • ParagraphProperty 或 RunProperty 的类定义和构造函数
  • domParagraphProperty 或 domRunProperty 的解析
  • format
  • paragraphPropertyHierarchy 或 runPropertyHierarchy
  • 页边距、页面大小

SectionProperties

  • 样式(default、h1-6、title、subtitle)

ParagraphStyleId

若得到的样式不在 styleIDSet 中,可考虑 assert(0) 处理

或者动态变化 styleIDSet(先解析 document,后解析 style)

style hierarchy:

  • Document defaults
  • Table styles
  • Numbering styles
  • Paragraph styles
  • Character styles
  • Direct Formatting

XML 结构

classDiagram
      document <|-- body
      document <|-- styles
      document <|-- relationships
      
      body <|-- paragraphs
      body <|-- sectionProperty
      
      paragraphs <|-- runs
      paragraphs <|-- paragraphProperty
      
      runs <|-- text
      runs <|-- runProperty
      
      paragraphProperty <|-- runProperty
      paragraphProperty <|-- pStyle
      runProperty <|-- rStyle
      
      paragraphs <|-- hyperlink
      hyperlink <|-- runs
      
      body <|-- table
      table <|-- tableProperty
      table <|-- tableGrid
      table <|-- tableRow
      tableRow <|-- tableCol
      tableRow <|-- tableRowProperty
      tableCol <|-- tableColProperty
      tableCol <|-- paragraphs

暂不考虑:

  • paragraphProperty <|-- runProperty
  • runProperty <|-- rStyle

更多功能

  • 超链接

一种编码为:

<w:p w:rsidP="00166FC2" w:rsidRDefault="00530648" w:rsidRPr="00166FC2" w:rsidR="00B13A56" w14:textId="1D9791CA" w14:paraId="773E792D">
    <w:r>
    	<w:fldChar w:fldCharType="begin"/>
    </w:r>
    <w:r>
    	<w:instrText xml:space="preserve"> HYPERLINK "https://cn.bing.com/" </w:instrText>
    </w:r>
    <w:r>
    	<w:fldChar w:fldCharType="separate"/>
    </w:r>
    <w:r w:rsidRPr="00166FC2" w:rsidR="00EE3A1D">
        <w:rPr>
        	<w:rStyle w:val="ad"/>
        </w:rPr>
        <w:t>必应</w:t>
    </w:r>
    <w:r>
        <w:rPr>
        	<w:rStyle w:val="ad"/>
        </w:rPr>
        <w:fldChar w:fldCharType="end"/>
    </w:r>
</w:p>

另一种编码为:

<w:p w:rsidP="00166FC2" w:rsidRDefault="00902AE8" w:rsidRPr="00166FC2" w:rsidR="00B13A56" w14:textId="30777BC3" w14:paraId="773E792D">
    <w:bookmarkStart w:name="_GoBack" w:id="0"/>
    <w:bookmarkEnd w:id="0"/>
    <w:r>
        <w:rPr>
            <w:rFonts w:hint="eastAsia"/>
        </w:rPr>
        <w:t>必应</w:t>
    </w:r>
    <w:hyperlink r:id="rId7" w:history="1">
        <w:r w:rsidRPr="00166FC2" w:rsidR="00EE3A1D">
            <w:rPr>
                <w:rStyle w:val="ad"/>
            </w:rPr>
            <w:t>必应</w:t>
        </w:r>
    </w:hyperlink>
    <w:r>
        <w:rPr>
            <w:rFonts w:hint="eastAsia"/>
        </w:rPr>
        <w:t>必应</w:t>
    </w:r>
</w:p>

目前只考虑第二种编码

处理思路为:

  1. 不考虑 rStyle
  2. domParagraphInfo 中对 w:hyperlink 进行特判,记录 ID,并在其所有的 runs 结点的 rPr 中添加 ID 信息(多个 runs 会导致 vis 后的颜色变化不统一,因而设置颜色不变)
  3. 得到 ID 到超链接的映射表,在解析时使用
  • 图片

w:drawing

有点难,放弃了

  • 表格

Table

为简单起见,只保留属性 td widthtr height,其余均为预设:

<style>
	table, table tr th, table tr td { border: 1px solid #000000; }
	table { border-collapse: collapse; }
</style>

利用 C++17 中的 std::variant 修改数据结构:

vector<variant<shared_ptr<ParagraphInfo>, shared_ptr<Table>>>

参考实现

Document.SaveAs2 方法 (Word) | Microsoft Docs

TODO

  • 测试:空行;整合测试文档
  • 编辑功能:目前只能使用已有格式,而不能修改格式,待 Project3 处理

example: textedit

或者研究一下 QTextDocument 类

解压

stachenov/quazip

Latex

语法入门

一份其实很短的 LaTeX 入门文档

总体思路

使用 pandoc 将 Latex 转换为 Word 或 HTML

Qt -> QProcess

前端参考

Amshaegar/PandoQ: Qt GUI frontend for Pandoc

TODO

  • 调用 pandoc 时的异常处理(根据对应文件是否生成来判断,也许有点不妥)
  • 样式主题
  • 导言区中定义的标题、作者、日期
  • 图片链接后缀名(源文件中显式加上,就不需要 Fix 了)
  • 目录
  • 编辑功能:使用自定义 QDialog,待 Project3 处理

样式主题

textedit 默认生成的 HTML 如下:

<style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style>

由于在编辑阶段,textedit 无法显示样式的变化,于是决定在预览阶段显示

使用自定义 QDialog (QWidget 和 MainWindow 似乎很难进行阻塞) 选择主题

从 Typora 主题文件夹里面了几个过来

使用 Qt Resource System 保存 css 文件

目前只支持 Latex,可以考虑 Markdown 适配,然而 Word 显然不能

Project3

实际上 QFileDialog 类已经实现了大部分功能……

还是自己写一个吧……

基本要求

  • 使用 QTreeWidget 类展示搜索界面
  • 通过 QDir 类的 entryList 方法过滤文件

功能扩展

  • 修改日期、大小
  • 处理逻辑优化
  • UI 界面美化