- Rename source module from RDReaderView to RDEpubReaderView - Move all source files from Sources/RDReaderView/ to Sources/RDEpubReaderView/ - Update podspec: RDReaderView.podspec -> RDEpubReaderView.podspec - Update Podfile, demo project, and CocoaPods config for new pod name - Delete old RDReaderView pod support files from ReadViewDemo/Pods - Add new RDEpubReaderView pod support files - Update documentation (API ref, architecture, UML, conventions, etc.) - Add FixedLayoutRotationTests - Update .gitignore: exclude .DS_Store, manual unpack backups, _ssoft-output
110 lines
3.7 KiB
Swift
110 lines
3.7 KiB
Swift
import Foundation
|
|
|
|
public enum RDEPUBCFISerializer {
|
|
|
|
public static func serialize(_ cfi: RDEPUBCFI) -> String {
|
|
|
|
var body = serializePath(cfi.packagePath)
|
|
|
|
let content = serializePath(cfi.contentPath)
|
|
|
|
if !content.isEmpty || cfi.characterOffset != nil {
|
|
body += "!" + content
|
|
}
|
|
|
|
if let characterOffset = cfi.characterOffset {
|
|
body += ":\(max(characterOffset, 0))"
|
|
}
|
|
|
|
if let sideBias = cfi.sideBias {
|
|
body += ";s=\(sideBias.rawValue)"
|
|
}
|
|
|
|
if let assertion = cfi.textAssertion {
|
|
body += "[\(assertion.prefix ?? ""),\(assertion.exact ?? ""),\(assertion.suffix ?? "")]"
|
|
}
|
|
return "epubcfi(\(body))"
|
|
}
|
|
|
|
public static func serializeRange(_ range: RDEPUBCFIRange) -> String {
|
|
let canonical = canonicalRangeComponents(for: range)
|
|
let parentBody = serializeRangeParent(canonical.parent)
|
|
let startBody = serializeRangeTerminal(
|
|
canonical.start,
|
|
relativeToPackagePath: canonical.parent.packagePath,
|
|
contentPrefix: canonical.parent.contentPath
|
|
)
|
|
let endBody = serializeRangeTerminal(
|
|
canonical.end,
|
|
relativeToPackagePath: canonical.parent.packagePath,
|
|
contentPrefix: canonical.parent.contentPath
|
|
)
|
|
return "epubcfi(\(parentBody),\(startBody),\(endBody))"
|
|
}
|
|
|
|
public static func serializePath(_ path: RDEPUBCFIPath) -> String {
|
|
guard !path.steps.isEmpty else { return "" }
|
|
return path.steps.map { step in
|
|
if let idAssertion = step.idAssertion {
|
|
return "/\(step.index)[\(idAssertion)]"
|
|
}
|
|
return "/\(step.index)"
|
|
}.joined()
|
|
}
|
|
|
|
private static func canonicalRangeComponents(for range: RDEPUBCFIRange) -> (
|
|
parent: RDEPUBCFI,
|
|
start: RDEPUBCFI,
|
|
end: RDEPUBCFI
|
|
) {
|
|
if let parent = range.parent {
|
|
return (parent, range.start, range.end)
|
|
}
|
|
|
|
let sharedPackage = range.start.packagePath.commonPrefix(with: range.end.packagePath)
|
|
let sharedContent = range.start.contentPath.commonPrefix(with: range.end.contentPath)
|
|
let parent = RDEPUBCFI(
|
|
packagePath: sharedPackage,
|
|
contentPath: sharedContent,
|
|
characterOffset: nil,
|
|
sideBias: nil,
|
|
textAssertion: nil
|
|
)
|
|
return (parent, range.start, range.end)
|
|
}
|
|
|
|
private static func serializeRangeParent(_ parent: RDEPUBCFI) -> String {
|
|
var body = serializePath(parent.packagePath)
|
|
let content = serializePath(parent.contentPath)
|
|
if !content.isEmpty {
|
|
body += "!" + content
|
|
}
|
|
return body
|
|
}
|
|
|
|
private static func serializeRangeTerminal(
|
|
_ cfi: RDEPUBCFI,
|
|
relativeToPackagePath parentPackagePath: RDEPUBCFIPath,
|
|
contentPrefix: RDEPUBCFIPath
|
|
) -> String {
|
|
let relativePackage = cfi.packagePath.droppingPrefix(parentPackagePath)
|
|
let relativeContent = cfi.contentPath.droppingPrefix(contentPrefix)
|
|
|
|
var body = serializePath(relativePackage)
|
|
let content = serializePath(relativeContent)
|
|
if !content.isEmpty || cfi.characterOffset != nil || !relativePackage.steps.isEmpty {
|
|
body += content
|
|
}
|
|
if let characterOffset = cfi.characterOffset {
|
|
body += ":\(max(characterOffset, 0))"
|
|
}
|
|
if let sideBias = cfi.sideBias {
|
|
body += ";s=\(sideBias.rawValue)"
|
|
}
|
|
if let assertion = cfi.textAssertion {
|
|
body += "[\(assertion.prefix ?? ""),\(assertion.exact ?? ""),\(assertion.suffix ?? "")]"
|
|
}
|
|
return body
|
|
}
|
|
}
|