修改分页问题

This commit is contained in:
shen 2026-05-26 21:08:27 +08:00
parent 0e7d952fe5
commit 83b705b9ae
14 changed files with 227 additions and 50 deletions

View File

@ -21,7 +21,7 @@
| 文档 | 说明 | | 文档 | 说明 |
|------|------| |------|------|
| [ReflowableEPUB_WXReadRenderer_Design.md](FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md) | 基于读书反编译的 CoreText 渲染架构,设计 Reflowable EPUB 的增强文本渲染方案CSS 分层、类型器升级) | | [ReflowableEPUB_WXReadRenderer_Design.md](FeatureSolution/ReflowableEPUB_WXReadRenderer_Design.md) | 基于读书的 CoreText 渲染架构,设计 Reflowable EPUB 的增强文本渲染方案CSS 分层、类型器升级) |
## 目录约定 ## 目录约定

View File

@ -185,10 +185,14 @@
inputFileListPaths = ( inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks-${CONFIGURATION}-input-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks-${CONFIGURATION}-input-files.xcfilelist",
); );
inputPaths = (
);
name = "[CP] Embed Pods Frameworks"; name = "[CP] Embed Pods Frameworks";
outputFileListPaths = ( outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks-${CONFIGURATION}-output-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks-${CONFIGURATION}-output-files.xcfilelist",
); );
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReadViewDemo/Pods-ReadViewDemo-frameworks.sh\"\n";

View File

