Skip to content

Commit

Permalink
修复阅读器渲染书本内容冲突的bug
Browse files Browse the repository at this point in the history
  • Loading branch information
buzhfianji committed Jul 8, 2024
1 parent 2e67ff0 commit 8c118f1
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 70 deletions.
86 changes: 45 additions & 41 deletions src/renderer/src/reader/epub.js
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,49 @@ class Loader {
if (!item) return href
return this.loadItem(item, parents.concat(base))
}

async repalceDocSource(doc, item, parents = []) {
const { href, mediaType } = item
if ([MIME.XHTML, MIME.HTML, MIME.SVG].includes(mediaType)) {
// change to HTML if it's not valid XHTML
if (mediaType === MIME.XHTML && doc.querySelector('parsererror')) {
console.warn(doc.querySelector('parsererror').innerText)
item.mediaType = MIME.HTML
doc = new DOMParser().parseFromString(str, item.mediaType)
}
// replace hrefs in XML processing instructions
// this is mainly for SVGs that use xml-stylesheet
if ([MIME.XHTML, MIME.SVG].includes(item.mediaType)) {
let child = doc.firstChild
while (child instanceof ProcessingInstruction) {
if (child.data) {
const replacedData = await replaceSeries(
child.data,
/(?:^|\s*)(href\s*=\s*['"])([^'"]*)(['"])/i,
(_, p1, p2, p3) => this.loadHref(p2, href, parents).then((p2) => `${p1}${p2}${p3}`)
)
child.replaceWith(doc.createProcessingInstruction(child.target, replacedData))
}
child = child.nextSibling
}
}
// replace hrefs (excluding anchors)
// TODO: srcset?
const replace = async (el, attr) =>
el.setAttribute(attr, await this.loadHref(el.getAttribute(attr), href, parents))
for (const el of doc.querySelectorAll('link[href]')) await replace(el, 'href')
for (const el of doc.querySelectorAll('[src]')) await replace(el, 'src')
for (const el of doc.querySelectorAll('[poster]')) await replace(el, 'poster')
for (const el of doc.querySelectorAll('object[data]')) await replace(el, 'data')
for (const el of doc.querySelectorAll('[*|href]:not([href]'))
el.setAttributeNS(
NS.XLINK,
'href',
await this.loadHref(el.getAttributeNS(NS.XLINK, 'href'), href, parents)
)
}
}

