@june24/expo-pdf-reader 0.1.19 → 0.1.21

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.
@@ -89,6 +89,7 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
89
89
  private val onTextPress = ViewEvent<Map<String, Any>>("onTextPress", this, null)
90
90
 
91
91
  private var pageHeights = mutableListOf<Int>()
92
+ private var rowHeights = mutableListOf<Int>()
92
93
  private var pageWidthsPdf = mutableListOf<Float>()
93
94
  private var pageHeightsPdf = mutableListOf<Float>()
94
95
  private var pageViews = mutableListOf<ImageView?>()
@@ -96,8 +97,8 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
96
97
  private var lastEmittedPageIndex = -1
97
98
  private var totalPageCount = 0
98
99
 
99
- private val renderWindowBefore = 8
100
- private val renderWindowAfter = 16
100
+ private val renderWindowBefore = 20
101
+ private val renderWindowAfter = 25
101
102
 
102
103
  // Annotation state
103
104
  private val pageAnnotations = mutableListOf<MutableList<Annotation>>()
@@ -552,11 +553,38 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
552
553
  notifyAnnotationChange()
553
554
  }
554
555
 
556
+ private fun isTwoUpMode(): Boolean = displayMode == "twoUp" || displayMode == "twoUpContinuous"
557
+
558
+ private fun marginBetweenPages(): Int = if (displayMode == "single") 0 else 16
559
+
560
+ private fun getFrameForPage(pageIndex: Int): FrameLayout? {
561
+ if (pageIndex !in pageHeights.indices) return null
562
+ return if (isTwoUpMode()) {
563
+ val row = pageIndex / 2
564
+ val col = pageIndex % 2
565
+ if (row >= container.childCount) null
566
+ else (container.getChildAt(row) as? LinearLayout)?.getChildAt(col) as? FrameLayout
567
+ } else {
568
+ container.getChildAt(pageIndex) as? FrameLayout
569
+ }
570
+ }
571
+
555
572
  private fun scrollToPage(pageIndex: Int) {
556
573
  if (pageHeights.isEmpty()) return
557
- var offset = 0
558
- for (i in 0 until pageIndex.coerceIn(0, pageHeights.size - 1)) {
559
- offset += pageHeights[i] + 16
574
+ val idx = pageIndex.coerceIn(0, pageHeights.size - 1)
575
+ val gap = marginBetweenPages()
576
+ val offset = if (isTwoUpMode() && rowHeights.isNotEmpty()) {
577
+ var sum = 0
578
+ for (r in 0 until idx / 2) {
579
+ sum += rowHeights[r] + gap
580
+ }
581
+ sum
582
+ } else {
583
+ var sum = 0
584
+ for (i in 0 until idx) {
585
+ sum += pageHeights[i] + gap
586
+ }
587
+ sum
560
588
  }
561
589
  scrollView.post { scrollView.smoothScrollTo(0, offset) }
562
590
  }
@@ -596,6 +624,7 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
596
624
  fileDescriptor?.close()
597
625
  fileDescriptor = null
598
626
  pageHeights.clear()
627
+ rowHeights.clear()
599
628
  pageWidthsPdf.clear()
600
629
  pageHeightsPdf.clear()
601
630
  pageViews.clear()
@@ -630,15 +659,19 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
630
659
  updateVisiblePages()
631
660
  return
632
661
  }