@ -104,7 +104,8 @@ public struct RDEPUBDTCoreTextRenderer: RDEPUBTextRenderer {
guard let element else { return } guard let element else { return }
RDEPUBTextRendererSupport.prepareHTMLElementForReaderRendering( RDEPUBTextRendererSupport.prepareHTMLElementForReaderRendering(
element, element,
style: request.style style: request.style,
maxImageSize: resolvedMaxImageSize(for: request)
) )
} }
return builder?.generatedAttributedString() return builder?.generatedAttributedString()
@ -116,7 +117,7 @@ public struct RDEPUBDTCoreTextRenderer: RDEPUBTextRenderer {
/// style.lineSpacing /// style.lineSpacing
private func dtOptions(request: RDEPUBTextChapterRenderRequest) -> [AnyHashable: Any] { private func dtOptions(request: RDEPUBTextChapterRenderRequest) -> [AnyHashable: Any] {
let style = request.style let style = request.style
let screenBounds = UIScreen.main.bounds.insetBy(dx: 20, dy: 28) let maxImageSize = resolvedMaxImageSize(for: request)
var options: [AnyHashable: Any] = [ var options: [AnyHashable: Any] = [
NSTextSizeMultiplierDocumentOption: 1.0, NSTextSizeMultiplierDocumentOption: 1.0,
DTDefaultFontFamily: style.font.familyName, DTDefaultFontFamily: style.font.familyName,
@ -124,7 +125,7 @@ public struct RDEPUBDTCoreTextRenderer: RDEPUBTextRenderer {
DTDefaultFontSize: style.font.pointSize, DTDefaultFontSize: style.font.pointSize,
DTDefaultLineHeightMultiplier: max((style.font.lineHeight + style.lineSpacing) / max(style.font.lineHeight, 1), 1), DTDefaultLineHeightMultiplier: max((style.font.lineHeight + style.lineSpacing) / max(style.font.lineHeight, 1), 1),
DTUseiOS6Attributes: true, DTUseiOS6Attributes: true,
DTMaxImageSize: NSValue(cgSize: screenBounds.size) DTMaxImageSize: NSValue(cgSize: maxImageSize)
] ]
if let baseURL = request.context.baseURL { if let baseURL = request.context.baseURL {
@ -136,5 +137,18 @@ public struct RDEPUBDTCoreTextRenderer: RDEPUBTextRenderer {
return options return options
} }
private func resolvedMaxImageSize(for request: RDEPUBTextChapterRenderRequest) -> CGSize {
if let pageSize = request.pageSize {
let layoutConfig = request.layoutConfig ?? .default
let contentRect = layoutConfig.contentRect(fallback: pageSize)
let maxWidth = max(round(contentRect.width), 1)
let maxHeight = max(round(contentRect.height * layoutConfig.imageMaxHeightRatio), 1)
return CGSize(width: maxWidth, height: maxHeight)
}
let screenBounds = UIScreen.main.bounds.insetBy(dx: 20, dy: 28)
return CGSize(width: max(round(screenBounds.width), 1), height: max(round(screenBounds.height * 0.85), 1))
}
#endif #endif
} }

View File

@ -293,7 +293,9 @@ public final class RDEPUBTextBookBuilder {
baseURL: parser.fileURL(forRelativePath: item.href)?.deletingLastPathComponent(), baseURL: parser.fileURL(forRelativePath: item.href)?.deletingLastPathComponent(),
style: style, style: style,
resourceResolver: publication.resourceResolver, resourceResolver: publication.resourceResolver,
contentLanguageCode: publication.metadata.language contentLanguageCode: publication.metadata.language,
pageSize: pageSize,
layoutConfig: layoutConfig
) )
// HTML NSAttributedString // HTML NSAttributedString

View File

@ -123,7 +123,7 @@ final class ChapterPaginationArchive: NSObject, NSSecureCoding {
public final class RDEPUBTextBookCache { public final class RDEPUBTextBookCache {
/// ///
public var schemaVersion: Int = 1 public var schemaVersion: Int = 4
/// 线 /// 线
private let queue = DispatchQueue(label: "com.rdreader.textbookcache", qos: .utility) private let queue = DispatchQueue(label: "com.rdreader.textbookcache", qos: .utility)

View File

@ -155,27 +155,31 @@ struct RDEPUBTextLayouter {
totalLength: attributedString.length, totalLength: attributedString.length,
lineRanges: lineRanges lineRanges: lineRanges
) )
let verifiedRange = verifiedDisplayRange(for: adjusted.range)
let trailingFragmentID = nearestTrailingFragmentID( let trailingFragmentID = nearestTrailingFragmentID(
endingAt: adjusted.range.location + adjusted.range.length, endingAt: verifiedRange.location + verifiedRange.length,
fragmentOffsets: fragmentOffsets fragmentOffsets: fragmentOffsets
) )
let diagnostics = verifiedRange == adjusted.range
? adjusted.diagnostics
: adjusted.diagnostics + ["verified-display-range \(NSStringFromRange(adjusted.range)) -> \(NSStringFromRange(verifiedRange))"]
frames.append( frames.append(
RDEPUBTextLayoutFrame( RDEPUBTextLayoutFrame(
contentRange: adjusted.range, contentRange: verifiedRange,
breakReason: adjusted.breakReason, breakReason: adjusted.breakReason,
blockRange: adjusted.blockRange, blockRange: blockRange(at: max(verifiedRange.location, verifiedRange.location + verifiedRange.length - 1)),
attachmentRanges: adjusted.attachmentRanges, attachmentRanges: attachmentRanges(in: verifiedRange),
attachmentKinds: adjusted.attachmentKinds, attachmentKinds: attachmentKinds(in: verifiedRange),
blockKinds: adjusted.blockKinds, blockKinds: blockKinds(in: verifiedRange),
semanticHints: adjusted.semanticHints, semanticHints: semanticHints(in: verifiedRange),
attachmentPlacements: adjusted.attachmentPlacements, attachmentPlacements: attachmentPlacements(in: verifiedRange),
trailingFragmentID: trailingFragmentID, trailingFragmentID: trailingFragmentID,
diagnostics: adjusted.diagnostics diagnostics: diagnostics
) )
) )
let nextLocation = adjusted.range.location + adjusted.range.length let nextLocation = verifiedRange.location + verifiedRange.length
guard nextLocation > location else { guard nextLocation > location else {
location += max(visibleRange.length, 1) location += max(visibleRange.length, 1)
continue continue
@ -185,6 +189,30 @@ struct RDEPUBTextLayouter {
return frames return frames
} }
private func verifiedDisplayRange(for range: NSRange) -> NSRange {
guard range.length > 0,
!attachmentRanges(in: range).isEmpty else {
return range
}
let pageContent = attributedString.attributedSubstring(from: range)
guard let layouter = DTCoreTextLayouter(attributedString: pageContent) else {
return range
}
layouter.shouldCacheLayoutFrames = false
guard let layoutFrame = layouter.layoutFrame(with: dtLayoutRect, range: NSRange(location: 0, length: 0)) else {
return range
}
let visibleRange = layoutFrame.visibleStringRange()
guard visibleRange.length > 0, visibleRange.length < range.length else {
return range
}
return NSRange(location: range.location, length: visibleRange.length)
}
#endif #endif
private static func makeLayoutPath(pageSize: CGSize, config: RDEPUBTextLayoutConfig) -> CGPath { private static func makeLayoutPath(pageSize: CGSize, config: RDEPUBTextLayoutConfig) -> CGPath {

View File

@ -286,10 +286,21 @@ public struct RDEPUBTextChapterContext: Equatable {
public struct RDEPUBTextChapterRenderRequest { public struct RDEPUBTextChapterRenderRequest {
public var context: RDEPUBTextChapterContext public var context: RDEPUBTextChapterContext
public var style: RDEPUBTextRenderStyle public var style: RDEPUBTextRenderStyle
/// page size
public var pageSize: CGSize?
/// WXRead
public var layoutConfig: RDEPUBTextLayoutConfig?
public init(context: RDEPUBTextChapterContext, style: RDEPUBTextRenderStyle) { public init(
context: RDEPUBTextChapterContext,
style: RDEPUBTextRenderStyle,
pageSize: CGSize? = nil,
layoutConfig: RDEPUBTextLayoutConfig? = nil
) {
self.context = context self.context = context
self.style = style self.style = style
self.pageSize = pageSize
self.layoutConfig = layoutConfig
} }
} }

View File

@ -197,7 +197,9 @@ enum RDEPUBTextRendererSupport {
baseURL: URL?, baseURL: URL?,
style: RDEPUBTextRenderStyle, style: RDEPUBTextRenderStyle,
resourceResolver: RDEPUBResourceResolver?, resourceResolver: RDEPUBResourceResolver?,
contentLanguageCode: String? = nil contentLanguageCode: String? = nil,
pageSize: CGSize? = nil,
layoutConfig: RDEPUBTextLayoutConfig? = nil
) -> RDEPUBTextChapterRenderRequest { ) -> RDEPUBTextChapterRenderRequest {
let normalizedHTML = injectPaginationSemanticMarkers(into: normalizeHTML(rawHTML)) let normalizedHTML = injectPaginationSemanticMarkers(into: normalizeHTML(rawHTML))
let stylesheetHrefReplacements = inlineLinkedStyleSheets( let stylesheetHrefReplacements = inlineLinkedStyleSheets(
@ -255,7 +257,12 @@ enum RDEPUBTextRendererSupport {
stylesheet: RDEPUBTextStyleSheetPackage(layers: layers), stylesheet: RDEPUBTextStyleSheetPackage(layers: layers),
resourceDiagnostics: resourceDiagnostics resourceDiagnostics: resourceDiagnostics
) )
return RDEPUBTextChapterRenderRequest(context: context, style: style) return RDEPUBTextChapterRenderRequest(
context: context,
style: style,
pageSize: pageSize,
layoutConfig: layoutConfig
)
} }
/// HTML CR HTML /// HTML CR HTML
@ -285,15 +292,20 @@ enum RDEPUBTextRendererSupport {
#if canImport(DTCoreText) #if canImport(DTCoreText)
static func prepareHTMLElementForReaderRendering( static func prepareHTMLElementForReaderRendering(
_ element: DTHTMLElement, _ element: DTHTMLElement,
style: RDEPUBTextRenderStyle style: RDEPUBTextRenderStyle,
maxImageSize: CGSize? = nil
) { ) {
guard let attachment = element.textAttachment else { return } guard let attachment = element.textAttachment else { return }
let pointSize = max(element.fontDescriptor.pointSize, style.font.pointSize) let pointSize = max(element.fontDescriptor.pointSize, style.font.pointSize)
let maxSize = CGSize( let fallbackSize = CGSize(
width: round(UIScreen.main.bounds.insetBy(dx: 20, dy: 28).width), width: round(UIScreen.main.bounds.insetBy(dx: 20, dy: 28).width),
height: round(UIScreen.main.bounds.insetBy(dx: 20, dy: 28).height * 0.85) height: round(UIScreen.main.bounds.insetBy(dx: 20, dy: 28).height * 0.85)
) )
normalizeAttachmentLayoutForWXRead(attachment, fontPointSize: pointSize, maxImageSize: maxSize) normalizeAttachmentLayoutForWXRead(
attachment,
fontPointSize: pointSize,
maxImageSize: maxImageSize ?? fallbackSize
)
if isFootnoteAttachment(attachment) { if isFootnoteAttachment(attachment) {
element.displayStyle = .inline element.displayStyle = .inline
} else if isCoverAttachment(attachment) { } else if isCoverAttachment(attachment) {
@ -390,6 +402,38 @@ enum RDEPUBTextRendererSupport {
private static func normalizeAttachmentHTMLMarkers(in html: String) -> String { private static func normalizeAttachmentHTMLMarkers(in html: String) -> String {
var normalized = html var normalized = html
if let bodyPicContainerRegex = try? NSRegularExpression(
pattern: #"<div\b([^>]*class\s*=\s*["'][^"']*\b(?:qrbodyPic|bodyPic)\b[^"']*["'][^>]*)>([\s\S]*?)</div>"#,
options: [.caseInsensitive]
) {
normalized = replaceMatches(
using: bodyPicContainerRegex,
in: normalized
) { tag in
guard let imageTagRegex = try? NSRegularExpression(pattern: #"<img\b[^>]*>"#, options: [.caseInsensitive]) else {
return tag
}
return replaceMatches(
using: imageTagRegex,
in: tag
) { imageTag in
mergeHTMLAttributes(
into: imageTag,
requiredClass: "bodyPic",
styleFragments: [
"wr-vertical-center-style:2",
"max-width:100%",
"height:auto",
"display:block",
"margin-left:auto",
"margin-right:auto"
]
)
}
}
}
if let footnoteRegex = try? NSRegularExpression( if let footnoteRegex = try? NSRegularExpression(
pattern: #"<img\b([^>]*class\s*=\s*["'][^"']*\bqqreader-footnote\b[^"']*["'][^>]*)>"#, pattern: #"<img\b([^>]*class\s*=\s*["'][^"']*\bqqreader-footnote\b[^"']*["'][^>]*)>"#,
options: [.caseInsensitive] options: [.caseInsensitive]

View File

@ -139,9 +139,10 @@ struct RDEPUBPageLayoutSnapshot {
var lines: [RDEPUBPageLine] = [] var lines: [RDEPUBPageLine] = []
var runs: [RDEPUBPageRun] = [] var runs: [RDEPUBPageRun] = []
var attachments: [RDEPUBPageAttachment] = [] var attachments: [RDEPUBPageAttachment] = []
let pageOffset = page.pageStartOffset
for dtLine in dtLines { for dtLine in dtLines {
let lineRange = dtLine.stringRange() let lineRange = offset(dtLine.stringRange(), by: pageOffset)
let line = RDEPUBPageLine( let line = RDEPUBPageLine(
stringRange: lineRange, stringRange: lineRange,
frame: dtLine.frame, frame: dtLine.frame,
@ -154,7 +155,7 @@ struct RDEPUBPageLayoutSnapshot {
if let glyphRuns = dtLine.glyphRuns as? [DTCoreTextGlyphRun] { if let glyphRuns = dtLine.glyphRuns as? [DTCoreTextGlyphRun] {
for run in glyphRuns { for run in glyphRuns {
let runRange = run.stringRange() let runRange = offset(run.stringRange(), by: pageOffset)
let isAttachment = run.attachment != nil let isAttachment = run.attachment != nil
runs.append( runs.append(
RDEPUBPageRun( RDEPUBPageRun(
@ -182,7 +183,7 @@ struct RDEPUBPageLayoutSnapshot {
} }
} }
let visibleRange = layoutFrame.visibleStringRange() let visibleRange = offset(layoutFrame.visibleStringRange(), by: pageOffset)
return RDEPUBPageLayoutSnapshot( return RDEPUBPageLayoutSnapshot(
page: page, page: page,
@ -210,5 +211,12 @@ struct RDEPUBPageLayoutSnapshot {
: nil : nil
return (placement, kind) return (placement, kind)
} }
private static func offset(_ range: NSRange, by offset: Int) -> NSRange {
guard range.location != NSNotFound else {
return range
}
return NSRange(location: range.location + offset, length: range.length)
}
#endif #endif
} }

View File

@ -165,6 +165,8 @@ public final class RDEPUBReaderController: UIViewController {
private var currentBrightness: CGFloat private var currentBrightness: CGFloat
private var didStartInitialLoad = false private var didStartInitialLoad = false
private var isRepaginating = false private var isRepaginating = false
private var lastTextPaginationPageSize: CGSize?
private var isReconcilingTextPaginationSize = false
private var paginationToken = UUID() private var paginationToken = UUID()
private var paginator: RDEPUBPaginator? private var paginator: RDEPUBPaginator?
private var searchState: RDEPUBSearchState? private var searchState: RDEPUBSearchState?
@ -695,6 +697,7 @@ public final class RDEPUBReaderController: UIViewController {
if publication.readingProfile == .textReflowable { if publication.readingProfile == .textReflowable {
let renderer = resolvedTextRenderer() let renderer = resolvedTextRenderer()
let pageSize = currentTextPageSize() let pageSize = currentTextPageSize()
lastTextPaginationPageSize = pageSize
let layoutConfig = currentTextLayoutConfig(pageSize: pageSize) let layoutConfig = currentTextLayoutConfig(pageSize: pageSize)
let builder = RDEPUBTextBookBuilder(renderer: renderer, cache: textBookCache, layoutConfig: layoutConfig) let builder = RDEPUBTextBookBuilder(renderer: renderer, cache: textBookCache, layoutConfig: layoutConfig)
let renderStyle = currentTextRenderStyle() let renderStyle = currentTextRenderStyle()
@ -1258,12 +1261,12 @@ public final class RDEPUBReaderController: UIViewController {
} }
private func currentTextPageSize() -> CGSize { private func currentTextPageSize() -> CGSize {
let viewportSize = currentLayoutContext().viewportSize let pageNum = readerView.currentPage >= 0 ? readerView.currentPage : nil
let insets = configuration.reflowableContentInsets let resolvedSize = readerView.resolvedSinglePageSize(pageNum: pageNum)
return CGSize( if resolvedSize.width > 0, resolvedSize.height > 0 {
width: max(viewportSize.width - insets.left - insets.right, 1), return resolvedSize
height: max(viewportSize.height - insets.top - insets.bottom, 1) }
) return currentLayoutContext().viewportSize
} }
private func currentTextRenderStyle() -> RDEPUBTextRenderStyle { private func currentTextRenderStyle() -> RDEPUBTextRenderStyle {
@ -1278,15 +1281,10 @@ public final class RDEPUBReaderController: UIViewController {
} }
private func currentTextLayoutConfig(pageSize: CGSize) -> RDEPUBTextLayoutConfig { private func currentTextLayoutConfig(pageSize: CGSize) -> RDEPUBTextLayoutConfig {
let viewportSize = currentLayoutContext().viewportSize
let derivedFrameSize = CGSize(
width: max(viewportSize.width, pageSize.width),
height: max(viewportSize.height, pageSize.height)
)
return RDEPUBTextLayoutConfig( return RDEPUBTextLayoutConfig(
frameWidth: derivedFrameSize.width, frameWidth: max(pageSize.width, 1),
frameHeight: derivedFrameSize.height, frameHeight: max(pageSize.height, 1),
edgeInsets: .zero, edgeInsets: configuration.reflowableContentInsets,
numberOfColumns: configuration.numberOfColumns, numberOfColumns: configuration.numberOfColumns,
columnGap: configuration.columnGap, columnGap: configuration.columnGap,
avoidOrphans: true, avoidOrphans: true,
@ -1834,6 +1832,7 @@ extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate {
public func pageNum(readerView: RDReaderView, pageNum: Int) { public func pageNum(readerView: RDReaderView, pageNum: Int) {
updateCurrentSelection(nil) updateCurrentSelection(nil)
reconcileTextPaginationSizeIfNeeded(for: pageNum)
let totalPages = pageCountOfReaderView(readerView: readerView) let totalPages = pageCountOfReaderView(readerView: readerView)
if totalPages > 0, pageNum == totalPages - 1 { if totalPages > 0, pageNum == totalPages - 1 {
@ -1859,6 +1858,36 @@ extension RDEPUBReaderController: RDReaderDataSource, RDReaderDelegate {
_ = isLandscape _ = isLandscape
pendingPresentationRestoreLocation = currentVisibleLocation() ?? persistenceLocation() pendingPresentationRestoreLocation = currentVisibleLocation() ?? persistenceLocation()
} }
private func reconcileTextPaginationSizeIfNeeded(for pageNum: Int) {
guard textBook != nil,
!isRepaginating,
!isReconcilingTextPaginationSize,
pageNum >= 0,
let lastTextPaginationPageSize else {
return
}
let resolvedSize = readerView.resolvedSinglePageSize(pageNum: pageNum)
guard resolvedSize.width > 0,
resolvedSize.height > 0 else {
return
}
let sizeChanged = abs(resolvedSize.width - lastTextPaginationPageSize.width) > 0.5
|| abs(resolvedSize.height - lastTextPaginationPageSize.height) > 0.5
guard sizeChanged else {
return
}
isReconcilingTextPaginationSize = true
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.isReconcilingTextPaginationSize = false
guard self.textBook != nil, !self.isRepaginating else { return }
self.repaginatePreservingCurrentLocation()
}
}
} }
// MARK: - Web EPUB /Web // MARK: - Web EPUB /Web

View File

@ -265,7 +265,7 @@ final class RDEPUBTextContentView: UIView {
) )
#if canImport(DTCoreText) #if canImport(DTCoreText)
let displayContent = NSMutableAttributedString(attributedString: page.chapterContent) let displayContent = NSMutableAttributedString(attributedString: page.content)
let fullRange = NSRange(location: 0, length: displayContent.length) let fullRange = NSRange(location: 0, length: displayContent.length)
displayContent.addAttribute( displayContent.addAttribute(
.foregroundColor, .foregroundColor,
@ -275,7 +275,7 @@ final class RDEPUBTextContentView: UIView {
coreTextContentView.isHidden = false coreTextContentView.isHidden = false
coreTextContentView.backgroundColor = .clear coreTextContentView.backgroundColor = .clear
coreTextDisplayContent = displayContent coreTextDisplayContent = displayContent
coreTextDisplayRange = page.contentRange coreTextDisplayRange = NSRange(location: 0, length: displayContent.length)
textView.isHidden = true textView.isHidden = true
textView.isUserInteractionEnabled = false textView.isUserInteractionEnabled = false
textView.attributedText = nil textView.attributedText = nil

View File

@ -123,7 +123,7 @@ public final class RDURLReaderController: UIViewController {
layoutConfig: RDEPUBTextLayoutConfig( layoutConfig: RDEPUBTextLayoutConfig(
frameWidth: pageSize.width, frameWidth: pageSize.width,
frameHeight: pageSize.height, frameHeight: pageSize.height,
edgeInsets: .zero, edgeInsets: epubConfiguration.reflowableContentInsets,
numberOfColumns: 1, numberOfColumns: 1,
columnGap: 20, columnGap: 20,
avoidOrphans: true, avoidOrphans: true,
@ -179,15 +179,11 @@ public final class RDURLReaderController: UIViewController {
print("[ReadViewDemo] automation \(prefix) -> page \(page) href \(href) progression \(progression)") print("[ReadViewDemo] automation \(prefix) -> page \(page) href \(href) progression \(progression)")
} }
/// ///
/// /// 使 viewport layoutConfig.edgeInsets
///
private func currentTextPageSize() -> CGSize { private func currentTextPageSize() -> CGSize {
let viewportSize = UIScreen.main.bounds.size UIScreen.main.bounds.size
let insets = epubConfiguration.reflowableContentInsets
return CGSize(
width: max(viewportSize.width - insets.left - insets.right, 1),
height: max(viewportSize.height - insets.top - insets.bottom, 1)
)
} }
/// ///

View File

@ -1265,6 +1265,47 @@ extension RDReaderView {
return cell?.containerView return cell?.containerView
} }
} }
///
/// 使退
public func resolvedSinglePageSize(pageNum: Int? = nil) -> CGSize {
let targetPage = pageNum ?? (currentPage >= 0 ? currentPage : nil)
if let targetPage,
let contentView = pageContentView(pageNum: targetPage),
contentView.bounds.width > 0,
contentView.bounds.height > 0 {
return contentView.bounds.size
}
if currentDisplayType == .pageCurl {
if let childViewController = pageViewController.viewControllers?.first as? RDReaderPageChildViewController,
childViewController.view.bounds.width > 0,
childViewController.view.bounds.height > 0 {
return childViewController.view.bounds.size
}
if pageViewController.view.bounds.width > 0, pageViewController.view.bounds.height > 0 {
return pageViewController.view.bounds.size
}
return bounds.size
}
let containerBounds = collectionView.bounds.size == .zero ? bounds.size : collectionView.bounds.size
guard containerBounds.width > 0, containerBounds.height > 0 else {
return bounds.size
}
switch currentDisplayType {
case .horizontalScroll:
return CGSize(
width: containerBounds.width / CGFloat(max(pagesPerScreen, 1)),
height: containerBounds.height
)
case .verticalScroll:
return containerBounds
case .pageCurl:
return bounds.size
}
}
} }
private var cellViewKey: Int8 = 0 private var cellViewKey: Int8 = 0