diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 966c3e0..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c3f4dc3..0000000 --- a/.travis.yml +++ /dev/null @@ -1 +0,0 @@ -language: objective-c diff --git a/README.md b/README.md deleted file mode 100644 index 17ce999..0000000 --- a/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Rebuilt from ground up using swift, added pagination, highlights, bookmarks. - - - -What happens when you load an Epub into a UITextView, look into the ** ![Experimental Branch](https://github.com/zeroCoder1/Epub-Reader/tree/Experimental) - -# Epub-Reader -[![Build Status](https://travis-ci.org/zeroCoder1/Epub-Reader.svg?branch=master)](https://travis-ci.org/zeroCoder1/Epub-Reader) - -Reads an ePub document for iPad/ iPhone - -This is a very simple ePub Reader that will read some ePub files by parsing xml file. - -Just drop in a ePub 3.0 file into the resources and should get it parsed and displayed to you. - - - - - -It is better to download this ePub -http://code.google.com/p/epub-revision/downloads/detail?name=9780316000000_MobyDick_r2.epub -and add it to the project. - -More on Epub 3.0 features here - -http://idpf.org/epub/30 - -Additional details. - - * Swipe to change chapters or tap on next and previous buttons. - * There are text font size increase and decrese methods. - * Day/Night reading Mode. - * Search text on a current page. - * Orientation support. - * Minimal Pagination. -- Once the scrollstops, have to press the next button. - -The app will rememeber the size of the text and mode selected. - - - - If you add any enhancements to the existing code, please do those changes here as well. - - -## GitAds Sponsored -[![Sponsored by GitAds](https://gitads.dev/v1/ad-serve?source=zerocoder1/epub-reader@github)](https://gitads.dev/v1/ad-track?source=zerocoder1/epub-reader@github) - diff --git a/images/checker.png b/images/checker.png new file mode 100644 index 0000000..ab14540 Binary files /dev/null and b/images/checker.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..a134cb3 --- /dev/null +++ b/index.html @@ -0,0 +1,47 @@ + + + + + + Epub-reader by zeroCoder1 + + + + + + + + + +
+
+

Epub-reader

+

Reads an ePub document

+

View the Project on GitHub zeroCoder1/Epub-Reader

+ +
+
+

Hello World.

+ +

This is a sample ePub reader. I have added an CSS3 ePub. +Although the commit description says deleted ePub.. :P

+ +

It might not be a great code to reuse. But you can surely learn how an ePub is rendered.

+ +

Thanks, +zer01

+
+
+ + + + \ No newline at end of file diff --git a/javascripts/scale.fix.js b/javascripts/scale.fix.js new file mode 100644 index 0000000..08716c0 --- /dev/null +++ b/javascripts/scale.fix.js @@ -0,0 +1,20 @@ +fixScale = function(doc) { + + var addEvent = 'addEventListener', + type = 'gesturestart', + qsa = 'querySelectorAll', + scales = [1, 1], + meta = qsa in doc ? doc[qsa]('meta[name=viewport]') : []; + + function fix() { + meta.content = 'width=device-width,minimum-scale=' + scales[0] + ',maximum-scale=' + scales[1]; + doc.removeEventListener(type, fix, true); + } + + if ((meta = meta[meta.length - 1]) && addEvent in doc) { + fix(); + scales = [.25, 1.6]; + doc[addEvent](type, fix, true); + } + +}; \ No newline at end of file diff --git a/params.json b/params.json new file mode 100644 index 0000000..6b75475 --- /dev/null +++ b/params.json @@ -0,0 +1 @@ +{"name":"Epub-reader","body":"#### Hello World.\r\n\r\nThis is a sample ePub reader. I have added an CSS3 ePub.\r\nAlthough the commit description says deleted ePub.. :P\r\n\r\nIt might not be a great code to reuse. But you can surely learn how an ePub is rendered. \r\n\r\nThanks,\r\nzer01","tagline":"Reads an ePub document","google":"","note":"Don't delete this file! It's used internally to help with page regeneration."} \ No newline at end of file diff --git a/stylesheets/pygment_trac.css b/stylesheets/pygment_trac.css new file mode 100644 index 0000000..1926cfd --- /dev/null +++ b/stylesheets/pygment_trac.css @@ -0,0 +1,60 @@ +.highlight .hll { background-color: #49483e } +.highlight { background: #3A3C42; color: #f8f8f2 } +.highlight .c { color: #75715e } /* Comment */ +.highlight .err { color: #960050; background-color: #1e0010 } /* Error */ +.highlight .k { color: #66d9ef } /* Keyword */ +.highlight .l { color: #ae81ff } /* Literal */ +.highlight .n { color: #f8f8f2 } /* Name */ +.highlight .o { color: #f92672 } /* Operator */ +.highlight .p { color: #f8f8f2 } /* Punctuation */ +.highlight .cm { color: #75715e } /* Comment.Multiline */ +.highlight .cp { color: #75715e } /* Comment.Preproc */ +.highlight .c1 { color: #75715e } /* Comment.Single */ +.highlight .cs { color: #75715e } /* Comment.Special */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .kc { color: #66d9ef } /* Keyword.Constant */ +.highlight .kd { color: #66d9ef } /* Keyword.Declaration */ +.highlight .kn { color: #f92672 } /* Keyword.Namespace */ +.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ +.highlight .kr { color: #66d9ef } /* Keyword.Reserved */ +.highlight .kt { color: #66d9ef } /* Keyword.Type */ +.highlight .ld { color: #e6db74 } /* Literal.Date */ +.highlight .m { color: #ae81ff } /* Literal.Number */ +.highlight .s { color: #e6db74 } /* Literal.String */ +.highlight .na { color: #a6e22e } /* Name.Attribute */ +.highlight .nb { color: #f8f8f2 } /* Name.Builtin */ +.highlight .nc { color: #a6e22e } /* Name.Class */ +.highlight .no { color: #66d9ef } /* Name.Constant */ +.highlight .nd { color: #a6e22e } /* Name.Decorator */ +.highlight .ni { color: #f8f8f2 } /* Name.Entity */ +.highlight .ne { color: #a6e22e } /* Name.Exception */ +.highlight .nf { color: #a6e22e } /* Name.Function */ +.highlight .nl { color: #f8f8f2 } /* Name.Label */ +.highlight .nn { color: #f8f8f2 } /* Name.Namespace */ +.highlight .nx { color: #a6e22e } /* Name.Other */ +.highlight .py { color: #f8f8f2 } /* Name.Property */ +.highlight .nt { color: #f92672 } /* Name.Tag */ +.highlight .nv { color: #f8f8f2 } /* Name.Variable */ +.highlight .ow { color: #f92672 } /* Operator.Word */ +.highlight .w { color: #f8f8f2 } /* Text.Whitespace */ +.highlight .mf { color: #ae81ff } /* Literal.Number.Float */ +.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ +.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ +.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ +.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ +.highlight .sc { color: #e6db74 } /* Literal.String.Char */ +.highlight .sd { color: #e6db74 } /* Literal.String.Doc */ +.highlight .s2 { color: #e6db74 } /* Literal.String.Double */ +.highlight .se { color: #ae81ff } /* Literal.String.Escape */ +.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ +.highlight .si { color: #e6db74 } /* Literal.String.Interpol */ +.highlight .sx { color: #e6db74 } /* Literal.String.Other */ +.highlight .sr { color: #e6db74 } /* Literal.String.Regex */ +.highlight .s1 { color: #e6db74 } /* Literal.String.Single */ +.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ +.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ +.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ +.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ +.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/stylesheets/styles.css b/stylesheets/styles.css new file mode 100644 index 0000000..1d14024 --- /dev/null +++ b/stylesheets/styles.css @@ -0,0 +1,363 @@ +@import url(https://fonts.googleapis.com/css?family=Lato:300italic,700italic,300,700); +html { + background: #6C7989; + background: #6c7989 -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #6c7989), color-stop(100%, #434b55)) fixed; + background: #6c7989 -webkit-linear-gradient(#6c7989, #434b55) fixed; + background: #6c7989 -moz-linear-gradient(#6c7989, #434b55) fixed; + background: #6c7989 -o-linear-gradient(#6c7989, #434b55) fixed; + background: #6c7989 -ms-linear-gradient(#6c7989, #434b55) fixed; + background: #6c7989 linear-gradient(#6c7989, #434b55) fixed; +} + +body { + padding: 50px 0; + margin: 0; + font: 14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #555; + font-weight: 300; + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAeCAYAAABNChwpAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAAAUdEVYdENyZWF0aW9uIFRpbWUAMy82LzEygrTcTAAAAFRJREFUSIljfPDggZRf5RIGGNjUHsNATz6jXmSL1Kb2GLiAX+USBnrymRgGGDCORgFmoNAXjEbBaBSMRsFoFIxGwWgUjEbBaBSMRsFoFIxGwWgUAABYNujumib3wAAAAABJRU5ErkJggg==') fixed; +} + +.wrapper { + width: 640px; + margin: 0 auto; + background: #DEDEDE; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + -o-border-radius: 8px; + -ms-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 0 0 1px, rgba(0, 0, 0, 0.45) 0 3px 10px; + -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 0 0 1px, rgba(0, 0, 0, 0.45) 0 3px 10px; + -o-box-shadow: rgba(0, 0, 0, 0.2) 0 0 0 1px, rgba(0, 0, 0, 0.45) 0 3px 10px; + box-shadow: rgba(0, 0, 0, 0.2) 0 0 0 1px, rgba(0, 0, 0, 0.45) 0 3px 10px; +} + +header, section, footer { + display: block; +} + +a { + color: #069; + text-decoration: none; +} + +p { + margin: 0 0 20px; + padding: 0; +} + +strong { + color: #222; + font-weight: 700; +} + +header { + -moz-border-radius: 8px 8px 0 0; + -webkit-border-radius: 8px 8px 0 0; + -o-border-radius: 8px 8px 0 0; + -ms-border-radius: 8px 8px 0 0; + -khtml-border-radius: 8px 8px 0 0; + border-radius: 8px 8px 0 0; + background: #C6EAFA; + background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ddfbfc), color-stop(100%, #c6eafa)); + background: -webkit-linear-gradient(#ddfbfc, #c6eafa); + background: -moz-linear-gradient(#ddfbfc, #c6eafa); + background: -o-linear-gradient(#ddfbfc, #c6eafa); + background: -ms-linear-gradient(#ddfbfc, #c6eafa); + background: linear-gradient(#ddfbfc, #c6eafa); + position: relative; + padding: 15px 20px; + border-bottom: 1px solid #B2D2E1; +} +header h1 { + margin: 0; + padding: 0; + font-size: 24px; + line-height: 1.2; + color: #069; + text-shadow: rgba(255, 255, 255, 0.9) 0 1px 0; +} +header.without-description h1 { + margin: 10px 0; +} +header p { + margin: 0; + color: #61778B; + width: 300px; + font-size: 13px; +} +header p.view { + display: none; + font-weight: 700; + text-shadow: rgba(255, 255, 255, 0.9) 0 1px 0; + -webkit-font-smoothing: antialiased; +} +header p.view a { + color: #06c; +} +header p.view small { + font-weight: 400; +} +header ul { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + z-index: 1; + right: 20px; + top: 20px; + height: 38px; + padding: 1px 0; + background: #5198DF; + background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #77b9fb), color-stop(100%, #3782cd)); + background: -webkit-linear-gradient(#77b9fb, #3782cd); + background: -moz-linear-gradient(#77b9fb, #3782cd); + background: -o-linear-gradient(#77b9fb, #3782cd); + background: -ms-linear-gradient(#77b9fb, #3782cd); + background: linear-gradient(#77b9fb, #3782cd); + border-radius: 5px; + -moz-box-shadow: inset rgba(255, 255, 255, 0.45) 0 1px 0, inset rgba(0, 0, 0, 0.2) 0 -1px 0; + -webkit-box-shadow: inset rgba(255, 255, 255, 0.45) 0 1px 0, inset rgba(0, 0, 0, 0.2) 0 -1px 0; + -o-box-shadow: inset rgba(255, 255, 255, 0.45) 0 1px 0, inset rgba(0, 0, 0, 0.2) 0 -1px 0; + box-shadow: inset rgba(255, 255, 255, 0.45) 0 1px 0, inset rgba(0, 0, 0, 0.2) 0 -1px 0; + width: 240px; +} +header ul:before { + content: ''; + position: absolute; + z-index: -1; + left: -5px; + top: -4px; + right: -5px; + bottom: -6px; + background: rgba(0, 0, 0, 0.1); + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + -o-border-radius: 8px; + -ms-border-radius: 8px; + -khtml-border-radius: 8px; + border-radius: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0, inset rgba(255, 255, 255, 0.7) 0 -1px 0; + -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0, inset rgba(255, 255, 255, 0.7) 0 -1px 0; + -o-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0, inset rgba(255, 255, 255, 0.7) 0 -1px 0; + box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0, inset rgba(255, 255, 255, 0.7) 0 -1px 0; +} +header ul li { + width: 79px; + float: left; + border-right: 1px solid #3A7CBE; + height: 38px; +} +header ul li + li { + width: 78px; + border-left: 1px solid #8BBEF3; +} +header ul li + li + li { + border-right: none; + width: 79px; +} +header ul a { + line-height: 1; + font-size: 11px; + color: #fff; + color: rgba(255, 255, 255, 0.8); + display: block; + text-align: center; + font-weight: 400; + padding-top: 6px; + height: 40px; + text-shadow: rgba(0, 0, 0, 0.4) 0 -1px 0; +} +header ul a strong { + font-size: 14px; + display: block; + color: #fff; + -webkit-font-smoothing: antialiased; +} + +section { + padding: 15px 20px; + font-size: 15px; + border-top: 1px solid #fff; + background: -webkit-gradient(linear, 50% 0%, 50% 700, color-stop(0%, #fafafa), color-stop(100%, #dedede)); + background: -webkit-linear-gradient(#fafafa, #dedede 700px); + background: -moz-linear-gradient(#fafafa, #dedede 700px); + background: -o-linear-gradient(#fafafa, #dedede 700px); + background: -ms-linear-gradient(#fafafa, #dedede 700px); + background: linear-gradient(#fafafa, #dedede 700px); + -moz-border-radius: 0 0 8px 8px; + -webkit-border-radius: 0 0 8px 8px; + -o-border-radius: 0 0 8px 8px; + -ms-border-radius: 0 0 8px 8px; + -khtml-border-radius: 0 0 8px 8px; + border-radius: 0 0 8px 8px; + position: relative; +} + +h1, h2, h3, h4, h5, h6 { + color: #222; + padding: 0; + margin: 0 0 20px; + line-height: 1.2; +} + +p, ul, ol, table, pre, dl { + margin: 0 0 20px; +} + +h1, h2, h3 { + line-height: 1.1; +} + +h1 { + font-size: 28px; +} + +h2 { + color: #393939; +} + +h3, h4, h5, h6 { + color: #494949; +} + +blockquote { + margin: 0 -20px 20px; + padding: 15px 20px 1px 40px; + font-style: italic; + background: #ccc; + background: rgba(0, 0, 0, 0.06); + color: #222; +} + +img { + max-width:100%; +} + +code, pre { + font-family: Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; + color: #333; + font-size: 12px; +} + +pre { + padding: 20px; + background: #3A3C42; + color: #f8f8f2; + margin: 0 -20px 20px; + overflow-x:auto; +} +pre code { + color: #f8f8f2; +} +li pre { + margin-left: -60px; + padding-left: 60px; +} + +table { + width: 100%; + border-collapse: collapse; +} + +th, td { + text-align: left; + padding: 5px 10px; + border-bottom: 1px solid #aaa; +} + +dt { + color: #222; + font-weight: 700; +} + +th { + color: #222; +} + +small { + font-size: 11px; +} + +hr { + border: 0; + background: #aaa; + height: 1px; + margin: 0 0 20px; +} + +footer { + width: 640px; + margin: 0 auto; + padding: 20px 0 0; + color: #ccc; + overflow: hidden; +} +footer a { + color: #fff; + font-weight: bold; +} +footer p { + float: left; +} +footer p + p { + float: right; +} + +@media print, screen and (max-width: 740px) { + body { + padding: 0; + } + + .wrapper { + -moz-border-radius: 0; + -webkit-border-radius: 0; + -o-border-radius: 0; + -ms-border-radius: 0; + -khtml-border-radius: 0; + border-radius: 0; + -moz-box-shadow: none; + -webkit-box-shadow: none; + -o-box-shadow: none; + box-shadow: none; + width: 100%; + } + + footer { + -moz-border-radius: 0; + -webkit-border-radius: 0; + -o-border-radius: 0; + -ms-border-radius: 0; + -khtml-border-radius: 0; + border-radius: 0; + padding: 20px; + width: auto; + } + footer p { + float: none; + margin: 0; + } + footer p + p { + float: none; + } +} +@media print, screen and (max-width:580px) { + header ul { + display: none; + } + + header p.view { + display: block; + } + + header p { + width: 100%; + } +} +@media print { + header p.view a small:before { + content: 'at http://github.com/'; + } +} diff --git a/testReader.xcodeproj/project.pbxproj b/testReader.xcodeproj/project.pbxproj deleted file mode 100644 index a7d92ee..0000000 --- a/testReader.xcodeproj/project.pbxproj +++ /dev/null @@ -1,387 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 39DBA78A2D80A8EA001F1C5B /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 39DBA7892D80A8EA001F1C5B /* SwiftSoup */; }; - 39DBA78D2D80A917001F1C5B /* ZipArchive in Frameworks */ = {isa = PBXBuildFile; productRef = 39DBA78C2D80A917001F1C5B /* ZipArchive */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 39DBA7702D80A8B5001F1C5B /* testReader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testReader.app; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 39DBA7822D80A8B7001F1C5B /* Exceptions for "testReader" folder in "testReader" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = 39DBA76F2D80A8B5001F1C5B /* testReader */; - }; -/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 39DBA7722D80A8B5001F1C5B /* testReader */ = { - isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 39DBA7822D80A8B7001F1C5B /* Exceptions for "testReader" folder in "testReader" target */, - ); - path = testReader; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 39DBA76D2D80A8B5001F1C5B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 39DBA78A2D80A8EA001F1C5B /* SwiftSoup in Frameworks */, - 39DBA78D2D80A917001F1C5B /* ZipArchive in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 39DBA7672D80A8B5001F1C5B = { - isa = PBXGroup; - children = ( - 39DBA7722D80A8B5001F1C5B /* testReader */, - 39DBA7712D80A8B5001F1C5B /* Products */, - ); - sourceTree = ""; - }; - 39DBA7712D80A8B5001F1C5B /* Products */ = { - isa = PBXGroup; - children = ( - 39DBA7702D80A8B5001F1C5B /* testReader.app */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 39DBA76F2D80A8B5001F1C5B /* testReader */ = { - isa = PBXNativeTarget; - buildConfigurationList = 39DBA7832D80A8B7001F1C5B /* Build configuration list for PBXNativeTarget "testReader" */; - buildPhases = ( - 39DBA76C2D80A8B5001F1C5B /* Sources */, - 39DBA76D2D80A8B5001F1C5B /* Frameworks */, - 39DBA76E2D80A8B5001F1C5B /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 39DBA7722D80A8B5001F1C5B /* testReader */, - ); - name = testReader; - packageProductDependencies = ( - 39DBA7892D80A8EA001F1C5B /* SwiftSoup */, - 39DBA78C2D80A917001F1C5B /* ZipArchive */, - ); - productName = testReader; - productReference = 39DBA7702D80A8B5001F1C5B /* testReader.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 39DBA7682D80A8B5001F1C5B /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1620; - LastUpgradeCheck = 1620; - TargetAttributes = { - 39DBA76F2D80A8B5001F1C5B = { - CreatedOnToolsVersion = 16.2; - }; - }; - }; - buildConfigurationList = 39DBA76B2D80A8B5001F1C5B /* Build configuration list for PBXProject "testReader" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 39DBA7672D80A8B5001F1C5B; - minimizedProjectReferenceProxies = 1; - packageReferences = ( - 39DBA7882D80A8EA001F1C5B /* XCRemoteSwiftPackageReference "SwiftSoup" */, - 39DBA78B2D80A917001F1C5B /* XCRemoteSwiftPackageReference "ZipArchive" */, - ); - preferredProjectObjectVersion = 77; - productRefGroup = 39DBA7712D80A8B5001F1C5B /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 39DBA76F2D80A8B5001F1C5B /* testReader */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 39DBA76E2D80A8B5001F1C5B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 39DBA76C2D80A8B5001F1C5B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 39DBA7842D80A8B7001F1C5B /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 7CSFZ8JM6F; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = testReader/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = Main; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 16.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = design.supr.testReader; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 39DBA7852D80A8B7001F1C5B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 7CSFZ8JM6F; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = testReader/Info.plist; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIMainStoryboardFile = Main; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 16.6; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = design.supr.testReader; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - 39DBA7862D80A8B7001F1C5B /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 39DBA7872D80A8B7001F1C5B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.2; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 39DBA76B2D80A8B5001F1C5B /* Build configuration list for PBXProject "testReader" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 39DBA7862D80A8B7001F1C5B /* Debug */, - 39DBA7872D80A8B7001F1C5B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 39DBA7832D80A8B7001F1C5B /* Build configuration list for PBXNativeTarget "testReader" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 39DBA7842D80A8B7001F1C5B /* Debug */, - 39DBA7852D80A8B7001F1C5B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 39DBA7882D80A8EA001F1C5B /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/scinfu/SwiftSoup.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.8.5; - }; - }; - 39DBA78B2D80A917001F1C5B /* XCRemoteSwiftPackageReference "ZipArchive" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ZipArchive/ZipArchive.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.6.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 39DBA7892D80A8EA001F1C5B /* SwiftSoup */ = { - isa = XCSwiftPackageProductDependency; - package = 39DBA7882D80A8EA001F1C5B /* XCRemoteSwiftPackageReference "SwiftSoup" */; - productName = SwiftSoup; - }; - 39DBA78C2D80A917001F1C5B /* ZipArchive */ = { - isa = XCSwiftPackageProductDependency; - package = 39DBA78B2D80A917001F1C5B /* XCRemoteSwiftPackageReference "ZipArchive" */; - productName = ZipArchive; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = 39DBA7682D80A8B5001F1C5B /* Project object */; -} diff --git a/testReader.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/testReader.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/testReader.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/testReader.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/testReader.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index fd25e78..0000000 --- a/testReader.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,24 +0,0 @@ -{ - "originHash" : "19367b3643418ff79e73fbdbe1b64178c4ecc49c15528ac32025d89f06a0b532", - "pins" : [ - { - "identity" : "swiftsoup", - "kind" : "remoteSourceControl", - "location" : "https://github.com/scinfu/SwiftSoup.git", - "state" : { - "revision" : "64e4daa41b0f22affa94a6063d509c137c26909c", - "version" : "2.8.5" - } - }, - { - "identity" : "ziparchive", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ZipArchive/ZipArchive.git", - "state" : { - "revision" : "df35718ea19a94e015b91dc4881dee028ce4cdba", - "version" : "2.6.0" - } - } - ], - "version" : 3 -} diff --git a/testReader.xcodeproj/project.xcworkspace/xcuserdata/shrutesh.xcuserdatad/UserInterfaceState.xcuserstate b/testReader.xcodeproj/project.xcworkspace/xcuserdata/shrutesh.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 206cd80..0000000 Binary files a/testReader.xcodeproj/project.xcworkspace/xcuserdata/shrutesh.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/testReader.xcodeproj/xcuserdata/shrutesh.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/testReader.xcodeproj/xcuserdata/shrutesh.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist deleted file mode 100644 index dba7422..0000000 --- a/testReader.xcodeproj/xcuserdata/shrutesh.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - diff --git a/testReader.xcodeproj/xcuserdata/shrutesh.xcuserdatad/xcschemes/xcschememanagement.plist b/testReader.xcodeproj/xcuserdata/shrutesh.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index fb61db6..0000000 --- a/testReader.xcodeproj/xcuserdata/shrutesh.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - SchemeUserState - - testReader.xcscheme_^#shared#^_ - - orderHint - 0 - - - - diff --git a/testReader/.DS_Store b/testReader/.DS_Store deleted file mode 100644 index 76b65ac..0000000 Binary files a/testReader/.DS_Store and /dev/null differ diff --git a/testReader/AppDelegate.swift b/testReader/AppDelegate.swift deleted file mode 100644 index cd84948..0000000 --- a/testReader/AppDelegate.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// AppDelegate.swift -// testReader -// -// Created by shrutesh sharma on 11/03/25. -// - -import UIKit - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - window = UIWindow(frame: UIScreen.main.bounds) - let navController = UINavigationController(rootViewController: LibraryViewController()) - window?.rootViewController = navController - window?.makeKeyAndVisible() - return true - } -} - diff --git a/testReader/Assets.xcassets/.DS_Store b/testReader/Assets.xcassets/.DS_Store deleted file mode 100644 index e55472c..0000000 Binary files a/testReader/Assets.xcassets/.DS_Store and /dev/null differ diff --git a/testReader/Assets.xcassets/AccentColor.colorset/Contents.json b/testReader/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb87897..0000000 --- a/testReader/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/testReader/Assets.xcassets/AppIcon.appiconset/Contents.json b/testReader/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 2305880..0000000 --- a/testReader/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/testReader/Assets.xcassets/Contents.json b/testReader/Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/testReader/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/testReader/Base.lproj/LaunchScreen.storyboard b/testReader/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 865e932..0000000 --- a/testReader/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/testReader/Base.lproj/Main.storyboard b/testReader/Base.lproj/Main.storyboard deleted file mode 100644 index 25a7638..0000000 --- a/testReader/Base.lproj/Main.storyboard +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/testReader/EPUBMetadata.swift b/testReader/EPUBMetadata.swift deleted file mode 100644 index b29addd..0000000 --- a/testReader/EPUBMetadata.swift +++ /dev/null @@ -1,102 +0,0 @@ -// -// EPUBMetadata.swift -// testReader -// -// Created by shrutesh sharma on 11/03/25. -// - - -import Foundation - -struct EPUBMetadata { - let title: String - let author: String -} - -struct EPUBSpineItem { - let id: String - let href: String -} - -struct EPUBTOCItem { - let label: String - let href: String -} - -struct Bookmark: Codable { - let spineIndex: Int - let pageNumber: Int - let date: Date - - // Computed property for display in bookmarks list - var displayText: String { - let formatter = DateFormatter() - formatter.dateStyle = .short - formatter.timeStyle = .short - return "Page \(pageNumber + 1) - \(formatter.string(from: date))" - } -} - -struct Highlight: Codable { - let spineIndex: Int - let pageNumber: Int - let text: String - let range: NSRange - let color: String - let date: Date - let textContext: String // Store surrounding text for better matching - let relativePosition: Double // Position as percentage of spine content - - init(spineIndex: Int, pageNumber: Int, text: String, range: NSRange, color: String, textContext: String = "", relativePosition: Double = 0.0) { - self.spineIndex = spineIndex - self.pageNumber = pageNumber - self.text = text - self.range = range - self.color = color - self.date = Date() - self.textContext = textContext - self.relativePosition = relativePosition - } - - // Computed property for display in highlights list - var displayText: String { - return "\(text)" - } - - // Custom encoding for NSRange since it's not Codable by default - enum CodingKeys: String, CodingKey { - case spineIndex, pageNumber, text, range, color, date, textContext, relativePosition - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(spineIndex, forKey: .spineIndex) - try container.encode(pageNumber, forKey: .pageNumber) - try container.encode(text, forKey: .text) - try container.encode(color, forKey: .color) - try container.encode(date, forKey: .date) - try container.encode(textContext, forKey: .textContext) - try container.encode(relativePosition, forKey: .relativePosition) - - // Encode NSRange as location and length - let rangeDict = ["location": range.location, "length": range.length] - try container.encode(rangeDict, forKey: .range) - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - spineIndex = try container.decode(Int.self, forKey: .spineIndex) - pageNumber = try container.decode(Int.self, forKey: .pageNumber) - text = try container.decode(String.self, forKey: .text) - color = try container.decode(String.self, forKey: .color) - date = try container.decode(Date.self, forKey: .date) - textContext = try container.decodeIfPresent(String.self, forKey: .textContext) ?? "" - relativePosition = try container.decodeIfPresent(Double.self, forKey: .relativePosition) ?? 0.0 - - // Decode NSRange from location and length - let rangeDict = try container.decode([String: Int].self, forKey: .range) - let location = rangeDict["location"] ?? 0 - let length = rangeDict["length"] ?? 0 - range = NSRange(location: location, length: length) - } -} diff --git a/testReader/EPUBParser.swift b/testReader/EPUBParser.swift deleted file mode 100644 index 6c569f8..0000000 --- a/testReader/EPUBParser.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// EPUBParser.swift -// testReader -// -// Created by shrutesh sharma on 11/03/25. -// - - -import Foundation -import ZipArchive -import SwiftSoup - -class EPUBParser { - static func parseEPUB(at url: URL) -> (metadata: EPUBMetadata, spine: [EPUBSpineItem], toc: [EPUBTOCItem], baseURL: URL)? { - let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) - do { - try SSZipArchive.unzipFile(atPath: url.path, toDestination: tempDir.path) - let containerURL = tempDir.appendingPathComponent("META-INF/container.xml") - guard let opfPath = parseContainerXML(at: containerURL) else { return nil } - let opfURL = tempDir.appendingPathComponent(opfPath) - guard let (metadata, spine, tocPath) = parseOPFFile(at: opfURL, baseURL: tempDir) else { return nil } - let toc = parseTOCFile(at: tempDir.appendingPathComponent("/OPS"), baseURL: tempDir) - return (metadata, spine, toc, tempDir) - } catch { - print("Error parsing EPUB: \(error)") - return nil - } - } - - private static func parseContainerXML(at url: URL) -> String? { - guard let xmlString = try? String(contentsOf: url), - let doc = try? SwiftSoup.parse(xmlString) else { return nil } - do { - let rootFile = try doc.select("rootfile").first() - return try rootFile?.attr("full-path") - } catch { - print("Error parsing container.xml: \(error)") - return nil - } - } - - private static func parseOPFFile(at url: URL, baseURL: URL) -> (EPUBMetadata, [EPUBSpineItem], String)? { - guard let xmlString = try? String(contentsOf: url), - let doc = try? SwiftSoup.parse(xmlString) else { return nil } - - do { - // Extract metadata by directly targeting tag names - let metadataElement = try doc.select("metadata").first() - let title = try metadataElement?.getElementsByTag("dc:title").first()?.text() ?? "Unknown Title" - let author = try metadataElement?.getElementsByTag("dc:creator").first()?.text() ?? "Unknown Author" - let metadata = EPUBMetadata(title: title, author: author) - - // Extract spine items - var spineItems: [EPUBSpineItem] = [] - let manifestItems = try doc.select("manifest item") - let spineRefs = try doc.select("spine itemref") - - for ref in spineRefs { - let idref = try ref.attr("idref") - var matchingItem: Element? = nil - for item in manifestItems { - if (try? item.attr("id")) == idref { - matchingItem = item - break - } - } - if let item = matchingItem { - let href = try item.attr("href") - spineItems.append(EPUBSpineItem(id: idref, href: href)) - } - } - - // Extract TOC path - let tocID = try doc.select("spine").attr("toc") - var tocItem: Element? = nil - for item in manifestItems { - if (try? item.attr("id")) == tocID { - tocItem = item - break - } - } - let tocPath = try tocItem?.attr("href") ?? "" - - return (metadata, spineItems, tocPath) - } catch { - print("Error parsing .opf file: \(error)") - return nil - } - } - - private static func parseTOCFile(at url: URL, baseURL: URL) -> [EPUBTOCItem] { - guard let xmlString = try? String(contentsOf: url.appending(path: "toc.xhtml")), - let doc = try? SwiftSoup.parse(xmlString) else { return [] } - do { - let navPoints = try doc.select("nav") - var tocItems: [EPUBTOCItem] = [] - for nav in navPoints { - let label = try nav.select("navLabel text").first()?.text() ?? "" - let href = try nav.select("content").attr("src") - tocItems.append(EPUBTOCItem(label: label, href: href)) - } - return tocItems - } catch { - print("Error parsing TOC: \(error)") - return [] - } - } -} diff --git a/testReader/Info.plist b/testReader/Info.plist deleted file mode 100644 index 97ff79d..0000000 --- a/testReader/Info.plist +++ /dev/null @@ -1,11 +0,0 @@ - - - - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - - - diff --git a/testReader/LibraryViewController.swift b/testReader/LibraryViewController.swift deleted file mode 100644 index 112b946..0000000 --- a/testReader/LibraryViewController.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// LibraryViewController.swift -// testReader -// -// Created by shrutesh sharma on 11/03/25. -// - - -import UIKit - -class LibraryViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { - private let tableView = UITableView() - private var epubFiles: [URL] = [] - - override func viewDidLoad() { - super.viewDidLoad() - title = "EPUB Library" - view.backgroundColor = .white - setupNavigationBar() - setupTableView() - loadEpubFiles() - } - - private func setupNavigationBar() { - navigationItem.rightBarButtonItem = UIBarButtonItem( - title: "Settings", - style: .plain, - target: self, - action: #selector(openSettings) - ) - } - - @objc private func openSettings() { - let settingsVC = SettingsViewController() - navigationController?.pushViewController(settingsVC, animated: true) - } - - private func setupTableView() { - view.addSubview(tableView) - tableView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor), - tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor) - ]) - tableView.dataSource = self - tableView.delegate = self - tableView.register(UITableViewCell.self, forCellReuseIdentifier: "EpubCell") - } - - private func loadEpubFiles() { - let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - if let files = try? FileManager.default.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil) { - epubFiles = files.filter { $0.pathExtension.lowercased() == "epub" } - tableView.reloadData() - } - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return epubFiles.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "EpubCell", for: indexPath) - cell.textLabel?.text = epubFiles[indexPath.row].lastPathComponent - return cell - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let readerVC = ReaderViewController(epubURL: epubFiles[indexPath.row]) - navigationController?.pushViewController(readerVC, animated: true) - tableView.deselectRow(at: indexPath, animated: true) - } -} diff --git a/testReader/PageContentViewController.swift b/testReader/PageContentViewController.swift deleted file mode 100644 index 29a19ca..0000000 --- a/testReader/PageContentViewController.swift +++ /dev/null @@ -1,72 +0,0 @@ -// -// PageContentViewController.swift -// testReader -// -// Created by shrutesh sharma on 28/05/25. -// - -import UIKit -import WebKit - -class PageContentViewController: UIViewController { - let webView: WKWebView - let pageIndex: Int - var targetPageIndex: Int = 0 - weak var delegate: ReaderViewController? - - init(webView: WKWebView, pageIndex: Int, delegate: ReaderViewController?) { - self.webView = webView - self.pageIndex = pageIndex - self.targetPageIndex = pageIndex - self.delegate = delegate - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - view.addSubview(webView) - webView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), - webView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - webView.trailingAnchor.constraint(equalTo: view.trailingAnchor) - ]) - - // Enable the menu controller - becomeFirstResponder() - } - - override var canBecomeFirstResponder: Bool { - return true - } - - override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { - if action == #selector(highlightSelectedText) || action == #selector(bookmarkFromMenu) { - return true - } - return super.canPerformAction(action, withSender: sender) - } - - @objc func highlightSelectedText() { - // Forward the action to the delegate (ReaderViewController) - delegate?.highlightSelectedText() - } - - @objc func bookmarkFromMenu() { - // Forward the action to the delegate (ReaderViewController) - delegate?.bookmarkFromMenu() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - // Just set the page index when view appears - no translation needed - if webView.alpha > 0 { - delegate?.scrollToPage(in: webView, pageIndex: targetPageIndex) - } - } -} diff --git a/testReader/ReaderViewController.swift b/testReader/ReaderViewController.swift deleted file mode 100644 index a9d2aba..0000000 --- a/testReader/ReaderViewController.swift +++ /dev/null @@ -1,977 +0,0 @@ -// -// ReaderViewController.swift -// testReader -// -// Created by shrutesh sharma on 11/03/25. -// - -import UIKit -import WebKit -import SwiftSoup - -class ReaderViewController: UIViewController, WKNavigationDelegate, UIPageViewControllerDataSource, UIPageViewControllerDelegate, UITableViewDataSource, UITableViewDelegate { - private var pageViewController: UIPageViewController! - private let epubURL: URL - private var spineItems: [EPUBSpineItem] = [] - private var currentSpineIndex = 0 - private var currentPage = 0 - private var totalPages = 0 - private var totalPagesPerSpine: [Int] = [] // Track pages per chapter - private var baseURL: URL? - private var isPageCurlEnabled = UserDefaults.standard.bool(forKey: "isPageCurlEnabled") - private let pageLabel = UILabel() - private var bookmarks: [Bookmark] = [] - private var highlights: [Highlight] = [] - private let tocTableView = UITableView() - private let highlightsTableView = UITableView() - private var tocItems: [EPUBTOCItem] = [] - private let bookmarksTableView = UITableView() - - init(epubURL: URL) { - self.epubURL = epubURL - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .white - setupNavigationBar() - setupPageLabel() - - // Load data first before parsing EPUB - loadHighlights() - loadBookmarks() - - parseAndLoadEPUB() - setupPageViewController() - setupMenuController() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - setupTOCView() - setupHighlightsView() - setupBookmarksView() - // Data already loaded in viewDidLoad, just reload table views - highlightsTableView.reloadData() - bookmarksTableView.reloadData() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - // Check if page curl setting changed - let newPageCurlSetting = UserDefaults.standard.bool(forKey: "isPageCurlEnabled") - if newPageCurlSetting != isPageCurlEnabled { - isPageCurlEnabled = newPageCurlSetting - // Recreate page view controller with new style - recreatePageViewController() - } - } - - func setupMenuController() { - // Add custom menu items for highlighting and bookmarking - let highlightMenuItem = UIMenuItem(title: "Highlight", action: #selector(highlightSelectedText)) - let bookmarkMenuItem = UIMenuItem(title: "Bookmark", action: #selector(bookmarkFromMenu)) - UIMenuController.shared.menuItems = [highlightMenuItem, bookmarkMenuItem] - } - - @objc func highlightSelectedText() { - guard let currentPageVC = pageViewController.viewControllers?.first as? PageContentViewController else { - return - } - - // Get the highlight color from settings - let highlightColor = UserDefaults.standard.string(forKey: "highlightColor") ?? "yellow" - - let webView = currentPageVC.webView - let highlightJS = """ - (function() { - var selection = window.getSelection(); - if (selection.rangeCount > 0) { - var range = selection.getRangeAt(0); - var span = document.createElement('span'); - span.className = 'highlight'; - span.style.backgroundColor = '\(highlightColor)'; - span.style.color = 'black'; - - var selectedText = selection.toString(); - - // Get context around the selection for better matching - var textContent = document.body.textContent || document.body.innerText; - var selectedIndex = textContent.indexOf(selectedText); - var contextStart = Math.max(0, selectedIndex - 50); - var contextEnd = Math.min(textContent.length, selectedIndex + selectedText.length + 50); - var textContext = textContent.substring(contextStart, contextEnd); - - // Calculate relative position in the document - var relativePosition = selectedIndex / textContent.length; - - try { - range.surroundContents(span); - selection.removeAllRanges(); - - // Return the highlighted data to Swift - return { - text: selectedText, - context: textContext, - position: relativePosition - }; - } catch(e) { - var contents = range.extractContents(); - span.appendChild(contents); - range.insertNode(span); - selection.removeAllRanges(); - - // Return the highlighted data to Swift - return { - text: selectedText, - context: textContext, - position: relativePosition - }; - } - } - return null; - })(); - """ - - webView.evaluateJavaScript(highlightJS) { result, error in - if let result = result as? [String: Any], - let text = result["text"] as? String, - let context = result["context"] as? String, - let position = result["position"] as? Double, - !text.isEmpty { - - let highlight = Highlight( - spineIndex: self.currentSpineIndex, - pageNumber: self.currentPage, - text: text, - range: NSRange(location: 0, length: text.count), - color: highlightColor, - textContext: context, - relativePosition: position - ) - - self.highlights.append(highlight) - self.saveHighlights() - print("Highlight saved: \(text) with color: \(highlightColor)") - } - } - } - - private func recreatePageViewController() { - // Remove old page view controller - pageViewController.willMove(toParent: nil) - pageViewController.view.removeFromSuperview() - pageViewController.removeFromParent() - - // Create new one with updated settings - setupPageViewController() - } - - private func setupPageViewController() { - let style: UIPageViewController.TransitionStyle = isPageCurlEnabled ? .pageCurl : .scroll - pageViewController = UIPageViewController(transitionStyle: style, navigationOrientation: .horizontal, options: nil) - pageViewController.dataSource = self - pageViewController.delegate = self - addChild(pageViewController) - view.addSubview(pageViewController.view) - pageViewController.view.frame = view.bounds - pageViewController.view.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - pageViewController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - pageViewController.view.bottomAnchor.constraint(equalTo: pageLabel.topAnchor, constant: -10), - pageViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), - pageViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) - ]) - // Load the initial page - if let initialPage = createPageViewController(for: currentPage) { - pageViewController.setViewControllers([initialPage], direction: .forward, animated: false) - } - } - - private func setupTOCView() { - tocTableView.isHidden = true - tocTableView.dataSource = self - tocTableView.delegate = self - tocTableView.register(UITableViewCell.self, forCellReuseIdentifier: "TOCCell") - view.addSubview(tocTableView) - tocTableView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - tocTableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - tocTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor), - tocTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - tocTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor) - ]) - } - - private func setupHighlightsView() { - highlightsTableView.isHidden = true - highlightsTableView.dataSource = self - highlightsTableView.delegate = self - highlightsTableView.register(UITableViewCell.self, forCellReuseIdentifier: "HighlightCell") - view.addSubview(highlightsTableView) - highlightsTableView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - highlightsTableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - highlightsTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor), - highlightsTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - highlightsTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor) - ]) - } - - private func setupBookmarksView() { - bookmarksTableView.isHidden = true - bookmarksTableView.dataSource = self - bookmarksTableView.delegate = self - bookmarksTableView.register(UITableViewCell.self, forCellReuseIdentifier: "BookmarkCell") - view.addSubview(bookmarksTableView) - bookmarksTableView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - bookmarksTableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - bookmarksTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor), - bookmarksTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - bookmarksTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor) - ]) - } - - private func setupPageLabel() { - view.addSubview(pageLabel) - pageLabel.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - pageLabel.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10), - pageLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor) - ]) - pageLabel.textAlignment = .center - } - - private func setupNavigationBar() { - navigationItem.rightBarButtonItems = [ - UIBarButtonItem(title: "Highlights", style: .plain, target: self, action: #selector(toggleHighlights)), - UIBarButtonItem(title: "Bookmarks", style: .plain, target: self, action: #selector(toggleBookmarks)), - UIBarButtonItem(title: "TOC", style: .plain, target: self, action: #selector(toggleTOC)) - ] - } - - - @objc func bookmarkFromMenu() { - addBookmark() - } - - // Add bookmark - @objc func addBookmark() { - let bookmark = Bookmark(spineIndex: currentSpineIndex, pageNumber: currentPage, date: Date()) - bookmarks.append(bookmark) - saveBookmarks() - print("Bookmark added: \(bookmark)") - - // Show confirmation - let alert = UIAlertController(title: "Bookmark Added", message: "Page bookmarked successfully", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "OK", style: .default)) - present(alert, animated: true) - } - - // Save bookmarks to UserDefaults - private func saveBookmarks() { - let encoder = JSONEncoder() - if let encoded = try? encoder.encode(bookmarks) { - UserDefaults.standard.set(encoded, forKey: "savedBookmarks") - print("Bookmarks saved: \(bookmarks.count) total") - } - } - - // Load bookmarks from UserDefaults - private func loadBookmarks() { - if let savedBookmarks = UserDefaults.standard.object(forKey: "savedBookmarks") as? Data { - let decoder = JSONDecoder() - if let loadedBookmarks = try? decoder.decode([Bookmark].self, from: savedBookmarks) { - bookmarks = loadedBookmarks - print("Bookmarks loaded: \(bookmarks.count) total") - } - } - } - - // Load highlights from UserDefaults - private func loadHighlights() { - if let savedHighlights = UserDefaults.standard.object(forKey: "savedHighlights") as? Data { - let decoder = JSONDecoder() - if let loadedHighlights = try? decoder.decode([Highlight].self, from: savedHighlights) { - highlights = loadedHighlights - print("Highlights loaded: \(highlights.count) total") - } - } - } - - // Save highlights to UserDefaults - private func saveHighlights() { - let encoder = JSONEncoder() - if let encoded = try? encoder.encode(highlights) { - UserDefaults.standard.set(encoded, forKey: "savedHighlights") - print("Highlights saved: \(highlights.count) total") - } - } - - // Apply saved highlights to a WebView - private func applySavedHighlights(to webView: WKWebView) { - let currentHighlights = highlights.filter { $0.spineIndex == currentSpineIndex } - - for (index, highlight) in currentHighlights.enumerated() { - let highlightJS = """ - (function() { - var textContent = document.body.textContent || document.body.innerText; - var searchText = '\(highlight.text.replacingOccurrences(of: "'", with: "\\'"))'; - var context = '\(highlight.textContext.replacingOccurrences(of: "'", with: "\\'"))'; - - // Try to find the text using context first - var foundIndex = -1; - if (context.length > 0) { - var contextIndex = textContent.indexOf(context); - if (contextIndex !== -1) { - var relativeIndexInContext = context.indexOf(searchText); - if (relativeIndexInContext !== -1) { - foundIndex = contextIndex + relativeIndexInContext; - } - } - } else { - // Fallback: use relative position - var estimatedIndex = Math.floor(textContent.length * \(highlight.relativePosition)); - var searchWindow = 200; // Search within 200 characters - var startSearch = Math.max(0, estimatedIndex - searchWindow); - var endSearch = Math.min(textContent.length, estimatedIndex + searchWindow); - var searchArea = textContent.substring(startSearch, endSearch); - var localIndex = searchArea.indexOf(searchText); - if (localIndex !== -1) { - foundIndex = startSearch + localIndex; - } - } - - // Final fallback: simple indexOf - if (foundIndex === -1) { - foundIndex = textContent.indexOf(searchText); - } - - if (foundIndex !== -1) { - // Find the text node and apply highlighting - var walker = document.createTreeWalker( - document.body, - NodeFilter.SHOW_TEXT, - null, - false - ); - - var currentIndex = 0; - var node; - while (node = walker.nextNode()) { - var nodeLength = node.textContent.length; - if (currentIndex + nodeLength > foundIndex) { - var localStart = foundIndex - currentIndex; - var localEnd = localStart + searchText.length; - - if (localEnd <= nodeLength) { - var range = document.createRange(); - range.setStart(node, localStart); - range.setEnd(node, localEnd); - - var span = document.createElement('span'); - span.style.backgroundColor = '\(highlight.color)'; - span.style.color = 'black'; - span.className = 'saved-highlight-\(index)'; - span.setAttribute('data-highlight-id', '\(index)'); - - try { - range.surroundContents(span); - return true; - } catch(e) { - var contents = range.extractContents(); - span.appendChild(contents); - range.insertNode(span); - return true; - } - } - } - currentIndex += nodeLength; - } - } - return false; - })(); - """ - - webView.evaluateJavaScript(highlightJS) { success, error in - if let error = error { - print("Error applying saved highlight: \(error)") - } else if let success = success as? Bool, success { - print("Successfully applied highlight: \(highlight.text)") - } - } - } - } - - @objc private func toggleBookmarks() { - bookmarksTableView.isHidden = !bookmarksTableView.isHidden - tocTableView.isHidden = true - highlightsTableView.isHidden = true - bookmarksTableView.reloadData() - } - - @objc private func toggleTOC() { - tocTableView.isHidden = !tocTableView.isHidden - highlightsTableView.isHidden = true - bookmarksTableView.isHidden = true - - // Load TOC from HTML file if empty - if tocItems.isEmpty { - loadTOCFromHTML() - } - - tocTableView.reloadData() - print("TOC toggled, items count: \(tocItems.count)") - } - - @objc private func toggleHighlights() { - highlightsTableView.isHidden = !highlightsTableView.isHidden - tocTableView.isHidden = true - bookmarksTableView.isHidden = true - highlightsTableView.reloadData() - } - - private func parseAndLoadEPUB() { - guard let (metadata, spine, toc, baseURL) = EPUBParser.parseEPUB(at: epubURL) else { return } - title = metadata.title - spineItems = spine - tocItems = toc // Keep this for fallback - self.baseURL = baseURL - totalPagesPerSpine = Array(repeating: 1, count: spine.count) - - // Try to load TOC from HTML file - loadTOCFromHTML() - } - - private func loadTOCFromHTML() { - guard let baseURL = baseURL else { return } - - let tocURL = baseURL.appendingPathComponent("OPS/toc.xhtml") - - do { - let tocHTML = try String(contentsOf: tocURL) - let doc = try SwiftSoup.parse(tocHTML) - - // Parse the TOC HTML - typically contains nav or ol/li structure - var newTocItems: [EPUBTOCItem] = [] - - // Try to find navigation elements (EPUB3 style) - if let nav = try doc.select("nav").first() { - let links = try nav.select("a") - for link in links { - let href = try link.attr("href") - let label = try link.text() - if !href.isEmpty && !label.isEmpty { - newTocItems.append(EPUBTOCItem(label: label, href: href)) - } - } - } else { - // Fallback: look for any links in the document - let links = try doc.select("a") - for link in links { - let href = try link.attr("href") - let label = try link.text() - if !href.isEmpty && !label.isEmpty { - newTocItems.append(EPUBTOCItem(label: label, href: href)) - } - } - } - - if !newTocItems.isEmpty { - tocItems = newTocItems - print("Loaded \(tocItems.count) TOC items from TOC.html") - } else { - print("No TOC items found in TOC.html, using existing TOC") - } - - } catch { - print("Error loading TOC.html: \(error)") - // Keep existing tocItems as fallback - } - } - - private func createPageViewController(for pageIndex: Int) -> PageContentViewController? { - guard let baseURL = baseURL, currentSpineIndex < spineItems.count else { return nil } - - // Create fresh WKWebViewConfiguration for each page - let config = WKWebViewConfiguration() - let webView = WKWebView(frame: .zero, configuration: config) - - webView.scrollView.isScrollEnabled = false - webView.navigationDelegate = self - webView.alpha = 0 // Hide initially to prevent flickering - let htmlURL = baseURL.appendingPathComponent("OPS/\(spineItems[currentSpineIndex].href)") - webView.loadFileURL(htmlURL, allowingReadAccessTo: baseURL) - - let pageVC = PageContentViewController(webView: webView, pageIndex: pageIndex, delegate: self) - pageVC.targetPageIndex = pageIndex - return pageVC - } - - func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - applySettings(to: webView) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.calculateTotalPages(for: webView) - // Find the PageContentViewController that owns this webView and scroll to its target page - if let pageVC = self.findPageViewController(for: webView) { - self.scrollToPage(in: webView, pageIndex: pageVC.targetPageIndex) { - // Apply saved highlights after page is positioned and content is loaded - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - self.applySavedHighlights(to: webView) - // Show the web view after everything is ready - webView.alpha = 1 - } - } - } else { - // If not found, still apply highlights and show the web view - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - self.applySavedHighlights(to: webView) - webView.alpha = 1 - } - } - } - } - - private func findPageViewController(for webView: WKWebView) -> PageContentViewController? { - // Check current page view controller - if let currentVC = pageViewController.viewControllers?.first as? PageContentViewController, - currentVC.webView == webView { - return currentVC - } - return nil - } - - private func calculateTotalPages(for webView: WKWebView) { - webView.evaluateJavaScript("window.getTotalPages()") { (totalPages, error) in - if let pages = totalPages as? Int, pages > 0 { - self.totalPagesPerSpine[self.currentSpineIndex] = pages - self.totalPages = pages - self.currentPage = min(self.currentPage, self.totalPages - 1) - self.updatePageLabel() - self.scrollToPage(in: webView, pageIndex: self.currentPage) - } else { - // Fallback: use a default single page if calculation fails - print("Page calculation failed - using default single page") - print("Total pages result: \(totalPages ?? "nil")") - self.totalPagesPerSpine[self.currentSpineIndex] = 1 - self.totalPages = 1 - self.currentPage = 0 - self.updatePageLabel() - } - } - } - - private func applySettings(to webView: WKWebView) { - let backgroundColor = UserDefaults.standard.string(forKey: "backgroundColor") ?? "#FFFFFF" - let fontFamily = UserDefaults.standard.string(forKey: "fontFamily") ?? "Georgia" - let fontSize = UserDefaults.standard.integer(forKey: "fontSize") > 0 ? UserDefaults.standard.integer(forKey: "fontSize") : 16 - let isDarkMode = UserDefaults.standard.bool(forKey: "isDarkMode") - - let viewHeight = view.safeAreaLayoutGuide.layoutFrame.height - pageLabel.frame.height - 20 - let viewWidth = view.frame.width - 40 - - let css = """ - * { - -webkit-touch-callout: default; - box-sizing: border-box; - } - html { - margin: 0; - padding: 0; - height: 100vh; - overflow: hidden; - background-color: \(backgroundColor); - } - body { - margin: 0; - padding: 20px; - font-family: '\(fontFamily)', serif; - font-size: \(fontSize)px; - color: \(isDarkMode ? "#FFFFFF" : "#000000"); - line-height: 1.6; - - /* Enable text selection */ - -webkit-user-select: text; - -moz-user-select: text; - -ms-user-select: text; - user-select: text; - - /* Column-based pagination */ - column-width: \(viewWidth)px; - column-height: \(viewHeight - 40)px; - column-gap: 20px; - column-fill: auto; - - /* Fixed height for consistent pagination */ - height: \(viewHeight - 40)px; - width: auto; - overflow: hidden; - - /* Prevent awkward breaks */ - orphans: 2; - widows: 2; - } - p { - margin-bottom: 1em; - break-inside: avoid-column; - -webkit-user-select: text; - user-select: text; - } - h1, h2, h3, h4, h5, h6 { - margin-top: 1.5em; - margin-bottom: 0.5em; - break-after: avoid-column; - break-inside: avoid-column; - -webkit-user-select: text; - user-select: text; - } - img { - max-width: 100%; - height: auto; - break-inside: avoid-column; - } - blockquote, pre { - break-inside: avoid-column; - -webkit-user-select: text; - user-select: text; - } - - /* Highlight styles */ - .highlight { - background-color: yellow; - color: black; - } - """ - - let js = """ - var meta = document.createElement('meta'); - meta.name = "viewport"; - meta.content = "width=device-width, initial-scale=1.0, user-scalable=no"; - document.head.appendChild(meta); - - var style = document.createElement('style'); - style.type = "text/css"; - style.textContent = `\(css)`; - document.head.appendChild(style); - - // Page configuration - var pageConfig = { - columnWidth: \(viewWidth), - columnGap: 20, - viewportWidth: \(viewWidth), - targetPageIndex: 0 - }; - - // Disable body scrolling but allow text selection - document.body.style.overflowX = 'hidden'; - document.body.style.overflowY = 'hidden'; - window.addEventListener('scroll', function(e) { e.preventDefault(); }); - window.addEventListener('wheel', function(e) { e.preventDefault(); }); - - // Existing page functions... - window.getTotalPages = function() { - var computedStyle = window.getComputedStyle(document.body); - var columnWidth = parseFloat(computedStyle.columnWidth) || pageConfig.columnWidth; - var columnGap = parseFloat(computedStyle.columnGap) || pageConfig.columnGap; - - var totalWidth = document.body.scrollWidth; - var singleColumnWidth = columnWidth + columnGap; - var totalColumns = Math.ceil(totalWidth / singleColumnWidth); - - console.log('Total width: ' + totalWidth + ', Single column: ' + singleColumnWidth + ', Columns: ' + totalColumns); - return Math.max(1, totalColumns); - }; - - window.setPageIndex = function(pageIndex) { - pageConfig.targetPageIndex = pageIndex; - - var columnWidth = pageConfig.columnWidth; - var columnGap = pageConfig.columnGap; - var singleColumnWidth = columnWidth + columnGap; - - var leftPosition = pageIndex * singleColumnWidth; - - var clipPath = 'inset(0 0 0 ' + leftPosition + 'px)'; - document.body.style.clipPath = clipPath; - document.body.style.webkitClipPath = clipPath; - document.body.style.marginLeft = '-' + leftPosition + 'px'; - - return { - totalPages: window.getTotalPages(), - currentPage: pageIndex - }; - }; - - window.getCurrentPage = function() { - return pageConfig.targetPageIndex; - }; - - window.refreshLayout = function() { - document.body.style.display = 'none'; - document.body.offsetHeight; - document.body.style.display = ''; - return window.getTotalPages(); - }; - """ - - webView.evaluateJavaScript(js) { _, error in - if let error = error { - print("Error injecting settings: \(error)") - } - } - } - - func scrollToPage(in webView: WKWebView, pageIndex: Int, completion: (() -> Void)? = nil) { - webView.evaluateJavaScript("window.setPageIndex(\(pageIndex))") { result, error in - if let error = error { - print("Error setting page index: \(error)") - } - completion?() - } - } - - private func updatePageLabel() { - let globalPageNumber = getCurrentGlobalPageNumber() - let totalGlobalPages = getTotalGlobalPages() - pageLabel.text = "Page \(globalPageNumber) of \(totalGlobalPages)" - } - - private func getCurrentGlobalPageNumber() -> Int { - var globalPage = 1 - for i in 0.. Int { - return totalPagesPerSpine.reduce(0, +) - } - - // MARK: - UIPageViewController DataSource - func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { - guard let current = viewController as? PageContentViewController else { return nil } - - if current.pageIndex > 0 { - // Previous page in same chapter - let previousPage = current.pageIndex - 1 - currentPage = previousPage - return createPageViewController(for: previousPage) - } else if currentSpineIndex > 0 { - // Move to previous chapter, last page - currentSpineIndex -= 1 - let lastPage = max(0, totalPagesPerSpine[currentSpineIndex] - 1) - currentPage = lastPage - return createPageViewController(for: lastPage) - } - return nil - } - - func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { - guard let current = viewController as? PageContentViewController else { return nil } - - if current.pageIndex < totalPagesPerSpine[currentSpineIndex] - 1 { - // Next page in same chapter - let nextPage = current.pageIndex + 1 - currentPage = nextPage - return createPageViewController(for: nextPage) - } else if currentSpineIndex < spineItems.count - 1 { - // Move to next chapter, first page - currentSpineIndex += 1 - currentPage = 0 - return createPageViewController(for: 0) - } - return nil - } - - func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { - if completed, let current = pageViewController.viewControllers?.first as? PageContentViewController { - currentPage = current.pageIndex - updatePageLabel() - } - } - - // MARK: - UITableViewDataSource - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if tableView == tocTableView { - print("TOC table view requesting \(tocItems.count) rows") - return tocItems.count - } - if tableView == highlightsTableView { return highlights.count } - if tableView == bookmarksTableView { return bookmarks.count } - return 0 - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - if tableView == tocTableView { - let cell = tableView.dequeueReusableCell(withIdentifier: "TOCCell", for: indexPath) - let tocItem = tocItems[indexPath.row] - cell.textLabel?.text = tocItem.label - cell.textLabel?.numberOfLines = 0 - cell.accessoryType = .disclosureIndicator - print("TOC cell configured: \(tocItem.label)") - return cell - } else if tableView == highlightsTableView { - let cell = tableView.dequeueReusableCell(withIdentifier: "HighlightCell", for: indexPath) - let highlight = highlights[indexPath.row] - cell.textLabel?.text = highlight.displayText - cell.textLabel?.numberOfLines = 0 - - // Set background color to match the highlight color - switch highlight.color { - case "yellow": - cell.backgroundColor = UIColor.yellow.withAlphaComponent(0.3) - case "green": - cell.backgroundColor = UIColor.green.withAlphaComponent(0.3) - case "pink": - cell.backgroundColor = UIColor.systemPink.withAlphaComponent(0.3) - default: - cell.backgroundColor = UIColor.yellow.withAlphaComponent(0.3) - } - return cell - } else { - let cell = tableView.dequeueReusableCell(withIdentifier: "BookmarkCell", for: indexPath) - let bookmark = bookmarks[indexPath.row] - let formatter = DateFormatter() - formatter.dateStyle = .short - formatter.timeStyle = .short - cell.textLabel?.text = "Page \(bookmark.pageNumber + 1) - \(formatter.string(from: bookmark.date))" - cell.textLabel?.numberOfLines = 0 - return cell - } - } - - // MARK: - UITableViewDelegate - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if tableView == tocTableView { - let tocItem = tocItems[indexPath.row] - print("TOC item selected: \(tocItem.label), href: \(tocItem.href)") - - // Clean the href - remove any leading "./" and handle fragments - var cleanHref = tocItem.href - if cleanHref.hasPrefix("./") { - cleanHref = String(cleanHref.dropFirst(2)) - } - - // Split href and fragment if present - let components = cleanHref.components(separatedBy: "#") - let fileHref = components[0] - - // Find the spine index that matches the TOC href - if let index = spineItems.firstIndex(where: { spine in - let spineHref = spine.href.components(separatedBy: "#")[0] - return spineHref == fileHref || spineHref.hasSuffix(fileHref) || fileHref.hasSuffix(spineHref) - }) { - currentSpineIndex = index - currentPage = 0 - if let newPage = createPageViewController(for: currentPage) { - pageViewController.setViewControllers([newPage], direction: .forward, animated: false) - } - updatePageLabel() - toggleTOC() - print("Navigated to spine index: \(index) for file: \(fileHref)") - } else { - print("Could not find matching spine for TOC href: \(fileHref)") - print("Available spine hrefs: \(spineItems.map { $0.href })") - } - } else if tableView == highlightsTableView { - let highlight = highlights[indexPath.row] - currentSpineIndex = highlight.spineIndex - - // Navigate to the spine first - if let newPage = createPageViewController(for: 0) { - pageViewController.setViewControllers([newPage], direction: .forward, animated: false) - } - - // Find the correct page after the content loads - DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { - self.findAndNavigateToHighlight(highlight) - } - - toggleHighlights() - } else { - currentSpineIndex = bookmarks[indexPath.row].spineIndex - currentPage = bookmarks[indexPath.row].pageNumber - if let newPage = createPageViewController(for: currentPage) { - pageViewController.setViewControllers([newPage], direction: .forward, animated: false) - } - updatePageLabel() - toggleBookmarks() - } - tableView.deselectRow(at: indexPath, animated: true) - } - - private func findAndNavigateToHighlight(_ highlight: Highlight) { - guard let currentPageVC = pageViewController.viewControllers?.first as? PageContentViewController else { - return - } - - let webView = currentPageVC.webView - let findHighlightJS = """ - (function() { - var textContent = document.body.textContent || document.body.innerText; - var searchText = '\(highlight.text.replacingOccurrences(of: "'", with: "\\'"))'; - var context = '\(highlight.textContext.replacingOccurrences(of: "'", with: "\\'"))'; - - // Find the text position - var foundIndex = -1; - if (context.length > 0) { - var contextIndex = textContent.indexOf(context); - if (contextIndex !== -1) { - var relativeIndexInContext = context.indexOf(searchText); - if (relativeIndexInContext !== -1) { - foundIndex = contextIndex + relativeIndexInContext; - } - } - } - - if (foundIndex === -1) { - var estimatedIndex = Math.floor(textContent.length * \(highlight.relativePosition)); - var searchWindow = 200; - var startSearch = Math.max(0, estimatedIndex - searchWindow); - var endSearch = Math.min(textContent.length, estimatedIndex + searchWindow); - var searchArea = textContent.substring(startSearch, endSearch); - var localIndex = searchArea.indexOf(searchText); - if (localIndex !== -1) { - foundIndex = startSearch + localIndex; - } - } - - if (foundIndex === -1) { - foundIndex = textContent.indexOf(searchText); - } - - if (foundIndex !== -1) { - // Calculate which page this text would be on - var totalPages = window.getTotalPages(); - var textPerPage = textContent.length / totalPages; - var estimatedPage = Math.floor(foundIndex / textPerPage); - return Math.max(0, Math.min(estimatedPage, totalPages - 1)); - } - - return 0; - })(); - """ - - webView.evaluateJavaScript(findHighlightJS) { result, error in - if let pageNumber = result as? Int { - self.currentPage = pageNumber - self.scrollToPage(in: webView, pageIndex: pageNumber) { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.updatePageLabel() - } - } - } - } - } -} - - diff --git a/testReader/SettingsViewController.swift b/testReader/SettingsViewController.swift deleted file mode 100644 index d4f6239..0000000 --- a/testReader/SettingsViewController.swift +++ /dev/null @@ -1,176 +0,0 @@ -// -// SettingsViewController.swift -// testReader -// -// Created by shrutesh sharma on 11/03/25. -// - -import UIKit - -class SettingsViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate { - private let darkModeSwitch = UISwitch() - private let backgroundColorPicker = UIPickerView() - private let fontFamilyPicker = UIPickerView() - private let fontSizeSlider = UISlider() - private let highlightColorPicker = UIPickerView() - private let pageCurlSwitch = UISwitch() - - private let colors = ["#FFFFFF", "#F5F5DC", "#E0E0E0"] - private let colorNames = ["White", "Beige", "Gray"] - private let fonts = ["Georgia", "Times New Roman", "Helvetica"] - private let highlightColors = ["yellow", "green", "pink"] - - override func viewDidLoad() { - super.viewDidLoad() - title = "Settings" - view.backgroundColor = .white - setupUI() - loadCurrentSettings() - } - - private func setupUI() { - let darkModeLabel = UILabel() - darkModeLabel.text = "Dark Mode" - darkModeLabel.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(darkModeLabel) - darkModeSwitch.translatesAutoresizingMaskIntoConstraints = false - darkModeSwitch.addTarget(self, action: #selector(saveSettings), for: .valueChanged) - view.addSubview(darkModeSwitch) - - let bgLabel = UILabel() - bgLabel.text = "Background Color" - bgLabel.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(bgLabel) - backgroundColorPicker.translatesAutoresizingMaskIntoConstraints = false - backgroundColorPicker.dataSource = self - backgroundColorPicker.delegate = self - view.addSubview(backgroundColorPicker) - - let fontLabel = UILabel() - fontLabel.text = "Font Family" - fontLabel.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(fontLabel) - fontFamilyPicker.translatesAutoresizingMaskIntoConstraints = false - fontFamilyPicker.dataSource = self - fontFamilyPicker.delegate = self - view.addSubview(fontFamilyPicker) - - let sizeLabel = UILabel() - sizeLabel.text = "Font Size" - sizeLabel.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(sizeLabel) - fontSizeSlider.translatesAutoresizingMaskIntoConstraints = false - fontSizeSlider.minimumValue = 12 - fontSizeSlider.maximumValue = 24 - fontSizeSlider.addTarget(self, action: #selector(saveSettings), for: .valueChanged) - view.addSubview(fontSizeSlider) - - let highlightLabel = UILabel() - highlightLabel.text = "Highlight Color" - highlightLabel.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(highlightLabel) - highlightColorPicker.translatesAutoresizingMaskIntoConstraints = false - highlightColorPicker.dataSource = self - highlightColorPicker.delegate = self - view.addSubview(highlightColorPicker) - - let pageCurlLabel = UILabel() - pageCurlLabel.text = "Page Curl Animation" - pageCurlLabel.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(pageCurlLabel) - pageCurlSwitch.translatesAutoresizingMaskIntoConstraints = false - pageCurlSwitch.addTarget(self, action: #selector(saveSettings), for: .valueChanged) - view.addSubview(pageCurlSwitch) - - NSLayoutConstraint.activate([ - darkModeLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), - darkModeLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), - darkModeSwitch.centerYAnchor.constraint(equalTo: darkModeLabel.centerYAnchor), - darkModeSwitch.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), - - bgLabel.topAnchor.constraint(equalTo: darkModeLabel.bottomAnchor, constant: 20), - bgLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), - backgroundColorPicker.topAnchor.constraint(equalTo: bgLabel.bottomAnchor, constant: 10), - backgroundColorPicker.leadingAnchor.constraint(equalTo: view.leadingAnchor), - backgroundColorPicker.trailingAnchor.constraint(equalTo: view.trailingAnchor), - backgroundColorPicker.heightAnchor.constraint(equalToConstant: 100), - - fontLabel.topAnchor.constraint(equalTo: backgroundColorPicker.bottomAnchor, constant: 20), - fontLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), - fontFamilyPicker.topAnchor.constraint(equalTo: fontLabel.bottomAnchor, constant: 10), - fontFamilyPicker.leadingAnchor.constraint(equalTo: view.leadingAnchor), - fontFamilyPicker.trailingAnchor.constraint(equalTo: view.trailingAnchor), - fontFamilyPicker.heightAnchor.constraint(equalToConstant: 100), - - sizeLabel.topAnchor.constraint(equalTo: fontFamilyPicker.bottomAnchor, constant: 20), - sizeLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), - fontSizeSlider.centerYAnchor.constraint(equalTo: sizeLabel.centerYAnchor), - fontSizeSlider.leadingAnchor.constraint(equalTo: sizeLabel.trailingAnchor, constant: 20), - fontSizeSlider.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), - - highlightLabel.topAnchor.constraint(equalTo: sizeLabel.bottomAnchor, constant: 20), - highlightLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), - highlightColorPicker.topAnchor.constraint(equalTo: highlightLabel.bottomAnchor, constant: 10), - highlightColorPicker.leadingAnchor.constraint(equalTo: view.leadingAnchor), - highlightColorPicker.trailingAnchor.constraint(equalTo: view.trailingAnchor), - highlightColorPicker.heightAnchor.constraint(equalToConstant: 100), - - pageCurlLabel.topAnchor.constraint(equalTo: highlightColorPicker.bottomAnchor, constant: 20), - pageCurlLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), - pageCurlSwitch.centerYAnchor.constraint(equalTo: pageCurlLabel.centerYAnchor), - pageCurlSwitch.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20) - ]) - } - - private func loadCurrentSettings() { - darkModeSwitch.isOn = UserDefaults.standard.bool(forKey: "isDarkMode") - if let bgColor = UserDefaults.standard.string(forKey: "backgroundColor"), - let index = colors.firstIndex(of: bgColor) { - backgroundColorPicker.selectRow(index, inComponent: 0, animated: false) - } - if let font = UserDefaults.standard.string(forKey: "fontFamily"), - let index = fonts.firstIndex(of: font) { - fontFamilyPicker.selectRow(index, inComponent: 0, animated: false) - } - fontSizeSlider.value = Float(UserDefaults.standard.integer(forKey: "fontSize") > 0 ? UserDefaults.standard.integer(forKey: "fontSize") : 16) - if let highlightColor = UserDefaults.standard.string(forKey: "highlightColor"), - let index = highlightColors.firstIndex(of: highlightColor) { - highlightColorPicker.selectRow(index, inComponent: 0, animated: false) - } - pageCurlSwitch.isOn = UserDefaults.standard.bool(forKey: "isPageCurlEnabled") - } - - @objc private func saveSettings() { - UserDefaults.standard.set(darkModeSwitch.isOn, forKey: "isDarkMode") - let bgIndex = backgroundColorPicker.selectedRow(inComponent: 0) - UserDefaults.standard.set(colors[bgIndex], forKey: "backgroundColor") - let fontIndex = fontFamilyPicker.selectedRow(inComponent: 0) - UserDefaults.standard.set(fonts[fontIndex], forKey: "fontFamily") - UserDefaults.standard.set(Int(fontSizeSlider.value), forKey: "fontSize") - let highlightIndex = highlightColorPicker.selectedRow(inComponent: 0) - UserDefaults.standard.set(highlightColors[highlightIndex], forKey: "highlightColor") - UserDefaults.standard.set(pageCurlSwitch.isOn, forKey: "isPageCurlEnabled") - } - - func numberOfComponents(in pickerView: UIPickerView) -> Int { - return 1 - } - - func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { - if pickerView == backgroundColorPicker { return colors.count } - if pickerView == fontFamilyPicker { return fonts.count } - if pickerView == highlightColorPicker { return highlightColors.count } - return 0 - } - - func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { - if pickerView == backgroundColorPicker { return colorNames[row] } - if pickerView == fontFamilyPicker { return fonts[row] } - if pickerView == highlightColorPicker { return highlightColors[row] } - return nil - } - - func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { - saveSettings() - } -} diff --git a/testReader/ViewController.swift b/testReader/ViewController.swift deleted file mode 100644 index 4e896fe..0000000 --- a/testReader/ViewController.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// ViewController.swift -// testReader -// -// Created by shrutesh sharma on 11/03/25. -// - -import UIKit - -class ViewController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view. - } - - -} -