Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTML/Epub reader #810

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0098e2d
Created new reader controller, added js reader, basic setup
michalrentka Aug 24, 2023
7659bee
push snapshot data to webview
michalrentka Aug 31, 2023
1c64c15
WIP
michalrentka Sep 5, 2023
af81e15
Separating annotation toolbar from PDF reader
michalrentka Sep 6, 2023
63f1df4
Finished separating toolbar from pdf viewer
michalrentka Sep 7, 2023
fe9a722
Added annotation toolbar to html/epub reader
michalrentka Sep 8, 2023
6088d62
WIP
michalrentka Sep 11, 2023
aa57309
fixed _view initialization
michalrentka Sep 12, 2023
40aa33e
wip
michalrentka Sep 15, 2023
c928bf6
wip
michalrentka Sep 20, 2023
be9148c
WIP
michalrentka Sep 26, 2023
df080c9
wip
michalrentka Sep 28, 2023
361fd6f
wip
michalrentka Oct 3, 2023
201500a
Fixed storing and loading of annotations
michalrentka Oct 5, 2023
32c6eb2
Html/epub reader sidebar wip
michalrentka Oct 9, 2023
d8181cd
wip sidebar functionality
michalrentka Oct 13, 2023
82b82e7
Implemented observing of annotations, search and filtering support, s…
michalrentka Oct 18, 2023
6a7c26e
wip
michalrentka Oct 20, 2023
60c9bbc
wip
michalrentka Oct 23, 2023
0b38964
Bug fixes, added search
Oct 24, 2023
f8d4d1e
Selection fixes
michalrentka Oct 25, 2023
0fcefbf
Annotation popover improvements
michalrentka Oct 26, 2023
29ad145
wip
michalrentka Nov 3, 2023
38b04e3
Bug fixes, view state restoration
michalrentka Nov 9, 2023
6076e0b
Bug fixes & reader improvements
michalrentka Nov 13, 2023
f7f0e45
Implemented html/epub reader sidebar editing
michalrentka Nov 14, 2023
b867bc1
Added filtering to html epub sidebar, added settings button
michalrentka Nov 15, 2023
f36bd1e
Implemented settings, bug fixes & improvements
michalrentka Nov 15, 2023
546527f
Removed reader
michalrentka Nov 21, 2023
16765e7
Added reader as submodule, added build script
michalrentka Nov 22, 2023
6d3bd32
rebase fix
michalrentka Nov 22, 2023
c9a9813
rebase fixes
michalrentka Jan 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,5 @@ fastlane/test_output

