@june24/expo-pdf-reader 0.1.20 → 0.1.22
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?>()
|
|
@@ -383,6 +384,7 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
383
384
|
addView(scrollView)
|
|
384
385
|
scrollView.viewTreeObserver.addOnScrollChangedListener {
|
|
385
386
|
notifyPageChange()
|
|
387
|
+
scrollView.post { updateVisiblePages() }
|
|
386
388
|
}
|
|
387
389
|
viewTreeObserver.addOnGlobalLayoutListener {
|
|
388
390
|
if (width <= 0) {
|
|
@@ -552,13 +554,38 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
552
554
|
notifyAnnotationChange()
|
|
553
555
|
}
|
|
554
556
|
|
|
557
|
+
private fun isTwoUpMode(): Boolean = displayMode == "twoUp" || displayMode == "twoUpContinuous"
|
|
558
|
+
|
|
555
559
|
private fun marginBetweenPages(): Int = if (displayMode == "single") 0 else 16
|
|
556
560
|
|
|
561
|
+
private fun getFrameForPage(pageIndex: Int): FrameLayout? {
|
|
562
|
+
if (pageIndex !in pageHeights.indices) return null
|
|
563
|
+
return if (isTwoUpMode()) {
|
|
564
|
+
val row = pageIndex / 2
|
|
565
|
+
val col = pageIndex % 2
|
|
566
|
+
if (row >= container.childCount) null
|
|
567
|
+
else (container.getChildAt(row) as? LinearLayout)?.getChildAt(col) as? FrameLayout
|
|
568
|
+
} else {
|
|
569
|
+
container.getChildAt(pageIndex) as? FrameLayout
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
557
573
|
private fun scrollToPage(pageIndex: Int) {
|
|
558
574
|
if (pageHeights.isEmpty()) return
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
575
|
+
val idx = pageIndex.coerceIn(0, pageHeights.size - 1)
|
|
576
|
+
val gap = marginBetweenPages()
|
|
577
|
+
val offset = if (isTwoUpMode() && rowHeights.isNotEmpty()) {
|
|
578
|
+
var sum = 0
|
|
579
|
+
for (r in 0 until idx / 2) {
|
|
580
|
+
sum += rowHeights[r] + gap
|
|
581
|
+
}
|
|
582
|
+
sum
|
|
583
|
+
} else {
|
|
584
|
+
var sum = 0
|
|
585
|
+
for (i in 0 until idx) {
|
|
586
|
+
sum += pageHeights[i] + gap
|
|
587
|
+
}
|
|
588
|
+
sum
|
|
562
589
|
}
|
|
563
590
|
scrollView.post { scrollView.smoothScrollTo(0, offset) }
|
|
564
591
|
}
|
|
@@ -598,6 +625,7 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
598
625
|
fileDescriptor?.close()
|
|
599
626
|
fileDescriptor = null
|
|
600
627
|
pageHeights.clear()
|
|
628
|
+
rowHeights.clear()
|
|
601
629
|
pageWidthsPdf.clear()
|
|
602
630
|
pageHeightsPdf.clear()
|
|
603
631
|
pageViews.clear()
|
|
@@ -632,33 +660,39 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
632
660
|
updateVisiblePages()
|
|
633
661
|
return
|
|
634
662
|
}
|
|
635
|
-
val viewHeight = height.coerceAtLeast(1)
|
|
636
663
|
val viewWidth = width.coerceAtLeast(1)
|
|
664
|
+
val viewHeight = height.coerceAtLeast(1)
|
|
665
|
+
val isTwoUp = displayMode == "twoUp" || displayMode == "twoUpContinuous"
|
|
637
666
|
val isSingle = displayMode == "single"
|
|
667
|
+
val effectiveWidth = if (isTwoUp) viewWidth / 2 else viewWidth
|
|
638
668
|
scope.launch {
|
|
639
|
-
val w =
|
|
669
|
+
val w = effectiveWidth
|
|
670
|
+
val minSlotHeight = if (isSingle) viewHeight else 0
|
|
640
671
|
val result = withContext(Dispatchers.Default) {
|
|
641
672
|
(0 until renderer.pageCount).map { i ->
|
|
642
673
|
val page = renderer.openPage(i)
|
|
643
674
|
try {
|
|
644
|
-
val
|
|
645
|
-
|
|
646
|
-
} else {
|
|
647
|
-
(w.toFloat() * page.height / page.width).toInt()
|
|
648
|
-
}
|
|
675
|
+
val naturalH = (w.toFloat() * page.height / page.width).toInt()
|
|
676
|
+
val pageH = if (isSingle) maxOf(naturalH, minSlotHeight) else naturalH
|
|
649
677
|
Triple(
|
|
650
678
|
i,
|
|
651
679
|
pageH,
|
|
652
680
|
Pair(page.width.toFloat(), page.height.toFloat())
|
|
653
681
|
)
|
|
654
682
|
} finally {
|
|
655
|
-
|
|
683
|
+
try {
|
|
684
|
+
page.close()
|
|
685
|
+
} catch (e: Exception) {
|
|
686
|
+
// Renderer may have been closed on main thread (e.g. resetRenderer)
|
|
687
|
+
}
|
|
656
688
|
}
|
|
657
689
|
}
|
|
658
690
|
}
|
|
659
691
|
if (!isActive) return@launch
|
|
660
692
|
withContext(Dispatchers.Main) {
|
|
693
|
+
if (pdfRenderer == null) return@launch
|
|
661
694
|
pageHeights.clear()
|
|
695
|
+
rowHeights.clear()
|
|
662
696
|
pageWidthsPdf.clear()
|
|
663
697
|
pageHeightsPdf.clear()
|
|
664
698
|
for ((_, h, pdfSize) in result) {
|
|
@@ -666,6 +700,14 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
666
700
|
pageWidthsPdf.add(pdfSize.first)
|
|
667
701
|
pageHeightsPdf.add(pdfSize.second)
|
|
668
702
|
}
|
|
703
|
+
if (isTwoUp) {
|
|
704
|
+
val rowCount = (renderer.pageCount + 1) / 2
|
|
705
|
+
for (r in 0 until rowCount) {
|
|
706
|
+
val h0 = pageHeights.getOrElse(2 * r) { 0 }
|
|
707
|
+
val h1 = pageHeights.getOrElse(2 * r + 1) { 0 }
|
|
708
|
+
rowHeights.add(maxOf(h0, h1))
|
|
709
|
+
}
|
|
710
|
+
}
|
|
669
711
|
ensurePlaceholders(renderer)
|
|
670
712
|
scrollToPage(initialPage.coerceIn(0, (renderer.pageCount - 1).coerceAtLeast(0)))
|
|
671
713
|
updateVisiblePages()
|
|
@@ -682,34 +724,81 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
682
724
|
while (pageAnnotations.size < pageCount) {
|
|
683
725
|
pageAnnotations.add(mutableListOf())
|
|
684
726
|
}
|
|
685
|
-
|
|
686
|
-
val
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
727
|
+
if (isTwoUpMode() && rowHeights.isNotEmpty()) {
|
|
728
|
+
val rowCount = rowHeights.size
|
|
729
|
+
for (r in 0 until rowCount) {
|
|
730
|
+
val rowLayout = LinearLayout(context).apply {
|
|
731
|
+
orientation = LinearLayout.HORIZONTAL
|
|
732
|
+
layoutParams = LinearLayout.LayoutParams(
|
|
733
|
+
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
734
|
+
rowHeights[r]
|
|
735
|
+
).apply { setMargins(0, 0, 0, marginBetween) }
|
|
736
|
+
}
|
|
737
|
+
for (col in 0..1) {
|
|
738
|
+
val i = 2 * r + col
|
|
739
|
+
if (i >= pageCount) break
|
|
740
|
+
val frame = FrameLayout(context).apply {
|
|
741
|
+
layoutParams = LinearLayout.LayoutParams(
|
|
742
|
+
0,
|
|
743
|
+
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
744
|
+
1f
|
|
745
|
+
).apply { setMargins(0, 0, if (col == 0) 8 else 0, 0) }
|
|
746
|
+
setBackgroundColor(Color.LTGRAY)
|
|
747
|
+
}
|
|
748
|
+
val overlay = AnnotationOverlayView(context, i).apply {
|
|
749
|
+
layoutParams = FrameLayout.LayoutParams(
|
|
750
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
751
|
+
FrameLayout.LayoutParams.MATCH_PARENT
|
|
752
|
+
)
|
|
753
|
+
}
|
|
754
|
+
frame.addView(overlay)
|
|
755
|
+
rowLayout.addView(frame)
|
|
756
|
+
overlayViews[i] = overlay
|
|
757
|
+
}
|
|
758
|
+
container.addView(rowLayout)
|
|
692
759
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
760
|
+
} else {
|
|
761
|
+
for (i in 0 until pageCount) {
|
|
762
|
+
val frame = FrameLayout(context).apply {
|
|
763
|
+
layoutParams = LinearLayout.LayoutParams(
|
|
764
|
+
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
765
|
+
pageHeights[i]
|
|
766
|
+
).apply { setMargins(0, 0, 0, marginBetween) }
|
|
767
|
+
setBackgroundColor(Color.LTGRAY)
|
|
768
|
+
}
|
|
769
|
+
val overlay = AnnotationOverlayView(context, i).apply {
|
|
770
|
+
layoutParams = FrameLayout.LayoutParams(
|
|
771
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
772
|
+
FrameLayout.LayoutParams.MATCH_PARENT
|
|
773
|
+
)
|
|
774
|
+
}
|
|
775
|
+
frame.addView(overlay)
|
|
776
|
+
container.addView(frame)
|
|
777
|
+
overlayViews[i] = overlay
|
|
698
778
|
}
|
|
699
|
-
frame.addView(overlay)
|
|
700
|
-
container.addView(frame)
|
|
701
|
-
overlayViews[i] = overlay
|
|
702
779
|
}
|
|
703
780
|
}
|
|
704
781
|
|
|
705
782
|
private fun currentPageFromScroll(): Int {
|
|
706
783
|
if (pageHeights.isEmpty()) return 0
|
|
707
784
|
val scrollY = scrollView.scrollY
|
|
708
|
-
var offset = 0
|
|
709
785
|
val gap = marginBetweenPages()
|
|
786
|
+
if (isTwoUpMode() && rowHeights.isNotEmpty()) {
|
|
787
|
+
var offset = 0
|
|
788
|
+
for (r in rowHeights.indices) {
|
|
789
|
+
val rowH = rowHeights[r]
|
|
790
|
+
if (scrollY < offset + rowH / 2) return (2 * r).coerceAtMost(pageHeights.size - 1)
|
|
791
|
+
if (scrollY < offset + rowH) return (2 * r + 1).coerceAtMost(pageHeights.size - 1)
|
|
792
|
+
offset += rowH + gap
|
|
793
|
+
}
|
|
794
|
+
return (pageHeights.size - 1).coerceAtLeast(0)
|
|
795
|
+
}
|
|
796
|
+
val viewportCenter = scrollY + scrollView.height / 2
|
|
797
|
+
var offset = 0
|
|
710
798
|
for (i in pageHeights.indices) {
|
|
711
|
-
|
|
712
|
-
|
|
799
|
+
val pageEnd = offset + pageHeights[i]
|
|
800
|
+
if (viewportCenter < pageEnd) return i
|
|
801
|
+
offset = pageEnd + gap
|
|
713
802
|
}
|
|
714
803
|
return (pageHeights.size - 1).coerceAtLeast(0)
|
|
715
804
|
}
|
|
@@ -717,13 +806,17 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
717
806
|
private fun updateVisiblePages() {
|
|
718
807
|
val renderer = pdfRenderer ?: return
|
|
719
808
|
val currentPage = currentPageFromScroll()
|
|
720
|
-
val from = (
|
|
721
|
-
|
|
809
|
+
val (from, to) = if (displayMode == "single") {
|
|
810
|
+
currentPage to currentPage
|
|
811
|
+
} else {
|
|
812
|
+
(currentPage - renderWindowBefore).coerceAtLeast(0) to
|
|
813
|
+
(currentPage + renderWindowAfter).coerceAtMost(renderer.pageCount - 1)
|
|
814
|
+
}
|
|
722
815
|
|
|
723
816
|
for (i in pageViews.indices) {
|
|
724
817
|
if (i in from..to) continue
|
|
725
818
|
val iv = pageViews.getOrNull(i) ?: continue
|
|
726
|
-
val frame =
|
|
819
|
+
val frame = getFrameForPage(i) ?: continue
|
|
727
820
|
val bmp = (iv.drawable as? BitmapDrawable)?.bitmap
|
|
728
821
|
iv.setImageBitmap(null)
|
|
729
822
|
bmp?.recycle()
|
|
@@ -740,11 +833,10 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
740
833
|
|
|
741
834
|
private fun renderPageToView(pageIndex: Int) {
|
|
742
835
|
val renderer = pdfRenderer ?: return
|
|
743
|
-
val frame =
|
|
744
|
-
// Frame always has overlay; only skip if we already have an ImageView for this page
|
|
836
|
+
val frame = getFrameForPage(pageIndex) ?: return
|
|
745
837
|
if (pageViews.getOrNull(pageIndex) != null) return
|
|
746
838
|
val page = renderer.openPage(pageIndex)
|
|
747
|
-
val w = width.coerceAtLeast(1)
|
|
839
|
+
val w = if (isTwoUpMode()) (width / 2).coerceAtLeast(1) else width.coerceAtLeast(1)
|
|
748
840
|
val h = pageHeights[pageIndex]
|
|
749
841
|
val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
|
|
750
842
|
page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
|
|
@@ -766,18 +858,7 @@ class ExpoPdfReaderView(context: Context, appContext: AppContext) : ExpoView(con
|
|
|
766
858
|
|
|
767
859
|
private fun notifyPageChange() {
|
|
768
860
|
if (pageHeights.isEmpty()) return
|
|
769
|
-
val
|
|
770
|
-
var offset = 0
|
|
771
|
-
var detectedPage = 0
|
|
772
|
-
val gap = marginBetweenPages()
|
|
773
|
-
for (i in pageHeights.indices) {
|
|
774
|
-
if (scrollY < offset + pageHeights[i] / 2) {
|
|
775
|
-
detectedPage = i
|
|
776
|
-
break
|
|
777
|
-
}
|
|
778
|
-
offset += pageHeights[i] + gap
|
|
779
|
-
detectedPage = i
|
|
780
|
-
}
|
|
861
|
+
val detectedPage = currentPageFromScroll()
|
|
781
862
|
if (detectedPage != lastEmittedPageIndex) {
|
|
782
863
|
lastEmittedPageIndex = detectedPage
|
|
783
864
|
onPageChange(mapOf(
|
package/package.json
CHANGED