@thatkid02/react-native-pdf-viewer 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  import Foundation
2
2
  import PDFKit
3
3
  import NitroModules
4
+ import CryptoKit
4
5
 
5
6
  // Error Types
6
7
  enum PdfViewerError: LocalizedError {
@@ -68,10 +69,12 @@ class HybridPdfViewer: HybridPdfViewerSpec {
68
69
  private var activityIndicator: UIActivityIndicatorView!
69
70
  private var document: PDFDocument?
70
71
  private var sourceUri: String?
72
+ private var documentHash: String? // MD5 hash of source URI for caching
71
73
  private var urlSession: URLSession!
72
74
  private var downloadTask: URLSessionDataTask?
73
75
  private var loadToken: Int = 0
74
76
  private var boundsObservation: NSKeyValueObservation?
77
+ private var backgroundObserver: NSObjectProtocol?
75
78
  private var isLoading: Bool = false {
76
79
  didSet {
77
80
  if isLoading != oldValue {
@@ -81,14 +84,9 @@ class HybridPdfViewer: HybridPdfViewerSpec {
81
84
  }
82
85
  }
83
86
 
84
- // Optimized caching with limits
85
- private lazy var thumbnailCache: NSCache<NSNumber, NSString> = {
86
- let cache = NSCache<NSNumber, NSString>()
87
- cache.countLimit = 50 // Max 50 thumbnails in memory
88
- cache.totalCostLimit = 10 * 1024 * 1024 // 10MB limit
89
- cache.evictsObjectsWithDiscardedContent = true
90
- return cache
91
- }()
87
+ // URL-scoped thumbnail cache: document hash -> page index -> URI
88
+ private var thumbnailCache = [String: [Int: String]]()
89
+ private let cacheLock = NSLock()
92
90
 
93
91
  // Dedicated queue for thumbnail generation with limited concurrency
94
92
  private let thumbnailQueue = DispatchQueue(label: "com.pdfviewer.thumbnails", qos: .utility, attributes: .concurrent)
@@ -345,19 +343,42 @@ class HybridPdfViewer: HybridPdfViewerSpec {
345
343
  object: nil
346
344
  )
347
345
 
346
+ // Observe app entering background to cleanup thumbnail cache
347
+ backgroundObserver = NotificationCenter.default.addObserver(
348
+ forName: UIApplication.didEnterBackgroundNotification,
349
+ object: nil,
350
+ queue: .main
351
+ ) { [weak self] _ in
352
+ self?.cleanupThumbnailDirectory()
353
+ }
354
+
348
355
  // Observe bounds changes to update scale factor when view is resized
349
356
  boundsObservation = pdfView.observe(\.bounds, options: [.new]) { [weak self] _, _ in
350
- self?.updateScaleToFitWidthIfNeeded()
357
+ guard let self = self else { return }
358
+ // Only re-enable autoScales during bounds change if we're at default scale
359
+ // This allows manual zoom to persist through view lifecycle changes
360
+ if self.pdfView.displayMode == .singlePageContinuous && self.pdfView.scaleFactor == 1.0 {
361
+ self.pdfView.autoScales = true
362
+ }
363
+ self.updateScaleToFitWidthIfNeeded()
351
364
  }
352
365
  }
353
366
 
354
367
  deinit {
355
368
  NotificationCenter.default.removeObserver(self)
369
+ if let observer = backgroundObserver {
370
+ NotificationCenter.default.removeObserver(observer)
371
+ }
356
372
  boundsObservation?.invalidate()
357
373
  downloadTask?.cancel()
358
374
  urlSession.invalidateAndCancel()
359
- thumbnailCache.removeAllObjects()
360
- cleanupThumbnailDirectory()
375
+
376
+ // Clear in-memory cache for this document only (disk cache persists)
377
+ if let hash = documentHash {
378
+ cacheLock.lock()
379
+ thumbnailCache.removeValue(forKey: hash)
380
+ cacheLock.unlock()
381
+ }
361
382
  }
362
383
 
363
384
  // Document Loading
@@ -387,6 +408,7 @@ class HybridPdfViewer: HybridPdfViewerSpec {
387
408
 
388
409
  // Store the source URI for thumbnail caching
389
410
  sourceUri = source
411
+ documentHash = computeDocumentHash(source)
390
412
 
391
413
  guard let url = resolveURL(from: source) else {
392
414
  ensureMainThread { self.isLoading = false }
@@ -492,11 +514,25 @@ class HybridPdfViewer: HybridPdfViewerSpec {
492
514
  isLoading = false
493
515
  self.document = document
494
516
  pdfView.document = document
495
- thumbnailCache.removeAllObjects()
517
+
518
+ // Clear in-memory cache for all documents
519
+ cacheLock.lock()
520
+ thumbnailCache.removeAll()
521
+ cacheLock.unlock()
522
+
523
+ // Ensure autoScales is enabled for proper width fitting
524
+ pdfView.autoScales = true
496
525
 
497
526
  // Update scale after document is loaded
498
527
  updateScaleToFitWidthIfNeeded()
499
528
 
529
+ // If bounds were zero, schedule a delayed update
530
+ if pdfView.bounds.width == 0 {
531
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
532
+ self?.updateScaleToFitWidthIfNeeded()
533
+ }
534
+ }
535
+
500
536
  if let firstPage = document.page(at: 0) {
501
537
  let pageRect = firstPage.bounds(for: .mediaBox)
502
538
  onLoadComplete?(LoadCompleteEvent(
@@ -517,10 +553,18 @@ class HybridPdfViewer: HybridPdfViewerSpec {
517
553
  // Only update scale if view has been laid out (width > 0)
518
554
  guard viewWidth > 0 && pageRect.width > 0 else { return }
519
555
 
520
- // In continuous scroll mode with autoScales=true, PDFKit handles scaling automatically
521
- // We just need to ensure autoScales is enabled
556
+ // In continuous scroll mode, check if we should enable autoScales
522
557
  if pdfView.displayMode == .singlePageContinuous {
523
- pdfView.autoScales = true
558
+ // Only enable autoScales if at default scale (hasn't been manually zoomed)
559
+ // This preserves user's manual zoom level through view lifecycle
560
+ let currentScale = pdfView.scaleFactor
561
+ let isDefaultScale = abs(currentScale - 1.0) < 0.01
562
+
563
+ if isDefaultScale {
564
+ pdfView.autoScales = true
565
+ // Force PDFView to recalculate scale if needed
566
+ pdfView.layoutDocumentView()
567
+ }
524
568
  } else {
525
569
  // For paging mode, calculate and set the scale to fit width
526
570
  let scale = viewWidth / pageRect.width
@@ -557,7 +601,9 @@ class HybridPdfViewer: HybridPdfViewerSpec {
557
601
 
558
602
  @objc private func didReceiveMemoryWarning() {
559
603
  // Clear thumbnail cache to free up memory
560
- thumbnailCache.removeAllObjects()
604
+ cacheLock.lock()
605
+ thumbnailCache.removeAll()
606
+ cacheLock.unlock()
561
607
 
562
608
  // Clear pending thumbnails set
563
609
  pendingLock.lock()
@@ -599,13 +645,11 @@ class HybridPdfViewer: HybridPdfViewerSpec {
599
645
  let maxScale = CGFloat(self.maxScale ?? 4.0)
600
646
  let clampedScale = max(minScale, min(maxScale, CGFloat(scale)))
601
647
 
602
- // Ensure UI update happens on main thread
603
- if Thread.isMainThread {
604
- pdfView.scaleFactor = clampedScale
605
- } else {
606
- DispatchQueue.main.async { [weak self] in
607
- self?.pdfView.scaleFactor = clampedScale
608
- }
648
+ // Disable autoScales when manually setting scale
649
+ // This allows programmatic zoom control to work
650
+ ensureMainThread {
651
+ self.pdfView.autoScales = false
652
+ self.pdfView.scaleFactor = clampedScale
609
653
  }
610
654
  }
611
655
 
@@ -614,16 +658,37 @@ class HybridPdfViewer: HybridPdfViewerSpec {
614
658
  throw PdfViewerError.documentNotLoaded
615
659
  }
616
660
 
661
+ guard let hash = documentHash else {
662
+ throw PdfViewerError.invalidSource
663
+ }
664
+
617
665
  let pageIndex = Int(page)
618
666
  guard pageIndex >= 0 && pageIndex < document.pageCount else {
619
667
  throw PdfViewerError.invalidPageIndex(page: pageIndex, pageCount: document.pageCount)
620
668
  }
621
669
 
622
- let pageKey = NSNumber(value: pageIndex)
670
+ // Check in-memory cache first
671
+ cacheLock.lock()
672
+ if let cachedUri = thumbnailCache[hash]?[pageIndex] {
673
+ cacheLock.unlock()
674
+ onThumbnailGenerated?(ThumbnailGeneratedEvent(page: Double(pageIndex), uri: cachedUri))
675
+ return
676
+ }
677
+ cacheLock.unlock()
623
678
 
624
- // Return cached thumbnail immediately if available
625
- if let cached = thumbnailCache.object(forKey: pageKey) {
626
- onThumbnailGenerated?(ThumbnailGeneratedEvent(page: Double(pageIndex), uri: cached as String))
679
+ // Check disk cache before generating
680
+ let diskPath = getThumbnailPath(hash: hash, page: pageIndex)
681
+ if FileManager.default.fileExists(atPath: diskPath.path) {
682
+ let uri = diskPath.absoluteString
683
+ // Cache in memory
684
+ cacheLock.lock()
685
+ if thumbnailCache[hash] == nil {
686
+ thumbnailCache[hash] = [:]
687
+ }
688
+ thumbnailCache[hash]?[pageIndex] = uri
689
+ cacheLock.unlock()
690
+
691
+ onThumbnailGenerated?(ThumbnailGeneratedEvent(page: Double(pageIndex), uri: uri))
627
692
  return
628
693
  }
629
694
 
@@ -649,15 +714,23 @@ class HybridPdfViewer: HybridPdfViewerSpec {
649
714
  self.thumbnailSemaphore.wait()
650
715
  defer { self.thumbnailSemaphore.signal() }
651
716
 
652
- // Double-check cache after acquiring semaphore
653
- if let cached = self.thumbnailCache.object(forKey: pageKey) {
717
+ // Double-check disk cache after acquiring semaphore
718
+ if FileManager.default.fileExists(atPath: diskPath.path) {
719
+ let uri = diskPath.absoluteString
720
+ self.cacheLock.lock()
721
+ if self.thumbnailCache[hash] == nil {
722
+ self.thumbnailCache[hash] = [:]
723
+ }
724
+ self.thumbnailCache[hash]?[pageIndex] = uri
725
+ self.cacheLock.unlock()
726
+
654
727
  DispatchQueue.main.async { [weak self] in
655
- self?.onThumbnailGenerated?(ThumbnailGeneratedEvent(page: Double(pageIndex), uri: cached as String))
728
+ self?.onThumbnailGenerated?(ThumbnailGeneratedEvent(page: Double(pageIndex), uri: uri))
656
729
  }
657
730
  return
658
731
  }
659
732
 
660
- self.generateThumbnailSync(document: document, pageIndex: pageIndex, pageKey: pageKey)
733
+ self.generateThumbnailSync(document: document, pageIndex: pageIndex, hash: hash)
661
734
  }
662
735
  }
663
736
 
@@ -666,6 +739,10 @@ class HybridPdfViewer: HybridPdfViewerSpec {
666
739
  throw PdfViewerError.documentNotLoaded
667
740
  }
668
741
 
742
+ guard let hash = documentHash else {
743
+ throw PdfViewerError.invalidSource
744
+ }
745
+
669
746
  let pageCount = document.pageCount
670
747
 
671
748
  // Process thumbnails in batches for better memory management
@@ -674,12 +751,30 @@ class HybridPdfViewer: HybridPdfViewerSpec {
674
751
 
675
752
  for pageIndex in 0..<pageCount {
676
753
  autoreleasepool {
677
- let pageKey = NSNumber(value: pageIndex)
754
+ // Check in-memory cache
755
+ self.cacheLock.lock()
756
+ if let cachedUri = self.thumbnailCache[hash]?[pageIndex] {
757
+ self.cacheLock.unlock()
758
+ DispatchQueue.main.async { [weak self] in
759
+ self?.onThumbnailGenerated?(ThumbnailGeneratedEvent(page: Double(pageIndex), uri: cachedUri))
760
+ }
761
+ return
762
+ }
763
+ self.cacheLock.unlock()
678
764
 
679
- // Return cached thumbnail immediately
680
- if let cached = self.thumbnailCache.object(forKey: pageKey) {
765
+ // Check disk cache
766
+ let diskPath = self.getThumbnailPath(hash: hash, page: pageIndex)
767
+ if FileManager.default.fileExists(atPath: diskPath.path) {
768
+ let uri = diskPath.absoluteString
769
+ self.cacheLock.lock()
770
+ if self.thumbnailCache[hash] == nil {
771
+ self.thumbnailCache[hash] = [:]
772
+ }
773
+ self.thumbnailCache[hash]?[pageIndex] = uri
774
+ self.cacheLock.unlock()
775
+
681
776
  DispatchQueue.main.async { [weak self] in
682
- self?.onThumbnailGenerated?(ThumbnailGeneratedEvent(page: Double(pageIndex), uri: cached as String))
777
+ self?.onThumbnailGenerated?(ThumbnailGeneratedEvent(page: Double(pageIndex), uri: uri))
683
778
  }
684
779
  return
685
780
  }
@@ -687,14 +782,38 @@ class HybridPdfViewer: HybridPdfViewerSpec {
687
782
  self.thumbnailSemaphore.wait()
688
783
  defer { self.thumbnailSemaphore.signal() }
689
784
 
690
- self.generateThumbnailSync(document: document, pageIndex: pageIndex, pageKey: pageKey)
785
+ self.generateThumbnailSync(document: document, pageIndex: pageIndex, hash: hash)
691
786
  }
692
787
  }
693
788
  }
694
789
  }
695
790
 
791
+ func getDocumentInfo() throws -> DocumentInfo? {
792
+ guard let document = document else {
793
+ return nil
794
+ }
795
+
796
+ guard let firstPage = document.page(at: 0) else {
797
+ return nil
798
+ }
799
+
800
+ // Get current page index
801
+ var currentPageIndex = 0
802
+ if let currentPage = pdfView.currentPage {
803
+ currentPageIndex = document.index(for: currentPage)
804
+ }
805
+
806
+ let bounds = firstPage.bounds(for: .mediaBox)
807
+ return DocumentInfo(
808
+ pageCount: Double(document.pageCount),
809
+ pageWidth: bounds.width,
810
+ pageHeight: bounds.height,
811
+ currentPage: Double(currentPageIndex)
812
+ )
813
+ }
814
+
696
815
  // Private Thumbnail Generation
697
- private func generateThumbnailSync(document: PDFDocument, pageIndex: Int, pageKey: NSNumber) {
816
+ private func generateThumbnailSync(document: PDFDocument, pageIndex: Int, hash: String) {
698
817
  guard let pdfPage = document.page(at: pageIndex) else { return }
699
818
 
700
819
  let pageRect = pdfPage.bounds(for: .mediaBox)
@@ -706,10 +825,14 @@ class HybridPdfViewer: HybridPdfViewerSpec {
706
825
 
707
826
  let thumbnail = pdfPage.thumbnail(of: CGSize(width: thumbWidth, height: thumbHeight), for: .mediaBox)
708
827
 
709
- if let uri = saveThumbnailToCache(thumbnail, page: pageIndex) {
710
- // Calculate approximate memory cost for cache
711
- let cost = Int(thumbWidth * thumbHeight * 4) // Approximate bytes
712
- thumbnailCache.setObject(uri as NSString, forKey: pageKey, cost: cost)
828
+ if let uri = saveThumbnailToCache(thumbnail, page: pageIndex, hash: hash) {
829
+ // Cache in memory
830
+ cacheLock.lock()
831
+ if thumbnailCache[hash] == nil {
832
+ thumbnailCache[hash] = [:]
833
+ }
834
+ thumbnailCache[hash]?[pageIndex] = uri
835
+ cacheLock.unlock()
713
836
 
714
837
  DispatchQueue.main.async { [weak self] in
715
838
  self?.onThumbnailGenerated?(ThumbnailGeneratedEvent(page: Double(pageIndex), uri: uri))
@@ -720,16 +843,17 @@ class HybridPdfViewer: HybridPdfViewerSpec {
720
843
  }
721
844
 
722
845
  // Helper Methods
723
- private func saveThumbnailToCache(_ image: UIImage, page: Int) -> String? {
846
+ private func saveThumbnailToCache(_ image: UIImage, page: Int, hash: String) -> String? {
724
847
  guard let data = image.jpegData(compressionQuality: 0.8) else { return nil }
725
848
 
726
- let hash = abs(sourceUri?.hashValue ?? 0)
727
- let fileName = "thumb_\(page)_\(hash).jpg"
728
- let cacheDir = FileManager.default.temporaryDirectory.appendingPathComponent("PDFThumbnails")
849
+ let fileName = "\(page).jpg"
850
+ let documentCacheDir = FileManager.default.temporaryDirectory
851
+ .appendingPathComponent("PDFThumbnails")
852
+ .appendingPathComponent(hash)
729
853
 
730
854
  do {
731
- try FileManager.default.createDirectory(at: cacheDir, withIntermediateDirectories: true)
732
- let fileURL = cacheDir.appendingPathComponent(fileName)
855
+ try FileManager.default.createDirectory(at: documentCacheDir, withIntermediateDirectories: true)
856
+ let fileURL = documentCacheDir.appendingPathComponent(fileName)
733
857
  try data.write(to: fileURL, options: .atomic)
734
858
  return fileURL.absoluteString
735
859
  } catch {
@@ -737,6 +861,19 @@ class HybridPdfViewer: HybridPdfViewerSpec {
737
861
  }
738
862
  }
739
863
 
864
+ private func getThumbnailPath(hash: String, page: Int) -> URL {
865
+ return FileManager.default.temporaryDirectory
866
+ .appendingPathComponent("PDFThumbnails")
867
+ .appendingPathComponent(hash)
868
+ .appendingPathComponent("\(page).jpg")
869
+ }
870
+
871
+ private func computeDocumentHash(_ uri: String) -> String {
872
+ let data = Data(uri.utf8)
873
+ let digest = Insecure.MD5.hash(data: data)
874
+ return digest.map { String(format: "%02hhx", $0) }.joined()
875
+ }
876
+
740
877
  private func emitError(_ error: PdfViewerError) {
741
878
  onError?(ErrorEvent(message: error.errorDescription ?? "Unknown error", code: error.errorCode))
742
879
  }
@@ -4,6 +4,12 @@ export interface LoadCompleteEvent {
4
4
  pageWidth: number;
5
5
  pageHeight: number;
6
6
  }
7
+ export interface DocumentInfo {
8
+ pageCount: number;
9
+ pageWidth: number;
10
+ pageHeight: number;
11
+ currentPage: number;
12
+ }
7
13
  export interface PageChangeEvent {
8
14
  page: number;
9
15
  pageCount: number;
@@ -81,6 +87,7 @@ export interface PdfViewerMethods extends HybridViewMethods {
81
87
  setScale(scale: number): void;
82
88
  generateThumbnail(page: number): void;
83
89
  generateAllThumbnails(): void;
90
+ getDocumentInfo(): DocumentInfo | null;
84
91
  }
85
92
  export type PdfViewer = HybridView<PdfViewerProps, PdfViewerMethods>;
86
93
  //# sourceMappingURL=PdfViewer.nitro.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PdfViewer.nitro.d.ts","sourceRoot":"","sources":["../../../src/PdfViewer.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EACjB,eAAe,EAChB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;CACpB;AAGD,MAAM,WAAW,iBAAiB;IAEhC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAe,SAAQ,eAAe;IACrD;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAG3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAGjC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACpD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAChE,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACvD;AAED,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IAEzD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAG7B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAG9B,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAGtC,qBAAqB,IAAI,IAAI,CAAC;CAC/B;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC"}
1
+ {"version":3,"file":"PdfViewer.nitro.d.ts","sourceRoot":"","sources":["../../../src/PdfViewer.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EACjB,eAAe,EAChB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;CACpB;AAGD,MAAM,WAAW,iBAAiB;IAEhC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAe,SAAQ,eAAe;IACrD;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAG3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAGjC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACpD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAChE,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACvD;AAED,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IAEzD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAG7B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAG9B,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAGtC,qBAAqB,IAAI,IAAI,CAAC;IAG9B,eAAe,IAAI,YAAY,GAAG,IAAI,CAAC;CACxC;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC"}
@@ -0,0 +1,65 @@
1
+ ///
2
+ /// JDocumentInfo.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © 2026 Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #include <fbjni/fbjni.h>
11
+ #include "DocumentInfo.hpp"
12
+
13
+
14
+
15
+ namespace margelo::nitro::pdfviewer {
16
+
17
+ using namespace facebook;
18
+
19
+ /**
20
+ * The C++ JNI bridge between the C++ struct "DocumentInfo" and the the Kotlin data class "DocumentInfo".
21
+ */
22
+ struct JDocumentInfo final: public jni::JavaClass<JDocumentInfo> {
23
+ public:
24
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/pdfviewer/DocumentInfo;";
25
+
26
+ public:
27
+ /**
28
+ * Convert this Java/Kotlin-based struct to the C++ struct DocumentInfo by copying all values to C++.
29
+ */
30
+ [[maybe_unused]]
31
+ [[nodiscard]]
32
+ DocumentInfo toCpp() const {
33
+ static const auto clazz = javaClassStatic();
34
+ static const auto fieldPageCount = clazz->getField<double>("pageCount");
35
+ double pageCount = this->getFieldValue(fieldPageCount);
36
+ static const auto fieldPageWidth = clazz->getField<double>("pageWidth");
37
+ double pageWidth = this->getFieldValue(fieldPageWidth);
38
+ static const auto fieldPageHeight = clazz->getField<double>("pageHeight");
39
+ double pageHeight = this->getFieldValue(fieldPageHeight);
40
+ static const auto fieldCurrentPage = clazz->getField<double>("currentPage");
41
+ double currentPage = this->getFieldValue(fieldCurrentPage);
42
+ return DocumentInfo(
43
+ pageCount,
44
+ pageWidth,
45
+ pageHeight,
46
+ currentPage
47
+ );
48
+ }
49
+
50
+ public:
51
+ /**
52
+ * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java.
53
+ */
54
+ [[maybe_unused]]
55
+ static jni::local_ref<JDocumentInfo::javaobject> fromCpp(const DocumentInfo& value) {
56
+ return newInstance(
57
+ value.pageCount,
58
+ value.pageWidth,
59
+ value.pageHeight,
60
+ value.currentPage
61
+ );
62
+ }
63
+ };
64
+
65
+ } // namespace margelo::nitro::pdfviewer
@@ -19,6 +19,8 @@ namespace margelo::nitro::pdfviewer { struct ErrorEvent; }
19
19
  namespace margelo::nitro::pdfviewer { struct ThumbnailGeneratedEvent; }
20
20
  // Forward declaration of `LoadingChangeEvent` to properly resolve imports.
21
21
  namespace margelo::nitro::pdfviewer { struct LoadingChangeEvent; }
22
+ // Forward declaration of `DocumentInfo` to properly resolve imports.
23
+ namespace margelo::nitro::pdfviewer { struct DocumentInfo; }
22
24
 
23
25
  #include <string>
24
26
  #include <optional>
@@ -41,6 +43,8 @@ namespace margelo::nitro::pdfviewer { struct LoadingChangeEvent; }
41
43
  #include "LoadingChangeEvent.hpp"
42
44
  #include "JFunc_void_LoadingChangeEvent.hpp"
43
45
  #include "JLoadingChangeEvent.hpp"
46
+ #include "DocumentInfo.hpp"
47
+ #include "JDocumentInfo.hpp"
44
48
 
45
49
  namespace margelo::nitro::pdfviewer {
46
50
 
@@ -305,5 +309,10 @@ namespace margelo::nitro::pdfviewer {
305
309
  static const auto method = javaClassStatic()->getMethod<void()>("generateAllThumbnails");
306
310
  method(_javaPart);
307
311
  }
312
+ std::optional<DocumentInfo> JHybridPdfViewerSpec::getDocumentInfo() {
313
+ static const auto method = javaClassStatic()->getMethod<jni::local_ref<JDocumentInfo>()>("getDocumentInfo");
314
+ auto __result = method(_javaPart);
315
+ return __result != nullptr ? std::make_optional(__result->toCpp()) : std::nullopt;
316
+ }
308
317
 
309
318
  } // namespace margelo::nitro::pdfviewer
@@ -92,6 +92,7 @@ namespace margelo::nitro::pdfviewer {
92
92
  void setScale(double scale) override;
93
93
  void generateThumbnail(double page) override;
94
94
  void generateAllThumbnails() override;
95
+ std::optional<DocumentInfo> getDocumentInfo() override;
95
96
 
96
97
  private:
97
98
  friend HybridBase;
@@ -0,0 +1,38 @@
1
+ ///
2
+ /// DocumentInfo.kt
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © 2026 Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ package com.margelo.nitro.pdfviewer
9
+
10
+ import androidx.annotation.Keep
11
+ import com.facebook.proguard.annotations.DoNotStrip
12
+ import com.margelo.nitro.core.*
13
+
14
+
15
+ /**
16
+ * Represents the JavaScript object/struct "DocumentInfo".
17
+ */
18
+ @DoNotStrip
19
+ @Keep
20
+ data class DocumentInfo
21
+ @DoNotStrip
22
+ @Keep
23
+ constructor(
24
+ @DoNotStrip
25
+ @Keep
26
+ val pageCount: Double,
27
+ @DoNotStrip
28
+ @Keep
29
+ val pageWidth: Double,
30
+ @DoNotStrip
31
+ @Keep
32
+ val pageHeight: Double,
33
+ @DoNotStrip
34
+ @Keep
35
+ val currentPage: Double
36
+ ) {
37
+ /* main constructor */
38
+ }
@@ -210,6 +210,10 @@ abstract class HybridPdfViewerSpec: HybridView() {
210
210
  @DoNotStrip
211
211
  @Keep
212
212
  abstract fun generateAllThumbnails(): Unit
213
+
214
+ @DoNotStrip
215
+ @Keep
216
+ abstract fun getDocumentInfo(): DocumentInfo?
213
217
 
214
218
  private external fun initHybrid(): HybridData
215
219
 
@@ -8,6 +8,8 @@
8
8
  #pragma once
9
9
 
10
10
  // Forward declarations of C++ defined types
11
+ // Forward declaration of `DocumentInfo` to properly resolve imports.
12
+ namespace margelo::nitro::pdfviewer { struct DocumentInfo; }
11
13
  // Forward declaration of `ErrorEvent` to properly resolve imports.
12
14
  namespace margelo::nitro::pdfviewer { struct ErrorEvent; }
13
15
  // Forward declaration of `HybridPdfViewerSpec` to properly resolve imports.
@@ -28,6 +30,7 @@ namespace margelo::nitro::pdfviewer { struct ThumbnailGeneratedEvent; }
28
30
  namespace PdfViewer { class HybridPdfViewerSpec_cxx; }
29
31
 
30
32
  // Include C++ defined types
33
+ #include "DocumentInfo.hpp"
31
34
  #include "ErrorEvent.hpp"
32
35
  #include "HybridPdfViewerSpec.hpp"
33
36
  #include "LoadCompleteEvent.hpp"
@@ -315,6 +318,21 @@ namespace margelo::nitro::pdfviewer::bridge::swift {
315
318
  return *optional;
316
319
  }
317
320
 
321
+ // pragma MARK: std::optional<DocumentInfo>
322
+ /**
323
+ * Specialized version of `std::optional<DocumentInfo>`.
324
+ */
325
+ using std__optional_DocumentInfo_ = std::optional<DocumentInfo>;
326
+ inline std::optional<DocumentInfo> create_std__optional_DocumentInfo_(const DocumentInfo& value) noexcept {
327
+ return std::optional<DocumentInfo>(value);
328
+ }
329
+ inline bool has_value_std__optional_DocumentInfo_(const std::optional<DocumentInfo>& optional) noexcept {
330
+ return optional.has_value();
331
+ }
332
+ inline DocumentInfo get_std__optional_DocumentInfo_(const std::optional<DocumentInfo>& optional) noexcept {
333
+ return *optional;
334
+ }
335
+
318
336
  // pragma MARK: std::shared_ptr<HybridPdfViewerSpec>
319
337
  /**
320
338
  * Specialized version of `std::shared_ptr<HybridPdfViewerSpec>`.
@@ -335,5 +353,14 @@ namespace margelo::nitro::pdfviewer::bridge::swift {
335
353
  inline Result_void_ create_Result_void_(const std::exception_ptr& error) noexcept {
336
354
  return Result<void>::withError(error);
337
355
  }
356
+
357
+ // pragma MARK: Result<std::optional<DocumentInfo>>
358
+ using Result_std__optional_DocumentInfo__ = Result<std::optional<DocumentInfo>>;
359
+ inline Result_std__optional_DocumentInfo__ create_Result_std__optional_DocumentInfo__(const std::optional<DocumentInfo>& value) noexcept {
360
+ return Result<std::optional<DocumentInfo>>::withValue(value);
361
+ }
362
+ inline Result_std__optional_DocumentInfo__ create_Result_std__optional_DocumentInfo__(const std::exception_ptr& error) noexcept {
363
+ return Result<std::optional<DocumentInfo>>::withError(error);
364
+ }
338
365
 
339
366
  } // namespace margelo::nitro::pdfviewer::bridge::swift
@@ -8,6 +8,8 @@
8
8
  #pragma once
9
9
 
10
10
  // Forward declarations of C++ defined types
11
+ // Forward declaration of `DocumentInfo` to properly resolve imports.
12
+ namespace margelo::nitro::pdfviewer { struct DocumentInfo; }
11
13
  // Forward declaration of `ErrorEvent` to properly resolve imports.
12
14
  namespace margelo::nitro::pdfviewer { struct ErrorEvent; }
13
15
  // Forward declaration of `HybridPdfViewerSpec` to properly resolve imports.
@@ -24,6 +26,7 @@ namespace margelo::nitro::pdfviewer { struct ScaleChangeEvent; }
24
26
  namespace margelo::nitro::pdfviewer { struct ThumbnailGeneratedEvent; }
25
27
 
26
28
  // Include C++ defined types
29
+ #include "DocumentInfo.hpp"
27
30
  #include "ErrorEvent.hpp"
28
31
  #include "HybridPdfViewerSpec.hpp"
29
32
  #include "LoadCompleteEvent.hpp"
@@ -24,6 +24,8 @@ namespace margelo::nitro::pdfviewer { struct ErrorEvent; }
24
24
  namespace margelo::nitro::pdfviewer { struct ThumbnailGeneratedEvent; }
25
25
  // Forward declaration of `LoadingChangeEvent` to properly resolve imports.
26
26
  namespace margelo::nitro::pdfviewer { struct LoadingChangeEvent; }
27
+ // Forward declaration of `DocumentInfo` to properly resolve imports.
28
+ namespace margelo::nitro::pdfviewer { struct DocumentInfo; }
27
29
 
28
30
  #include <string>
29
31
  #include <optional>
@@ -34,6 +36,7 @@ namespace margelo::nitro::pdfviewer { struct LoadingChangeEvent; }
34
36
  #include "ErrorEvent.hpp"
35
37
  #include "ThumbnailGeneratedEvent.hpp"
36
38
  #include "LoadingChangeEvent.hpp"
39
+ #include "DocumentInfo.hpp"
37
40
 
38
41
  #include "PdfViewer-Swift-Cxx-Umbrella.hpp"
39
42
 
@@ -225,6 +228,14 @@ namespace margelo::nitro::pdfviewer {
225
228
  std::rethrow_exception(__result.error());
226
229
  }
227
230
  }
231
+ inline std::optional<DocumentInfo> getDocumentInfo() override {
232
+ auto __result = _swiftPart.getDocumentInfo();
233
+ if (__result.hasError()) [[unlikely]] {
234
+ std::rethrow_exception(__result.error());
235
+ }
236
+ auto __value = std::move(__result.value());
237
+ return __value;
238
+ }
228
239
 
229
240
  private:
230
241
  PdfViewer::HybridPdfViewerSpec_cxx _swiftPart;