662
+ val viewWidth = width.coerceAtLeast(1)
663
+ val isTwoUp = displayMode == "twoUp" || displayMode == "twoUpContinuous"
664
+ val effectiveWidth = if (isTwoUp) viewWidth / 2 else viewWidth
633
665
  scope.launch {
634
- val w = width.coerceAtLeast(1)
666
+ val w = effectiveWidth
635
667
  val result = withContext(Dispatchers.Default) {
636
668
  (0 until renderer.pageCount).map { i ->
637
669
  val page = renderer.openPage(i)
638
670
  try {
671
+ val pageH = (w.toFloat() * page.height / page.width).toInt()
639
672
  Triple(
640
673
  i,
641
- (w.toFloat() * page.height / page.width).toInt(),
674
+ pageH,
642
675
  Pair(page.width.toFloat(), page.height.toFloat())
643
676
  )
644
677
  } finally {
@@ -649,6 +682,7 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
649
682
  if (!isActive) return@launch
650
683
  withContext(Dispatchers.Main) {
651
684
  pageHeights.clear()
685
+ rowHeights.clear()
652
686
  pageWidthsPdf.clear()
653
687
  pageHeightsPdf.clear()
654
688
  for ((_, h, pdfSize) in result) {
@@ -656,6 +690,14 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
656
690
  pageWidthsPdf.add(pdfSize.first)
657
691
  pageHeightsPdf.add(pdfSize.second)
658
692
  }
693
+ if (isTwoUp) {
694
+ val rowCount = (renderer.pageCount + 1) / 2
695
+ for (r in 0 until rowCount) {
696
+ val h0 = pageHeights.getOrElse(2 * r) { 0 }
697
+ val h1 = pageHeights.getOrElse(2 * r + 1) { 0 }
698
+ rowHeights.add(maxOf(h0, h1))
699
+ }
700
+ }
659
701
  ensurePlaceholders(renderer)
660
702
  scrollToPage(initialPage.coerceIn(0, (renderer.pageCount - 1).coerceAtLeast(0)))
661
703
  updateVisiblePages()
@@ -666,38 +708,85 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
666
708
  private fun ensurePlaceholders(renderer: PdfRenderer) {
667
709
  container.removeAllViews()
668
710
  val pageCount = renderer.pageCount
711
+ val marginBetween = marginBetweenPages()
669
712
  pageViews = MutableList(pageCount) { null }
670
713
  overlayViews = MutableList(pageCount) { null }
671
714
  while (pageAnnotations.size < pageCount) {
672
715
  pageAnnotations.add(mutableListOf())
673
716
  }
674
- for (i in 0 until pageCount) {
675
- val frame = FrameLayout(context).apply {
676
- layoutParams = LinearLayout.LayoutParams(
677
- LinearLayout.LayoutParams.MATCH_PARENT,
678
- pageHeights[i]
679
- ).apply { setMargins(0, 0, 0, 16) }
680
- setBackgroundColor(Color.LTGRAY)
717
+ if (isTwoUpMode() && rowHeights.isNotEmpty()) {
718
+ val rowCount = rowHeights.size
719
+ for (r in 0 until rowCount) {
720
+ val rowLayout = LinearLayout(context).apply {
721
+ orientation = LinearLayout.HORIZONTAL
722
+ layoutParams = LinearLayout.LayoutParams(
723
+ LinearLayout.LayoutParams.MATCH_PARENT,
724
+ rowHeights[r]
725
+ ).apply { setMargins(0, 0, 0, marginBetween) }
726
+ }
727
+ for (col in 0..1) {
728
+ val i = 2 * r + col
729
+ if (i >= pageCount) break
730
+ val frame = FrameLayout(context).apply {
731
+ layoutParams = LinearLayout.LayoutParams(
732
+ 0,
733
+ LinearLayout.LayoutParams.MATCH_PARENT,
734
+ 1f
735
+ ).apply { setMargins(0, 0, if (col == 0) 8 else 0, 0) }
736
+ setBackgroundColor(Color.LTGRAY)
737
+ }
738
+ val overlay = AnnotationOverlayView(context, i).apply {
739
+ layoutParams = FrameLayout.LayoutParams(
740
+ FrameLayout.LayoutParams.MATCH_PARENT,
741
+ FrameLayout.LayoutParams.MATCH_PARENT
742
+ )
743
+ }
744
+ frame.addView(overlay)
745
+ rowLayout.addView(frame)
746
+ overlayViews[i] = overlay
747
+ }
748
+ container.addView(rowLayout)
681
749
  }
682
- val overlay = AnnotationOverlayView(context, i).apply {
683
- layoutParams = FrameLayout.LayoutParams(
684
- FrameLayout.LayoutParams.MATCH_PARENT,
685
- FrameLayout.LayoutParams.MATCH_PARENT
686
- )
750
+ } else {
751
+ for (i in 0 until pageCount) {
752
+ val frame = FrameLayout(context).apply {
753
+ layoutParams = LinearLayout.LayoutParams(
754
+ LinearLayout.LayoutParams.MATCH_PARENT,
755
+ pageHeights[i]
756
+ ).apply { setMargins(0, 0, 0, marginBetween) }
757
+ setBackgroundColor(Color.LTGRAY)
758
+ }
759
+ val overlay = AnnotationOverlayView(context, i).apply {
760
+ layoutParams = FrameLayout.LayoutParams(
761
+ FrameLayout.LayoutParams.MATCH_PARENT,
762
+ FrameLayout.LayoutParams.MATCH_PARENT
763
+ )
764
+ }
765
+ frame.addView(overlay)
766
+ container.addView(frame)
767
+ overlayViews[i] = overlay
687
768
  }
688
- frame.addView(overlay)
689
- container.addView(frame)
690
- overlayViews[i] = overlay
691
769
  }
692
770
  }
693
771
 
694
772
  private fun currentPageFromScroll(): Int {
695
773
  if (pageHeights.isEmpty()) return 0
696
774
  val scrollY = scrollView.scrollY
775
+ val gap = marginBetweenPages()
776
+ if (isTwoUpMode() && rowHeights.isNotEmpty()) {
777
+ var offset = 0
778
+ for (r in rowHeights.indices) {
779
+ val rowH = rowHeights[r]
780
+ if (scrollY < offset + rowH / 2) return (2 * r).coerceAtMost(pageHeights.size - 1)
781
+ if (scrollY < offset + rowH) return (2 * r + 1).coerceAtMost(pageHeights.size - 1)
782
+ offset += rowH + gap
783
+ }
784
+ return (pageHeights.size - 1).coerceAtLeast(0)
785
+ }
697
786
  var offset = 0
698
787
  for (i in pageHeights.indices) {
699
788
  if (scrollY < offset + pageHeights[i] / 2) return i
700
- offset += pageHeights[i] + 16
789
+ offset += pageHeights[i] + gap
701
790
  }
702
791
  return (pageHeights.size - 1).coerceAtLeast(0)
703
792
  }
@@ -711,7 +800,7 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
711
800
  for (i in pageViews.indices) {
712
801
  if (i in from..to) continue
713
802
  val iv = pageViews.getOrNull(i) ?: continue
714
- val frame = container.getChildAt(i) as? FrameLayout ?: continue
803
+ val frame = getFrameForPage(i) ?: continue
715
804
  val bmp = (iv.drawable as? BitmapDrawable)?.bitmap
716
805
  iv.setImageBitmap(null)
717
806
  bmp?.recycle()
@@ -728,11 +817,10 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
728
817
 
729
818
  private fun renderPageToView(pageIndex: Int) {
730
819
  val renderer = pdfRenderer ?: return
731
- val frame = container.getChildAt(pageIndex) as? FrameLayout ?: return
732
- // Frame always has overlay; only skip if we already have an ImageView for this page
820
+ val frame = getFrameForPage(pageIndex) ?: return
733
821
  if (pageViews.getOrNull(pageIndex) != null) return
734
822
  val page = renderer.openPage(pageIndex)
735
- val w = width.coerceAtLeast(1)
823
+ val w = if (isTwoUpMode()) (width / 2).coerceAtLeast(1) else width.coerceAtLeast(1)
736
824
  val h = pageHeights[pageIndex]
737
825
  val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
738
826
  page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
@@ -754,17 +842,7 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
754
842
 
755
843
  private fun notifyPageChange() {
756
844
  if (pageHeights.isEmpty()) return
757
- val scrollY = scrollView.scrollY
758
- var offset = 0
759
- var detectedPage = 0
760
- for (i in pageHeights.indices) {
761
- if (scrollY < offset + pageHeights[i] / 2) {
762
- detectedPage = i
763
- break
764
- }
765
- offset += pageHeights[i] + 16
766
- detectedPage = i
767
- }
845
+ val detectedPage = currentPageFromScroll()
768
846
  if (detectedPage != lastEmittedPageIndex) {
769
847
  lastEmittedPageIndex = detectedPage
770
848
  onPageChange(mapOf(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@june24/expo-pdf-reader",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "A PDF reader for Expo apps with annotation and zoom controls",
5
5
  "homepage": "git@gitlab.com:june_241/expo-pdf-reader.git",
6
6
  "main": "build/index.js",