feat(epub): align wxread css and overlay pagination behavior
This commit is contained in:
@@ -80,29 +80,101 @@ public struct RDEPUBTextRenderStyle {
|
||||
|
||||
/// 分页引擎的布局控制参数。
|
||||
public struct RDEPUBTextLayoutConfig: Equatable {
|
||||
/// 页面帧宽度;传 0 时回退为分页入口传入的 pageSize.width
|
||||
public var frameWidth: CGFloat
|
||||
/// 页面帧高度;传 0 时回退为分页入口传入的 pageSize.height
|
||||
public var frameHeight: CGFloat
|
||||
/// 页面内容内边距,对标 WXRead 的 WRCoreTextLayoutConfig.edgeInsets
|
||||
public var edgeInsets: UIEdgeInsets
|
||||
/// 栏数,对标 WXRead 的 numberOfColumns
|
||||
public var numberOfColumns: Int
|
||||
/// 栏间距,对标 WXRead 的 columnGap
|
||||
public var columnGap: CGFloat
|
||||
/// 是否避免孤行(段落最后一行单独在下一页顶部)
|
||||
public var avoidOrphans: Bool
|
||||
/// 是否避免寡行(段落第一行单独在上一页底部)
|
||||
public var avoidWidows: Bool
|
||||
/// 是否启用 avoidPageBreakInside 保护(对标 WXRead 的行级回退扫描)
|
||||
public var avoidPageBreakInsideEnabled: Bool
|
||||
/// 是否启用连字符断字,对标 WXRead 的 hyphenation
|
||||
public var hyphenation: Bool
|
||||
/// 图片最大高度占页面高度的比例
|
||||
public var imageMaxHeightRatio: CGFloat
|
||||
|
||||
public init(
|
||||
frameWidth: CGFloat = 0,
|
||||
frameHeight: CGFloat = 0,
|
||||
edgeInsets: UIEdgeInsets = .zero,
|
||||
numberOfColumns: Int = 1,
|
||||
columnGap: CGFloat = 20,
|
||||
avoidOrphans: Bool = true,
|
||||
avoidWidows: Bool = true,
|
||||
avoidPageBreakInsideEnabled: Bool = true,
|
||||
hyphenation: Bool = true,
|
||||
imageMaxHeightRatio: CGFloat = 0.85
|
||||
) {
|
||||
self.frameWidth = frameWidth
|
||||
self.frameHeight = frameHeight
|
||||
self.edgeInsets = edgeInsets
|
||||
self.numberOfColumns = max(1, numberOfColumns)
|
||||
self.columnGap = max(0, columnGap)
|
||||
self.avoidOrphans = avoidOrphans
|
||||
self.avoidWidows = avoidWidows
|
||||
self.avoidPageBreakInsideEnabled = avoidPageBreakInsideEnabled
|
||||
self.hyphenation = hyphenation
|
||||
self.imageMaxHeightRatio = imageMaxHeightRatio
|
||||
}
|
||||
|
||||
/// 默认配置
|
||||
public static let `default` = RDEPUBTextLayoutConfig()
|
||||
|
||||
/// 结合调用方 pageSize 解析后的实际页面尺寸。
|
||||
public func resolvedFrameSize(fallback pageSize: CGSize) -> CGSize {
|
||||
CGSize(
|
||||
width: max(frameWidth > 0 ? frameWidth : pageSize.width, 1),
|
||||
height: max(frameHeight > 0 ? frameHeight : pageSize.height, 1)
|
||||
)
|
||||
}
|
||||
|
||||
/// 实际内容区域;对标 WXRead 的 frame + edgeInsets 组合。
|
||||
public func contentRect(fallback pageSize: CGSize) -> CGRect {
|
||||
let size = resolvedFrameSize(fallback: pageSize)
|
||||
return CGRect(origin: .zero, size: size).inset(by: edgeInsets)
|
||||
}
|
||||
|
||||
/// 多栏布局时的列矩形数组。
|
||||
public func columnRects(fallback pageSize: CGSize) -> [CGRect] {
|
||||
let rect = contentRect(fallback: pageSize)
|
||||
let columns = max(1, numberOfColumns)
|
||||
guard columns > 1 else { return [rect] }
|
||||
|
||||
let totalGap = CGFloat(columns - 1) * columnGap
|
||||
let columnWidth = max((rect.width - totalGap) / CGFloat(columns), 1)
|
||||
|
||||
return (0..<columns).map { index in
|
||||
let originX = rect.minX + CGFloat(index) * (columnWidth + columnGap)
|
||||
return CGRect(x: originX, y: rect.minY, width: columnWidth, height: rect.height)
|
||||
}
|
||||
}
|
||||
|
||||
/// 持久化/缓存键使用的稳定签名。
|
||||
public var cacheSignature: String {
|
||||
[
|
||||
String(format: "%.3f", frameWidth),
|
||||
String(format: "%.3f", frameHeight),
|
||||
String(format: "%.3f", edgeInsets.top),
|
||||
String(format: "%.3f", edgeInsets.left),
|
||||
String(format: "%.3f", edgeInsets.bottom),
|
||||
String(format: "%.3f", edgeInsets.right),
|
||||
String(numberOfColumns),
|
||||
String(format: "%.3f", columnGap),
|
||||
avoidOrphans ? "1" : "0",
|
||||
avoidWidows ? "1" : "0",
|
||||
avoidPageBreakInsideEnabled ? "1" : "0",
|
||||
hyphenation ? "1" : "0",
|
||||
String(format: "%.3f", imageMaxHeightRatio)
|
||||
].joined(separator: "|")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CSS 样式表层级
|
||||
|
||||
Reference in New Issue
Block a user