记录一下实现 .md \ .pdf \ .docx
文件的预览。
markdown
文件预览安装 markdown-it
$> npm install --save markdown-it
解析.md
文件转换为 html,然后添加到 dom 节点中。
import MarkdownIt from "markdown-it";
function parseFile(fileUrl) {
// 创建实例
const md = new MarkdownIt({
html: true, // Enable HTML tags in source
xhtmlOut: false, // Use '/' to close single tags (<br />).
// This is only for full CommonMark compatibility.
breaks: false, // Convert '\n' in paragraphs into <br>
langPrefix: "language-", // CSS language prefix for fenced blocks. Can be
// useful for external highlighters.
linkify: false, // Autoconvert URL-like text to links
// Enable some language-neutral replacement + quotes beautification
// For the full list of replacements, see https://github.com/markdown-it/markdown-it/blob/master/lib/rules_core/replacements.js
typographer: false,
// Double + single quotes replacement pairs, when typographer enabled,
// and smartquotes on. Could be either a String or an Array.
//
// For example, you can use '«»„“' for Russian, '„“‚‘' for German,
// and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
quotes: "“”‘’",
});
// 解析文件
const html = md.render(fileUrl);
return html;
}
拿到转换后的 html,插入到 dom 节点中,这里给一个 vue 的示例
<template>
<div class="md-preview">
<div class="content">
<div className="markdown-content" v-html="content" />
</div>
</div>
</template>
<script>
import { onMounted, ref, computed } from "vue";
// md
import ReadME from "@/README.md?raw";
// 解析后的内容变量
const content = ref("");
onMounted(() => {
//
content = parseFile(ReadME);
});
</script>
highlight.js
代码高亮解析出来的内容没有任何样式,通常我们的代码使用高亮,方便阅读。
安装
$> npm install highlight.js
通过属性highlight
定义需要处理的代码片段
const md = new MarkdownIt({
// ...
highlight: (str, lang) => {
if (lang && hljs.getLanguage(lang)) {
try {
return (
'<pre class="hljs"><code>' +
hljs.highlight(str, {
language: lang,
ignoreIllegals: true,
}).value +
"</code></pre>"
);
} catch (__) {}
}
return (
'<pre class="hljs"><code>' + md.utils.escapeHtml(str) + "</code></pre>"
);
},
});
此时只加了基本的样式,可以通过导入不同主题的.css
文件,来应用样式。主题文件目录highlight.js/styles/**
在顶部导入样式文件,
import "highlight.js/styles/atom-one-dark.css";
markdown-it-anchor
增加导航标识通过标题导航到指定的文字,通常#
会被转换为h
. 通过配置增加永久性导航设置
安装
@sindresorhus/slugify
用来处理获取到的文本内容,有时候我们的标题中会含有一些特殊字符。能很好的处理
$> npm install markdown-it-anchor @sindresorhus/slugify --save
MarkdownIt
通过 use 方法安装插件,
import MarkdownItAnchor from "markdown-it-anchor";
import slugify from "@sindresorhus/slugify";
const md = new MarkdownIt({
// ...
});
md.use(MarkdownItAnchor, {
level: 1,
slugify: (s) => {
return slugify(s);
},
getTokensText(tokens) {
return tokens
.filter((t) => ["text", "code_inline"].includes(t.type))
.map((t) => t.content)
.join("");
},
});
可以看到转换后的 html 内容中,所有的标题都包含有 id 属性,通过permalink
生成永久性导航链接,
md.use(MarkdownItAnchor, {
// ...
permalink: MarkdownItAnchor.permalink.headerLink(),
});
可以在生成的 html 中看到h
标题中有<a class="header-anchor" href="#title"></a>
node-html-parser
生成简单 dom 结构,支持 query 节点在转换后的 html 中已经包含了可导航的标题信息;再从中提取出标题数据,即可自定义渲染导航菜单。
$> npm install node-html-parser --save
以 vue 代码示例
import { parse } from "node-html-parser";
// 通过计算属性,等待md文件解析完后生成对应的导航数据
const navData = computed(() => {
const root = parse(content.value);
const match = [];
for (const h of root.querySelectorAll("h1, h2, h3, h4, h5, h6")) {
const slug = h.getAttribute("id") || slugify(h.textContent);
h.setAttribute("id", slug);
// h.innerHTML = `<a href="#${slug}>${h.innerHTML}</a>`
match.push(`<a href="#${slug}" class="${h.rawTagName}">${h.innerHTML}</a>`);
}
return match.join("");
});
pdf
文件预览加载 pdf 文件,进行预览。
安装
$> npm install pdfjs-dist --save
vue3 中没有 require,所以安装其他依赖
# 支持es module
$> npm install @bundled-es-modules/pdfjs-dist
通过读取.pdf
文件内容,绘制到 canvas 中,在家动态生成的 canvas 节点添加到指定区域。
import pdfjs from "@bundled-es-modules/pdfjs-dist/build/pdf";
// import viewer from '@bundled-es-modules/pdfjs-dist/web/pdf_viewer';
import worker from "@bundled-es-modules/pdfjs-dist/build/pdf.worker.js?url";
pdfjs.GlobalWorkerOptions.workerSrc = worker;
function parsePdf(fileUrl) {
const self = this;
// 创建实例,加载文件
const loadingTask = pdfjs.getDocument(fileUrl);
// container box ref 引用
const container = this.$refs.pdf;
// 通过promise读取
loadingTask.promise.then(function (doc) {
const numPages = doc.numPages;
// meta 数据信息
doc.getMetadata().then(function (data) {
// 元数据信息
});
const loadPage = function (pageNum) {
doc.getPage(pageNum).then(function (page) {
const scale = 1;
const viewport = page.getViewport({ scale });
const outputScale = window.devicePixelRatio || 1;
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
// contianer 为预览所在的dom区域
container.appendChild(canvas);
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = Math.floor(viewport.width) + "px";
canvas.style.height = Math.floor(viewport.height) + "px";
const transform =
outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
const renderContext = {
canvasContext: context,
transform,
viewport,
};
page.render(renderContext);
});
};
for (let i = 1; i <= numPages; i++) {
// 加载读取每一页数据
loadPage(i);
}
});
}
那对应的 vue 示例,
<template>
<div class="pdf-preview">
<div ref="pdf" />
</div>
</template>
docx
文件预览加载 docx 文件数据流转换为 html,然后插入 dom 节点中
安装
$> npm install mammoth --save
浏览器使用引用的是mammoth/mammoth.browser
,需要的数据格式必须是ArrayBuffer
示例是加载本地项目总的资源,通过网络请求加在资源为数据流blob
,然后通过FileReader
转换为 ArrayBuffer
此处以 vue 示例
<template>
<div class="docx-preview">
<div ref="docx" v-html="content" />
</div>
</template>
<script setup>
import mammoth from 'mammoth/mammoth.browser'
import { ref } from 'vue'
const conent = ref('')
function parseDocs(fileUrl){
let $this = this
// raadfile
const blob = await this.$http.request({
type: 'get',
url: fileUrl,
responseType: 'blob',
})
// blob 转 arrayBuffer
const fileReader = new FileReader()
fileReader.onload = function (res) {
// 解析转换
mammoth
.convertToHtml({ arrayBuffer: fileReader.result })
.then(function (result) {
const html = result.value // The generated HTML
$this.content = html
})
.done()
}
fileReader.readAsArrayBuffer(blob)
}
<script>
.docx
只解析为 html,没有任何样式,需要自己去设置样式。所以说如果就只是预览,可以让后端转为 pdf 后前端在展示预览。
具体实例代码可参考示例项目:
vite+vue3 模板代码仓库(vite-vue3)[https://gitee.com/ngd_b/vue3-vite]