async loadReplaced(item, parents = []) {
const { href, mediaType } = item
const parent = parents.at(-1)
Expand Down Expand Up @@ -990,49 +1033,10 @@ ${doc.querySelector('parsererror').innerText}`)
}
async loadDocument(item, parents = []) {
const str = await this.loadText(item.href)
const { href, mediaType } = item
const doc = this.parser.parseFromString(str, item.mediaType)

// parse and replace in HTML
if ([MIME.XHTML, MIME.HTML, MIME.SVG].includes(mediaType)) {
// change to HTML if it's not valid XHTML
if (mediaType === MIME.XHTML && doc.querySelector('parsererror')) {
console.warn(doc.querySelector('parsererror').innerText)
item.mediaType = MIME.HTML
doc = new DOMParser().parseFromString(str, item.mediaType)
}
// replace hrefs in XML processing instructions
// this is mainly for SVGs that use xml-stylesheet
if ([MIME.XHTML, MIME.SVG].includes(item.mediaType)) {
let child = doc.firstChild
while (child instanceof ProcessingInstruction) {
if (child.data) {
const replacedData = await replaceSeries(
child.data,
/(?:^|\s*)(href\s*=\s*['"])([^'"]*)(['"])/i,
(_, p1, p2, p3) => this.loadHref(p2, href, parents).then((p2) => `${p1}${p2}${p3}`)
)
child.replaceWith(doc.createProcessingInstruction(child.target, replacedData))
}
child = child.nextSibling
}
}
// replace hrefs (excluding anchors)
// TODO: srcset?
const replace = async (el, attr) =>
el.setAttribute(attr, await this.loadHref(el.getAttribute(attr), href, parents))
for (const el of doc.querySelectorAll('link[href]')) await replace(el, 'href')
for (const el of doc.querySelectorAll('[src]')) await replace(el, 'src')
for (const el of doc.querySelectorAll('[poster]')) await replace(el, 'poster')
for (const el of doc.querySelectorAll('object[data]')) await replace(el, 'data')
for (const el of doc.querySelectorAll('[*|href]:not([href]'))
el.setAttributeNS(
NS.XLINK,
'href',
await this.loadHref(el.getAttributeNS(NS.XLINK, 'href'), href, parents)
)
}
const doc = this.parser.parseFromString(str, item.mediaType)

await this.#loader.repalceDocSource(doc, item)
return doc
}
getMediaOverlay() {
Expand Down
26 changes: 14 additions & 12 deletions src/renderer/src/reader/reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,18 +173,20 @@ export class Reader {

getSections = async () => {
if (this.book.type !== 'pdf') {
const result = []
for (const section of this.book.sections) {
const id = section.id
const doc = await section.createDocument()
const body = doc.querySelector('body')
const height = estimatedHeight(body.cloneNode(true))
this.#handleLinks(body, section)
const html = body.innerHTML
.replace(/xmlns=".*?"/g, '')
.replace(/<([a-zA-Z0-9]+)(\s[^>]*)?>\s*<\/\1>/g, '') // 过滤掉空节点
result.push({ height, html, id })
}
const result = await Promise.all(
this.book.sections.map(async (section) => {
const id = section.id
const doc = await section.createDocument()
const body = doc.querySelector('body')
const height = estimatedHeight(body.cloneNode(true))
this.#handleLinks(body, section)
const html = body.innerHTML
.replace(/xmlns=".*?"/g, '')
.replace(/<([a-zA-Z0-9]+)(\s[^>]*)?>\s*<\/\1>/g, '') // 过滤掉空节点

return { height, html, id }
})
)
return result
} else {
return this.book.sections
Expand Down
42 changes: 25 additions & 17 deletions src/renderer/src/view/reader/reader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@ const props = defineProps({
const router = useRouter()
const book = BookAction.observableOne(props.id!) as Ref<Book>// 书籍信息
const watchBook = BookAction.observableOne(props.id!) as Ref<Book>
const bookInfo = ref<Book>()
watchEffect(() => {
if (get(watchBook)) {
set(bookInfo, get(watchBook))
}
})
const bookContent = ref<BookContent>() // 书籍内容
Expand Down Expand Up @@ -80,7 +88,6 @@ async function loadData() {
// 获取书本信息
const info = await BookAction.fineOne(bookId)
if (!info) return
// 获取书本内容
const content = await getBookContent(bookId, info.path)
if (!content) return
Expand All @@ -89,9 +96,10 @@ async function loadData() {
// 获取书本渲染器
const { sections, toc } = await render(file)
section.value = sections
tocList.value = toc
bookContent.value = content
set(bookContent, content)
set(section, sections)
set(tocList, toc)
set(bookInfo, info)
setLoading(false)
await nextTick()
Expand All @@ -102,6 +110,7 @@ async function loadData() {
set(tocList, outline)
}
initHighlight(info);
// 笔记跳转
const note = localStorage.getItem('__note__')
Expand Down Expand Up @@ -150,7 +159,6 @@ async function catalogJump({ page }: any) {
}
async function noteJump(note: Note) {
console.log('note', note)
const source = NoteAction.getDomSource(note.domSource)
if (source.length === 0) return
Expand Down Expand Up @@ -249,7 +257,7 @@ function sizeIn() {
// 阅读位置
const bookPageStore = useBookPageStore()
async function recordPosition() {
const info = get(book)
const info = get(bookInfo)
if (!info) return
let postion: Position | null = null
Expand Down Expand Up @@ -290,7 +298,7 @@ function restorePostion() {
// 页面刷新,直接从localStorage取
postion = sessionStorage.getItem('book-wise_refrersh')
} else {
postion = get(book)?.lastReadPosition
postion = get(bookInfo)?.lastReadPosition
}
sessionStorage.removeItem('book-wise_refrersh')
Expand All @@ -309,7 +317,7 @@ function restorePostion() {
// 进度
const updateProgress = (val: number) => {
BookAction.update(get(book)!.id, { progress: val })
BookAction.update(get(bookInfo)!.id, { progress: val })
}
// 记录阅读时长
Expand All @@ -331,7 +339,7 @@ function resetReadTime() {
}
function recordReadTime() {
const info = get(book)
const info = get(bookInfo)
if (!info) return
const spaceTime = +get(readTime)
if (spaceTime >= 1) {
Expand Down Expand Up @@ -388,7 +396,7 @@ onBeforeUnmount(() => {
<template>
<RingLoadingView class="min-h-screen" v-if="isLoading" />
<template v-else>
<template v-if="book && bookContent">
<template v-if="bookInfo && bookContent">
<!-- 目录 -->
<div class="block lg:hidden">
<DrawerView id="catalog-drawer">
Expand All @@ -399,9 +407,9 @@ onBeforeUnmount(() => {
<CatalogView :class="{ 'hide': isCatalog }" :data="tocList" @click="catalogJump" />
</div>
<div class="w-full max-w-full h-screen ">
<progress v-if="book.progress"
<progress v-if="bookInfo.progress"
class="progress progress-primary w-full fixed top-0 left-0 right-0 z-[9999999] h-[2px]"
:value="book.progress * 100" max="100"></progress>
:value="bookInfo.progress * 100" max="100"></progress>

<div class="flex h-full flex-col ">
<!-- 头部 -->
Expand Down Expand Up @@ -509,21 +517,21 @@ onBeforeUnmount(() => {
</template>

<!-- 工具栏 -->
<ToolbarView :book="book" v-if="isShowToolBar" />
<ToolbarView :book="bookInfo" v-if="isShowToolBar" />

<!-- 添加笔记 -->
<NoteRichView :book="book" v-if="isNoteRichShow" />
<NoteRichView :book="bookInfo" v-if="isNoteRichShow" />
</div>
</div>
</div>
<!-- 笔记 -->
<div class="block lg:hidden">
<DrawerView id="note-drawer" :is-right="true">
<NoteView :book="book" @jump="noteJump" :read-time="readTime" />
<NoteView :book="bookInfo" @jump="noteJump" :read-time="readTime" />
</DrawerView>
</div>
<div class="hidden lg:block">
<NoteView :book="book" :read-time="readTime" @jump="noteJump" :class="{ 'hide': isNote }" />
<NoteView :book="bookInfo" :read-time="readTime" @jump="noteJump" :class="{ 'hide': isNote }" />
</div>
</template>
<ErrorView v-else />
Expand Down

0 comments on commit 8c118f1

Please sign in to comment.