bundled/translators
bundled/styles
bundled/locales
bundled/locales
bundled/reader
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "translators"]
path = translators
url = https://github.com/zotero/translators.git
[submodule "reader"]
path = reader
url = https://github.com/zotero/reader.git
10 changes: 5 additions & 5 deletions ZShare/Controllers/TranslationWebViewHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,14 @@ final class TranslationWebViewHandler {
}
.flatMap { translators -> Single<Any> in
DDLogInfo("WebViewHandler: encode translators")
let encodedTranslators = WKWebView.encodeAsJSONForJavascript(translators)
let encodedTranslators = WebViewEncoder.encodeAsJSONForJavascript(translators)
return self.webViewHandler.call(javascript: "initTranslators(\(encodedTranslators));")
}
.flatMap({ _ -> Single<Any> in
DDLogInfo("WebViewHandler: call translate js")
let encodedHtml = WKWebView.encodeForJavascript(html.data(using: .utf8))
let encodedHtml = WebViewEncoder.encodeForJavascript(html.data(using: .utf8))
let jsonFramesData = try? JSONSerialization.data(withJSONObject: frames, options: .fragmentsAllowed)
let encodedFrames = jsonFramesData.flatMap({ WKWebView.encodeForJavascript($0) }) ?? "''"
let encodedFrames = jsonFramesData.flatMap({ WebViewEncoder.encodeForJavascript($0) }) ?? "''"
return self.webViewHandler.call(javascript: "translate('\(url.absoluteString)', \(encodedHtml), \(encodedFrames));")
})
.subscribe(onFailure: { [weak self] error in
Expand All @@ -169,8 +169,8 @@ final class TranslationWebViewHandler {
return Disposables.create()
}

let encodedSchema = WKWebView.encodeForJavascript(schemaData)
let encodedFormats = WKWebView.encodeForJavascript(dateFormatData)
let encodedSchema = WebViewEncoder.encodeForJavascript(schemaData)
let encodedFormats = WebViewEncoder.encodeForJavascript(dateFormatData)

DDLogInfo("WebViewHandler: loaded bundled files")

Expand Down
246 changes: 185 additions & 61 deletions Zotero.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Zotero/Assets/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@
"errors.pdf.cant_update_annotation" = "Can't update annotation.";
"errors.pdf.cant_add_annotations" = "Can't add annotations.";
"errors.pdf.cant_delete_annotations" = "Can't delete annotations.";
"errors.pdf.incompatible_document" = "This document is not supported.";
"errors.pdf.page_index_not_int" = "Incorrect format of page stored for this document.";

"accessibility.untitled" = "Untitled";
Expand Down
16 changes: 8 additions & 8 deletions Zotero/Controllers/AnnotationConverter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ struct AnnotationConverter {
username: String,
displayName: String,
boundingBoxConverter: AnnotationBoundingBoxConverter?
) -> DocumentAnnotation? {
) -> PdfDocumentAnnotation? {
guard let document = annotation.document, AnnotationsConfig.supported.contains(annotation.type) else { return nil }

let key = annotation.key ?? annotation.uuid
Expand Down Expand Up @@ -113,7 +113,7 @@ struct AnnotationConverter {
return nil
}

return DocumentAnnotation(
return PdfDocumentAnnotation(
key: key,
type: type,
page: page,
Expand Down Expand Up @@ -191,7 +191,7 @@ struct AnnotationConverter {
) -> [PSPDFKit.Annotation] {
return items.map({ item in
return self.annotation(
from: DatabaseAnnotation(item: item),
from: PdfDatabaseAnnotation(item: item),
type: type,
interfaceStyle: interfaceStyle,
currentUserId: currentUserId,
Expand All @@ -204,7 +204,7 @@ struct AnnotationConverter {
}

static func annotation(
from zoteroAnnotation: DatabaseAnnotation,
from zoteroAnnotation: PdfDatabaseAnnotation,
type: Kind,
interfaceStyle: UIUserInterfaceStyle,
currentUserId: Int,
Expand Down Expand Up @@ -260,7 +260,7 @@ struct AnnotationConverter {

/// Creates corresponding `SquareAnnotation`.
/// - parameter annotation: Zotero annotation.
private static func areaAnnotation(from annotation: Annotation, type: Kind, color: UIColor, boundingBoxConverter: AnnotationBoundingBoxConverter) -> PSPDFKit.SquareAnnotation {
private static func areaAnnotation(from annotation: PdfAnnotation, type: Kind, color: UIColor, boundingBoxConverter: AnnotationBoundingBoxConverter) -> PSPDFKit.SquareAnnotation {
let square: PSPDFKit.SquareAnnotation
switch type {
case .export:
Expand All @@ -280,7 +280,7 @@ struct AnnotationConverter {
/// Creates corresponding `HighlightAnnotation`.
/// - parameter annotation: Zotero annotation.
private static func highlightAnnotation(
from annotation: Annotation,
from annotation: PdfAnnotation,
type: Kind,
color: UIColor,
alpha: CGFloat,
Expand All @@ -305,7 +305,7 @@ struct AnnotationConverter {

/// Creates corresponding `NoteAnnotation`.
/// - parameter annotation: Zotero annotation.
private static func noteAnnotation(from annotation: Annotation, type: Kind, color: UIColor, boundingBoxConverter: AnnotationBoundingBoxConverter) -> PSPDFKit.NoteAnnotation {
private static func noteAnnotation(from annotation: PdfAnnotation, type: Kind, color: UIColor, boundingBoxConverter: AnnotationBoundingBoxConverter) -> PSPDFKit.NoteAnnotation {
let note: PSPDFKit.NoteAnnotation
switch type {
case .export:
Expand All @@ -323,7 +323,7 @@ struct AnnotationConverter {
return note
}

private static func inkAnnotation(from annotation: Annotation, type: Kind, color: UIColor, boundingBoxConverter: AnnotationBoundingBoxConverter) -> PSPDFKit.InkAnnotation {
private static func inkAnnotation(from annotation: PdfAnnotation, type: Kind, color: UIColor, boundingBoxConverter: AnnotationBoundingBoxConverter) -> PSPDFKit.InkAnnotation {
let lines = annotation.paths(boundingBoxConverter: boundingBoxConverter).map({ group in
return group.map({ DrawingPoint(cgPoint: $0) })
})
Expand Down
10 changes: 5 additions & 5 deletions Zotero/Controllers/Citation/CitationController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class CitationController: NSObject {
}
itemsData.append(data)
}
return WKWebView.encodeAsJSONForJavascript(itemsData)
return WebViewEncoder.encodeAsJSONForJavascript(itemsData)
}

/// Bibliography happens once for selected item(s). Appropriate style and locale xmls are loaded, webView is initialized and loaded with index.html. When everything is loaded,
Expand Down Expand Up @@ -266,7 +266,7 @@ class CitationController: NSObject {
let localeData = try Data(contentsOf: localeUrl)
let styleData = try self.fileStorage.read(Files.style(filename: styleFilename))

subscriber(.success((WKWebView.encodeForJavascript(styleData), WKWebView.encodeForJavascript(localeData))))
subscriber(.success((WebViewEncoder.encodeForJavascript(styleData), WebViewEncoder.encodeForJavascript(localeData))))
} catch let error {
DDLogError("CitationController: can't read locale or style - \(error)")
subscriber(.failure(Error.styleOrLocaleMissing))
Expand Down Expand Up @@ -302,7 +302,7 @@ class CitationController: NSObject {
return Disposables.create()
}

subscriber(.success(WKWebView.encodeForJavascript(schemaData)))
subscriber(.success(WebViewEncoder.encodeForJavascript(schemaData)))

return Disposables.create()
}
Expand All @@ -323,7 +323,7 @@ class CitationController: NSObject {

items.first?.realm?.invalidate()

subscriber(.success(WKWebView.encodeAsJSONForJavascript(data)))
subscriber(.success(WebViewEncoder.encodeAsJSONForJavascript(data)))
} catch let error {
DDLogError("CitationController: can't read items - \(error)")
subscriber(.failure(error))
Expand Down Expand Up @@ -498,7 +498,7 @@ extension CitationController: WKScriptMessageHandler {

case .csl:
if let csl = jsResult as? [[String: Any]] {
result = .success(WKWebView.encodeAsJSONForJavascript(csl))
result = .success(WebViewEncoder.encodeAsJSONForJavascript(csl))
} else {
DDLogError("CitationController: CSL got unknown response - \(jsResult)")
result = .failure(Error.missingResponse)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//
// CreateHtmlEpubAnnotationsDbRequest.swift
// Zotero
//
// Created by Michal Rentka on 28.09.2023.
// Copyright © 2023 Corporation for Digital Scholarship. All rights reserved.
//

import Foundation

import RealmSwift

struct CreateHtmlEpubAnnotationsDbRequest: DbRequest {
let attachmentKey: String
let libraryId: LibraryIdentifier
let annotations: [HtmlEpubAnnotation]
let userId: Int

unowned let schemaController: SchemaController

var needsWrite: Bool { return true }

func process(in database: Realm) throws {
guard let parent = database.objects(RItem.self).filter(.key(self.attachmentKey, in: self.libraryId)).first else { return }

for annotation in self.annotations {
self.create(annotation: annotation, parent: parent, in: database)
}
}

private func create(annotation: HtmlEpubAnnotation, parent: RItem, in database: Realm) {
let item: RItem

if let _item = database.objects(RItem.self).filter(.key(annotation.key, in: self.libraryId)).first {
if !_item.deleted {
// If item exists and is not deleted locally, we can ignore this request
return
}

// If item exists and was already deleted locally and not yet synced, we re-add the item
item = _item
item.deleted = false
} else {
// If item didn't exist, create it
item = RItem()
item.key = annotation.key
item.rawType = ItemTypes.annotation
item.localizedType = self.schemaController.localized(itemType: ItemTypes.annotation) ?? ""
item.libraryId = self.libraryId
item.dateAdded = annotation.dateCreated
database.add(item)
}

item.syncState = .synced
item.changeType = .user
item.htmlFreeContent = annotation.comment.isEmpty ? nil : annotation.comment.strippedRichTextTags
item.dateModified = annotation.dateModified
item.parent = parent

if annotation.isAuthor {
item.createdBy = database.object(ofType: RUser.self, forPrimaryKey: self.userId)
}

// We need to submit tags on creation even if they are empty, so we need to mark them as changed
self.addFields(for: annotation, to: item, database: database)
let changes: RItemChanges = [.parent, .fields, .type, .tags]
item.changes.append(RObjectChange.create(changes: changes))
}

private func addFields(for annotation: HtmlEpubAnnotation, to item: RItem, database: Realm) {
for field in FieldKeys.Item.Annotation.allHtmlEpubFields(for: annotation.type) {
let rField = RItemField()
rField.key = field.key
rField.baseKey = field.baseKey
rField.changed = true

switch (field.key, field.baseKey) {
case (FieldKeys.Item.Annotation.type, nil):
rField.value = annotation.type.rawValue

case (FieldKeys.Item.Annotation.color, nil):
rField.value = annotation.color

case (FieldKeys.Item.Annotation.comment, nil):
rField.value = annotation.comment

case (FieldKeys.Item.Annotation.pageLabel, nil):
rField.value = annotation.pageLabel

case (FieldKeys.Item.Annotation.sortIndex, nil):
rField.value = annotation.sortIndex
item.annotationSortIndex = annotation.sortIndex

case (FieldKeys.Item.Annotation.text, nil):
rField.value = annotation.text ?? ""

case (FieldKeys.Item.Annotation.Position.htmlEpubType, FieldKeys.Item.Annotation.position):
guard let value = annotation.position[FieldKeys.Item.Annotation.Position.htmlEpubType] as? String else { continue }
rField.value = value

case (FieldKeys.Item.Annotation.Position.htmlEpubValue, FieldKeys.Item.Annotation.position):
guard let value = annotation.position[FieldKeys.Item.Annotation.Position.htmlEpubValue] as? String else { continue }
rField.value = value

default: break
}

item.fields.append(rField)
}
}

private func addTags(for annotation: HtmlEpubAnnotation, to item: RItem, database: Realm) {
let allTags = database.objects(RTag.self)

for tag in annotation.tags {
guard let rTag = allTags.filter(.name(tag.name)).first else { continue }

let rTypedTag = RTypedTag()
rTypedTag.type = .manual
database.add(rTypedTag)

rTypedTag.item = item
rTypedTag.tag = rTag
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// CreateAnnotationsDbRequest.swift
// CreatePdfAnnotationsDbRequest.swift
// Zotero
//
// Created by Michal Rentka on 31.08.2022.
Expand All @@ -10,10 +10,10 @@ import UIKit

import RealmSwift

struct CreateAnnotationsDbRequest: DbRequest {
struct CreatePdfAnnotationsDbRequest: DbRequest {
let attachmentKey: String
let libraryId: LibraryIdentifier
let annotations: [DocumentAnnotation]
let annotations: [PdfDocumentAnnotation]
let userId: Int

unowned let schemaController: SchemaController
Expand All @@ -29,7 +29,7 @@ struct CreateAnnotationsDbRequest: DbRequest {
}
}

private func create(annotation: DocumentAnnotation, parent: RItem, in database: Realm) {
private func create(annotation: PdfDocumentAnnotation, parent: RItem, in database: Realm) {
let item: RItem

if let _item = database.objects(RItem.self).filter(.key(annotation.key, in: self.libraryId)).first {
Expand Down Expand Up @@ -70,8 +70,8 @@ struct CreateAnnotationsDbRequest: DbRequest {
item.changes.append(RObjectChange.create(changes: changes))
}

private func addFields(for annotation: Annotation, to item: RItem, database: Realm) {
for field in FieldKeys.Item.Annotation.allFields(for: annotation.type) {
private func addFields(for annotation: PdfDocumentAnnotation, to item: RItem, database: Realm) {
for field in FieldKeys.Item.Annotation.allPdfFields(for: annotation.type) {
let rField = RItemField()
rField.key = field.key
rField.baseKey = field.baseKey
Expand Down Expand Up @@ -110,7 +110,7 @@ struct CreateAnnotationsDbRequest: DbRequest {
private func add(rects: [CGRect], to item: RItem, changes: inout RItemChanges, database: Realm) {
guard !rects.isEmpty else { return }

let page = UInt(DatabaseAnnotation(item: item).page)
let page = UInt(PdfDatabaseAnnotation(item: item).page)

for rect in rects {
let dbRect = self.boundingBoxConverter.convertToDb(rect: rect, page: page) ?? rect
Expand All @@ -128,7 +128,7 @@ struct CreateAnnotationsDbRequest: DbRequest {
private func add(paths: [[CGPoint]], to item: RItem, changes: inout RItemChanges, database: Realm) {
guard !paths.isEmpty else { return }

let page = UInt(DatabaseAnnotation(item: item).page)
let page = UInt(PdfDatabaseAnnotation(item: item).page)

for (idx, path) in paths.enumerated() {
let rPath = RPath()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct EditAnnotationPathsDbRequest: DbRequest {

func process(in database: Realm) throws {
guard let item = database.objects(RItem.self).filter(.key(self.key, in: self.libraryId)).first else { return }
let page = UInt(DatabaseAnnotation(item: item).page)
let page = UInt(PdfDatabaseAnnotation(item: item).page)
let dbPaths = self.paths.map { path in
return path.map({ self.boundingBoxConverter.convertToDb(point: $0, page: page) ?? $0 })
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct EditAnnotationRectsDbRequest: DbRequest {

func process(in database: Realm) throws {
guard let item = database.objects(RItem.self).filter(.key(self.key, in: self.libraryId)).first else { return }
let page = UInt(DatabaseAnnotation(item: item).page)
let page = UInt(PdfDatabaseAnnotation(item: item).page)
let dbRects = self.rects.map({ self.boundingBoxConverter.convertToDb(rect: $0, page: page) ?? $0 })
guard self.rects(dbRects, differFrom: item.rects) else { return }
self.sync(rects: dbRects, in: item, database: database)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ struct EditItemFieldsDbRequest: DbRequest {
if didChange {
item.changes.append(RObjectChange.create(changes: RItemChanges.fields))
item.changeType = .user
item.dateModified = Date()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ struct EditTagsForItemDbRequest: DbRequest {
item.rawType = item.rawType
item.changeType = .user
item.changes.append(RObjectChange.create(changes: RItemChanges.tags))
item.dateModified = Date()
}
}
}
Loading
Loading