@june24/expo-pdf-reader 0.1.27 → 0.1.29
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.
|
@@ -95,6 +95,8 @@ class ExpoPdfReaderView(
|
|
|
95
95
|
private var windowStart = 0 // pageEntries[0]-н absolute page index
|
|
96
96
|
private var chunkLoading = false // chunk дуусах хүртэл дахин trigger хийхгүй
|
|
97
97
|
private var currentViewWidth = 0 // chunk load-д дахин хэрэглэнэ
|
|
98
|
+
/** Thumbnail panel нээх/хаах зэргээр RN өргөн өөрчлөгдөнө — reflow-ийн суурь */
|
|
99
|
+
private var lastHostWidthForPdf = 0
|
|
98
100
|
private var loadingFooter: View? = null
|
|
99
101
|
|
|
100
102
|
companion object {
|
|
@@ -133,42 +135,61 @@ class ExpoPdfReaderView(
|
|
|
133
135
|
private var maxZoom = 5.0f
|
|
134
136
|
private var currentZoom = 1.0f
|
|
135
137
|
|
|
138
|
+
/** RN / Drawer зэрэг дээд scroll хуудас pinch-ийг scroll болгож идэвхжүүлэхээс сэргийлнэ */
|
|
139
|
+
private fun propagateDisallowInterceptFrom(view: View, disallow: Boolean) {
|
|
140
|
+
var p: ViewParent? = view.parent
|
|
141
|
+
while (p is ViewGroup) {
|
|
142
|
+
p.requestDisallowInterceptTouchEvent(disallow)
|
|
143
|
+
p = p.parent
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
136
147
|
/**
|
|
137
|
-
* continuous / twoup / twoupcontinuous — pinch + zoom үед
|
|
138
|
-
*
|
|
148
|
+
* continuous / twoup / twoupcontinuous — pinch + zoom хийсэн үед pan (хөндлөн/босоо),
|
|
149
|
+
* scroll-ийг идэвхгүй болгоно. Zoom = 1 үед хэвийн босоо scroll.
|
|
139
150
|
*/
|
|
140
151
|
private inner class ZoomableScrollView(ctx: Context) : ScrollView(ctx) {
|
|
141
|
-
|
|
142
|
-
var
|
|
152
|
+
|
|
153
|
+
private var panX = 0f
|
|
154
|
+
private var panY = 0f
|
|
155
|
+
|
|
156
|
+
private fun isZoomedForPan(): Boolean =
|
|
157
|
+
activeTool == null && currentZoom > 1.02f
|
|
143
158
|
|
|
144
159
|
private val pinch = ScaleGestureDetector(
|
|
145
160
|
ctx,
|
|
146
161
|
object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
|
147
162
|
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
|
|
148
163
|
if (activeTool != null) return false
|
|
164
|
+
stopNestedScroll()
|
|
165
|
+
this@ExpoPdfReaderView.propagateDisallowInterceptFrom(this@ZoomableScrollView, true)
|
|
149
166
|
parent?.requestDisallowInterceptTouchEvent(true)
|
|
150
167
|
return true
|
|
151
168
|
}
|
|
152
169
|
|
|
153
170
|
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
|
154
171
|
if (activeTool != null) return false
|
|
155
|
-
applyZoom(
|
|
172
|
+
applyZoom(
|
|
173
|
+
(currentZoom * detector.scaleFactor).coerceIn(minZoom, maxZoom),
|
|
174
|
+
detector.focusX,
|
|
175
|
+
detector.focusY
|
|
176
|
+
)
|
|
156
177
|
return true
|
|
157
178
|
}
|
|
158
179
|
|
|
159
180
|
override fun onScaleEnd(detector: ScaleGestureDetector) {
|
|
160
181
|
parent?.requestDisallowInterceptTouchEvent(false)
|
|
182
|
+
this@ExpoPdfReaderView.propagateDisallowInterceptFrom(this@ZoomableScrollView, false)
|
|
161
183
|
}
|
|
162
184
|
}
|
|
163
185
|
)
|
|
164
|
-
private var panLastX = 0f
|
|
165
|
-
private var panLastY = 0f
|
|
166
186
|
|
|
167
187
|
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
|
168
188
|
if (activeTool != null) return super.onInterceptTouchEvent(ev)
|
|
169
|
-
// 2+ хуруу эсвэл pinch эхэлсэн үед ScrollView босоо scroll intercept хийхгүй (scroll + zoom өрсөлдөнө)
|
|
170
|
-
if (!allowVerticalScrollFromTouch) return false
|
|
171
189
|
if (ev.pointerCount > 1 || pinch.isInProgress) return false
|
|
190
|
+
if (isZoomedForPan() && ev.actionMasked == MotionEvent.ACTION_DOWN) {
|
|
191
|
+
return true
|
|
192
|
+
}
|
|
172
193
|
return super.onInterceptTouchEvent(ev)
|
|
173
194
|
}
|
|
174
195
|
|
|
@@ -179,36 +200,56 @@ class ExpoPdfReaderView(
|
|
|
179
200
|
stopNestedScroll()
|
|
180
201
|
}
|
|
181
202
|
if (pinch.isInProgress || ev.pointerCount > 1) {
|
|
203
|
+
this@ExpoPdfReaderView.propagateDisallowInterceptFrom(this@ZoomableScrollView, true)
|
|
182
204
|
parent?.requestDisallowInterceptTouchEvent(true)
|
|
183
205
|
}
|
|
206
|
+
if (ev.actionMasked == MotionEvent.ACTION_UP ||
|
|
207
|
+
ev.actionMasked == MotionEvent.ACTION_CANCEL
|
|
208
|
+
) {
|
|
209
|
+
this@ExpoPdfReaderView.propagateDisallowInterceptFrom(this@ZoomableScrollView, false)
|
|
210
|
+
}
|
|
184
211
|
}
|
|
185
212
|
return super.dispatchTouchEvent(ev)
|
|
186
213
|
}
|
|
187
214
|
|
|
188
215
|
override fun onTouchEvent(ev: MotionEvent): Boolean {
|
|
189
216
|
if (activeTool != null) return super.onTouchEvent(ev)
|
|
217
|
+
|
|
190
218
|
if (pinch.isInProgress || ev.pointerCount > 1) {
|
|
191
219
|
return true
|
|
192
220
|
}
|
|
193
|
-
|
|
221
|
+
|
|
222
|
+
if (isZoomedForPan()) {
|
|
194
223
|
when (ev.actionMasked) {
|
|
195
224
|
MotionEvent.ACTION_DOWN -> {
|
|
196
|
-
|
|
197
|
-
|
|
225
|
+
stopNestedScroll()
|
|
226
|
+
panX = ev.x
|
|
227
|
+
panY = ev.y
|
|
198
228
|
}
|
|
199
229
|
MotionEvent.ACTION_MOVE -> {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
230
|
+
container.translationX += ev.x - panX
|
|
231
|
+
container.translationY += ev.y - panY
|
|
232
|
+
panX = ev.x
|
|
233
|
+
panY = ev.y
|
|
234
|
+
clampContainerTranslationsInPlace()
|
|
205
235
|
}
|
|
236
|
+
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { }
|
|
206
237
|
}
|
|
207
238
|
return true
|
|
208
239
|
}
|
|
209
|
-
|
|
240
|
+
|
|
210
241
|
return super.onTouchEvent(ev)
|
|
211
242
|
}
|
|
243
|
+
|
|
244
|
+
override fun canScrollVertically(direction: Int): Boolean {
|
|
245
|
+
if (isZoomedForPan()) return false
|
|
246
|
+
return super.canScrollVertically(direction)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
override fun canScrollHorizontally(direction: Int): Boolean {
|
|
250
|
+
if (isZoomedForPan()) return false
|
|
251
|
+
return super.canScrollHorizontally(direction)
|
|
252
|
+
}
|
|
212
253
|
}
|
|
213
254
|
|
|
214
255
|
// ── UI ───────────────────────────────────────────────────────────────────
|
|
@@ -244,13 +285,18 @@ class ExpoPdfReaderView(
|
|
|
244
285
|
override fun onScrollStateChanged(rv: RecyclerView, newState: Int) {
|
|
245
286
|
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
|
246
287
|
val lm = rv.layoutManager as? LinearLayoutManager ?: return
|
|
247
|
-
|
|
248
|
-
|
|
288
|
+
var pos = lm.findFirstCompletelyVisibleItemPosition()
|
|
289
|
+
if (pos < 0) pos = lm.findFirstVisibleItemPosition()
|
|
290
|
+
if (pos < 0) return
|
|
249
291
|
if (pos != currentPageIndex) {
|
|
250
292
|
currentPageIndex = pos
|
|
251
293
|
// Хуудас солигдоход zoom-г reset хийнэ
|
|
252
294
|
(pager as? ZoomRecyclerView)?.resetZoom()
|
|
253
295
|
onPageChange(mapOf("currentPage" to pos, "totalPage" to totalPages))
|
|
296
|
+
} else {
|
|
297
|
+
// scrollToPage() нь currentPageIndex-ийг урьдчилан тохируулдаг тул энд орохгүй —
|
|
298
|
+
// гэхдээ zoom transform + Fabric layout-оос хуудас шинэчлэгдэхгүй үлдэхээс сэргийлнэ
|
|
299
|
+
(pager as? ZoomRecyclerView)?.resetZoom()
|
|
254
300
|
}
|
|
255
301
|
}
|
|
256
302
|
}
|
|
@@ -268,8 +314,33 @@ class ExpoPdfReaderView(
|
|
|
268
314
|
startLoad(it)
|
|
269
315
|
}
|
|
270
316
|
}
|
|
271
|
-
//
|
|
272
|
-
|
|
317
|
+
// RN: thumbnail panel нээх/хаахад өргөн өөрчлөгдөнө; Fabric child requestLayout сул → reflow шаардлагатай
|
|
318
|
+
if (renderer != null && isLayoutReady && lastHostWidthForPdf > 0 && width > 0) {
|
|
319
|
+
val dw = kotlin.math.abs(width - lastHostWidthForPdf)
|
|
320
|
+
if (dw > 4) {
|
|
321
|
+
post { reflowForHostWidthChange() }
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Хостын өргөн өөрчлөгдсөний дараа PDF-ийг шинэ өргөнөөр дахин тохируулна */
|
|
327
|
+
private fun reflowForHostWidthChange() {
|
|
328
|
+
val w = width.takeIf { it > 0 } ?: return
|
|
329
|
+
if (renderer == null) return
|
|
330
|
+
if (w <= 0) return
|
|
331
|
+
if (lastHostWidthForPdf > 0 && kotlin.math.abs(w - lastHostWidthForPdf) <= 4) return
|
|
332
|
+
lastHostWidthForPdf = w
|
|
333
|
+
currentViewWidth = w
|
|
334
|
+
when (displayMode) {
|
|
335
|
+
"single" -> {
|
|
336
|
+
forcePagerLayout()
|
|
337
|
+
singleAdapter?.notifyDataSetChanged()
|
|
338
|
+
forcePagerLayout()
|
|
339
|
+
val p = currentPageIndex.coerceIn(0, maxOf(0, totalPages - 1))
|
|
340
|
+
post { scrollToPage(p, smooth = false) }
|
|
341
|
+
}
|
|
342
|
+
else -> triggerRender()
|
|
343
|
+
}
|
|
273
344
|
}
|
|
274
345
|
|
|
275
346
|
override fun onDetachedFromWindow() {
|
|
@@ -536,25 +607,46 @@ class ExpoPdfReaderView(
|
|
|
536
607
|
// Zoom
|
|
537
608
|
// ─────────────────────────────────────────────────────────────────────────
|
|
538
609
|
|
|
539
|
-
|
|
610
|
+
/**
|
|
611
|
+
* Scroll mode: pivot (0,0) + translation — pinch төв (focus) тогтвортой үлдэхийн тулд translation-ийг
|
|
612
|
+
* scrollX/scrollY-тай нийлүүлэн шинэчилнэ (ZoomRecyclerView-тай ижил математик).
|
|
613
|
+
*/
|
|
614
|
+
private fun applyZoom(
|
|
615
|
+
newZoom: Float,
|
|
616
|
+
pinchFocusX: Float = Float.NaN,
|
|
617
|
+
pinchFocusY: Float = Float.NaN
|
|
618
|
+
) {
|
|
619
|
+
val prevZoom = currentZoom
|
|
540
620
|
currentZoom = newZoom.coerceIn(minZoom, maxZoom)
|
|
541
621
|
container.pivotX = 0f
|
|
542
622
|
container.pivotY = 0f
|
|
543
|
-
|
|
544
|
-
container.scaleY = currentZoom
|
|
623
|
+
|
|
545
624
|
if (displayMode != "single") {
|
|
546
625
|
if (currentZoom <= 1.02f) {
|
|
547
|
-
scrollView.allowVerticalScrollFromTouch = true
|
|
548
626
|
container.translationX = 0f
|
|
549
627
|
container.translationY = 0f
|
|
550
|
-
} else {
|
|
551
|
-
|
|
628
|
+
} else if (!pinchFocusX.isNaN() && !pinchFocusY.isNaN() && prevZoom > 0.01f) {
|
|
629
|
+
val af = currentZoom / prevZoom
|
|
630
|
+
if (kotlin.math.abs(af - 1f) > 1e-5f) {
|
|
631
|
+
val scrX = scrollView.scrollX.toFloat()
|
|
632
|
+
val scrY = scrollView.scrollY.toFloat()
|
|
633
|
+
val tx = container.translationX
|
|
634
|
+
val ty = container.translationY
|
|
635
|
+
val contentFx = pinchFocusX + scrX
|
|
636
|
+
val contentFy = pinchFocusY + scrY
|
|
637
|
+
container.translationX = contentFx - (contentFx - tx) * af
|
|
638
|
+
container.translationY = contentFy - (contentFy - ty) * af
|
|
639
|
+
}
|
|
552
640
|
}
|
|
553
641
|
}
|
|
642
|
+
container.scaleX = currentZoom
|
|
643
|
+
container.scaleY = currentZoom
|
|
644
|
+
if (displayMode != "single" && currentZoom > 1.02f) {
|
|
645
|
+
clampContainerTranslationsInPlace()
|
|
646
|
+
}
|
|
554
647
|
}
|
|
555
648
|
|
|
556
|
-
|
|
557
|
-
private fun applyContainerPanDelta(dx: Float, dy: Float) {
|
|
649
|
+
private fun clampContainerTranslationsInPlace() {
|
|
558
650
|
val ch = container.height.toFloat()
|
|
559
651
|
val cw = container.width.toFloat()
|
|
560
652
|
if (ch <= 0f || cw <= 0f) return
|
|
@@ -562,8 +654,8 @@ class ExpoPdfReaderView(
|
|
|
562
654
|
val scaledW = cw * currentZoom
|
|
563
655
|
val vw = scrollView.width.toFloat()
|
|
564
656
|
val vh = scrollView.height.toFloat()
|
|
565
|
-
var tx = container.translationX
|
|
566
|
-
var ty = container.translationY
|
|
657
|
+
var tx = container.translationX
|
|
658
|
+
var ty = container.translationY
|
|
567
659
|
if (scaledW <= vw) tx = 0f else tx = tx.coerceIn(vw - scaledW, 0f)
|
|
568
660
|
if (scaledH <= vh) ty = 0f else ty = ty.coerceIn(vh - scaledH, 0f)
|
|
569
661
|
container.translationX = tx
|
|
@@ -662,10 +754,17 @@ class ExpoPdfReaderView(
|
|
|
662
754
|
|
|
663
755
|
private fun scrollToPage(pageIndex: Int, smooth: Boolean = true) {
|
|
664
756
|
if (displayMode == "single") {
|
|
665
|
-
|
|
666
|
-
else pager.scrollToPosition(pageIndex)
|
|
757
|
+
pager.stopScroll()
|
|
667
758
|
currentPageIndex = pageIndex
|
|
668
759
|
onPageChange(mapOf("currentPage" to pageIndex, "totalPage" to totalPages))
|
|
760
|
+
// scrollToPosition нь заримдаа layout/bind хойшлуулна (RN Fabric); post + forcePagerLayout
|
|
761
|
+
pager.post {
|
|
762
|
+
if (smooth) pager.smoothScrollToPosition(pageIndex)
|
|
763
|
+
else pager.scrollToPosition(pageIndex)
|
|
764
|
+
forcePagerLayout()
|
|
765
|
+
singleAdapter?.notifyItemChanged(pageIndex)
|
|
766
|
+
(pager as? ZoomRecyclerView)?.resetZoom()
|
|
767
|
+
}
|
|
669
768
|
return
|
|
670
769
|
}
|
|
671
770
|
if (pageEntries.isEmpty()) return
|
|
@@ -743,7 +842,6 @@ class ExpoPdfReaderView(
|
|
|
743
842
|
container.scaleY = 1f
|
|
744
843
|
container.translationX = 0f
|
|
745
844
|
container.translationY = 0f
|
|
746
|
-
scrollView.allowVerticalScrollFromTouch = true
|
|
747
845
|
container.removeAllViews()
|
|
748
846
|
pageEntries.clear()
|
|
749
847
|
loadingFooter = null
|
|
@@ -771,6 +869,7 @@ class ExpoPdfReaderView(
|
|
|
771
869
|
"height" to (this@ExpoPdfReaderView.height / density).toDouble()
|
|
772
870
|
)
|
|
773
871
|
)
|
|
872
|
+
lastHostWidthForPdf = this@ExpoPdfReaderView.width.takeIf { it > 0 } ?: viewWidth
|
|
774
873
|
}
|
|
775
874
|
}
|
|
776
875
|
|
|
@@ -865,7 +964,7 @@ class ExpoPdfReaderView(
|
|
|
865
964
|
private suspend fun renderTwoUpRange(
|
|
866
965
|
pdf: PdfRenderer, pageCount: Int, viewWidth: Int, startPage: Int, endPage: Int
|
|
867
966
|
) {
|
|
868
|
-
val halfWidth = viewWidth / 2
|
|
967
|
+
val halfWidth = (viewWidth / 2).coerceAtLeast(1)
|
|
869
968
|
var i = startPage
|
|
870
969
|
while (i < endPage) {
|
|
871
970
|
val leftBmp = renderPageBitmap(pdf, i, halfWidth)
|
|
@@ -929,12 +1028,21 @@ class ExpoPdfReaderView(
|
|
|
929
1028
|
}
|
|
930
1029
|
}
|
|
931
1030
|
|
|
1031
|
+
private fun resolveViewWidthForRender(): Int {
|
|
1032
|
+
val w = when {
|
|
1033
|
+
currentViewWidth > 0 -> currentViewWidth
|
|
1034
|
+
width > 0 -> width
|
|
1035
|
+
else -> resources.displayMetrics.widthPixels
|
|
1036
|
+
}
|
|
1037
|
+
return w.coerceAtLeast(1)
|
|
1038
|
+
}
|
|
1039
|
+
|
|
932
1040
|
private suspend fun loadNextChunk() {
|
|
933
1041
|
val pdf = renderer ?: return
|
|
934
1042
|
val start = renderedUpTo + 1
|
|
935
1043
|
if (start >= totalPages) return
|
|
936
1044
|
val end = minOf(start + PAGE_CHUNK, totalPages)
|
|
937
|
-
val viewWidth =
|
|
1045
|
+
val viewWidth = resolveViewWidthForRender()
|
|
938
1046
|
|
|
939
1047
|
withContext(Dispatchers.Main) { removeLoadingFooter() }
|
|
940
1048
|
|
|
@@ -997,7 +1105,7 @@ class ExpoPdfReaderView(
|
|
|
997
1105
|
val prevStart = maxOf(0, windowStart - PAGE_CHUNK)
|
|
998
1106
|
val prevEnd = windowStart
|
|
999
1107
|
if (prevStart >= prevEnd) return
|
|
1000
|
-
val viewWidth =
|
|
1108
|
+
val viewWidth = resolveViewWidthForRender()
|
|
1001
1109
|
|
|
1002
1110
|
data class PageBmp(val bmp: Bitmap, val idx: Int, val pdfW: Float, val pdfH: Float)
|
|
1003
1111
|
val pages = mutableListOf<PageBmp>()
|
|
@@ -1034,8 +1142,8 @@ class ExpoPdfReaderView(
|
|
|
1034
1142
|
val prevStart = maxOf(0, windowStart - PAGE_CHUNK)
|
|
1035
1143
|
val prevEnd = windowStart
|
|
1036
1144
|
if (prevStart >= prevEnd) return
|
|
1037
|
-
val viewWidth =
|
|
1038
|
-
val halfW = viewWidth / 2
|
|
1145
|
+
val viewWidth = resolveViewWidthForRender()
|
|
1146
|
+
val halfW = (viewWidth / 2).coerceAtLeast(1)
|
|
1039
1147
|
|
|
1040
1148
|
data class RowBmps(val leftIdx: Int, val leftBmp: Bitmap, val rightBmp: Bitmap?)
|
|
1041
1149
|
val rows = mutableListOf<RowBmps>()
|
|
@@ -1171,7 +1279,7 @@ class ExpoPdfReaderView(
|
|
|
1171
1279
|
*/
|
|
1172
1280
|
private fun jumpToPage(targetPage: Int) {
|
|
1173
1281
|
val pdf = renderer ?: return
|
|
1174
|
-
val viewWidth =
|
|
1282
|
+
val viewWidth = resolveViewWidthForRender()
|
|
1175
1283
|
renderJob?.cancel()
|
|
1176
1284
|
renderJob = scope.launch {
|
|
1177
1285
|
try {
|
|
@@ -1290,14 +1398,15 @@ class ExpoPdfReaderView(
|
|
|
1290
1398
|
|
|
1291
1399
|
private suspend fun renderPageBitmap(pdf: PdfRenderer, index: Int, targetWidth: Int): Bitmap =
|
|
1292
1400
|
withContext(pdfDispatcher) {
|
|
1401
|
+
val safeWidth = targetWidth.coerceAtLeast(1)
|
|
1293
1402
|
pdf.openPage(index).use { page ->
|
|
1294
1403
|
if (index < pagePdfW.size) {
|
|
1295
1404
|
pagePdfW[index] = page.width
|
|
1296
1405
|
pagePdfH[index] = page.height
|
|
1297
1406
|
}
|
|
1298
|
-
val s =
|
|
1407
|
+
val s = safeWidth.toFloat() / page.width.toFloat()
|
|
1299
1408
|
val h = (page.height * s).toInt().coerceAtLeast(1)
|
|
1300
|
-
val bmp = Bitmap.createBitmap(
|
|
1409
|
+
val bmp = Bitmap.createBitmap(safeWidth, h, Bitmap.Config.ARGB_8888)
|
|
1301
1410
|
bmp.eraseColor(Color.WHITE)
|
|
1302
1411
|
page.render(bmp, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
|
|
1303
1412
|
bmp
|
|
@@ -1307,7 +1416,11 @@ class ExpoPdfReaderView(
|
|
|
1307
1416
|
private fun safeCloseRenderer() {
|
|
1308
1417
|
try { renderer?.close(); fileDescriptor?.close() }
|
|
1309
1418
|
catch (_: Exception) { }
|
|
1310
|
-
finally {
|
|
1419
|
+
finally {
|
|
1420
|
+
renderer = null
|
|
1421
|
+
fileDescriptor = null
|
|
1422
|
+
lastHostWidthForPdf = 0
|
|
1423
|
+
}
|
|
1311
1424
|
}
|
|
1312
1425
|
|
|
1313
1426
|
private suspend fun resolveFile(url: String): File = withContext(pdfDispatcher) {
|
|
@@ -1384,7 +1497,18 @@ class ExpoPdfReaderView(
|
|
|
1384
1497
|
|
|
1385
1498
|
holder.loadJob = scope.launch {
|
|
1386
1499
|
val pdf = renderer ?: return@launch
|
|
1387
|
-
val
|
|
1500
|
+
val viewWidth = this@ExpoPdfReaderView.width
|
|
1501
|
+
if (viewWidth <= 0) {
|
|
1502
|
+
withContext(Dispatchers.Main) {
|
|
1503
|
+
post {
|
|
1504
|
+
if (holder.boundPage == position && this@ExpoPdfReaderView.width > 0) {
|
|
1505
|
+
notifyItemChanged(position)
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
return@launch
|
|
1510
|
+
}
|
|
1511
|
+
val bmp = renderPageBitmap(pdf, position, viewWidth)
|
|
1388
1512
|
val pdfW = pagePdfW.getOrElse(position) { 1 }.toFloat()
|
|
1389
1513
|
val pdfH = pagePdfH.getOrElse(position) { 1 }.toFloat()
|
|
1390
1514
|
withContext(Dispatchers.Main) {
|
|
@@ -1447,6 +1571,14 @@ class ExpoPdfReaderView(
|
|
|
1447
1571
|
|
|
1448
1572
|
private val zoomDetector = ScaleGestureDetector(context,
|
|
1449
1573
|
object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
|
1574
|
+
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
|
|
1575
|
+
if (activeTool != null) return false
|
|
1576
|
+
stopScroll()
|
|
1577
|
+
this@ExpoPdfReaderView.propagateDisallowInterceptFrom(this@ZoomRecyclerView, true)
|
|
1578
|
+
parent?.requestDisallowInterceptTouchEvent(true)
|
|
1579
|
+
return true
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1450
1582
|
override fun onScale(d: ScaleGestureDetector): Boolean {
|
|
1451
1583
|
val newScale = (currentScale * d.scaleFactor).coerceIn(minZoom, maxZoom)
|
|
1452
1584
|
val af = newScale / currentScale
|
|
@@ -1457,6 +1589,11 @@ class ExpoPdfReaderView(
|
|
|
1457
1589
|
clampAndApply()
|
|
1458
1590
|
return true
|
|
1459
1591
|
}
|
|
1592
|
+
|
|
1593
|
+
override fun onScaleEnd(detector: ScaleGestureDetector) {
|
|
1594
|
+
parent?.requestDisallowInterceptTouchEvent(false)
|
|
1595
|
+
this@ExpoPdfReaderView.propagateDisallowInterceptFrom(this@ZoomRecyclerView, false)
|
|
1596
|
+
}
|
|
1460
1597
|
}
|
|
1461
1598
|
)
|
|
1462
1599
|
|
|
@@ -1467,8 +1604,14 @@ class ExpoPdfReaderView(
|
|
|
1467
1604
|
stopScroll()
|
|
1468
1605
|
}
|
|
1469
1606
|
if (ev.pointerCount > 1 || zoomDetector.isInProgress) {
|
|
1607
|
+
this@ExpoPdfReaderView.propagateDisallowInterceptFrom(this@ZoomRecyclerView, true)
|
|
1470
1608
|
parent?.requestDisallowInterceptTouchEvent(true)
|
|
1471
1609
|
}
|
|
1610
|
+
if (ev.actionMasked == MotionEvent.ACTION_UP ||
|
|
1611
|
+
ev.actionMasked == MotionEvent.ACTION_CANCEL
|
|
1612
|
+
) {
|
|
1613
|
+
this@ExpoPdfReaderView.propagateDisallowInterceptFrom(this@ZoomRecyclerView, false)
|
|
1614
|
+
}
|
|
1472
1615
|
}
|
|
1473
1616
|
return super.dispatchTouchEvent(ev)
|
|
1474
1617
|
}
|
|
@@ -56,6 +56,8 @@ final class ExpoPdfReaderView: ExpoView {
|
|
|
56
56
|
// Zoom range: minZoom=1.0 (fit width), maxZoom=5.0 → pinch-to-zoom дэмжинэ
|
|
57
57
|
private var minZoom: CGFloat = 1.0
|
|
58
58
|
private var maxZoom: CGFloat = 5.0
|
|
59
|
+
/// RN thumbnail panel нээх/хаахад bounds өргөн өөрчлөгдөнө — scaleToFit дахин хийх суурь
|
|
60
|
+
private var lastHostWidthForPdf: CGFloat = 0
|
|
59
61
|
|
|
60
62
|
required init(appContext: AppContext? = nil) {
|
|
61
63
|
super.init(appContext: appContext)
|
|
@@ -164,11 +166,31 @@ final class ExpoPdfReaderView: ExpoView {
|
|
|
164
166
|
super.layoutSubviews()
|
|
165
167
|
pdfView.frame = bounds
|
|
166
168
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
guard let document = pdfView.document, document.pageCount > 0 else { return }
|
|
170
|
+
let w = bounds.width
|
|
171
|
+
guard w > 1 else { return }
|
|
172
|
+
|
|
173
|
+
// Анх: зөвхөн scaleFactor идэвхгүй үед fit (өмнөх зан)
|
|
174
|
+
if lastHostWidthForPdf <= 0 {
|
|
175
|
+
lastHostWidthForPdf = w
|
|
176
|
+
if pdfView.scaleFactor == 0 {
|
|
177
|
+
scaleToFitWidth()
|
|
178
|
+
}
|
|
179
|
+
return
|
|
171
180
|
}
|
|
181
|
+
|
|
182
|
+
// Thumbnail / side panel нээх эсвэл хаахад өргөн өөрчлөгдөнө → fit width дахин (жижиг үлдэхээс сэргийлнэ)
|
|
183
|
+
let dw = abs(w - lastHostWidthForPdf)
|
|
184
|
+
guard dw > 4 else { return }
|
|
185
|
+
|
|
186
|
+
let maxIdx = max(0, document.pageCount - 1)
|
|
187
|
+
let idx = min(max(0, lastPageIndex), maxIdx)
|
|
188
|
+
lastHostWidthForPdf = w
|
|
189
|
+
scaleToFitWidth()
|
|
190
|
+
if let page = document.page(at: idx) {
|
|
191
|
+
pdfView.go(to: page)
|
|
192
|
+
}
|
|
193
|
+
requestDisplayUpdate()
|
|
172
194
|
}
|
|
173
195
|
|
|
174
196
|
/// Custom hitTest:
|
|
@@ -198,6 +220,7 @@ final class ExpoPdfReaderView: ExpoView {
|
|
|
198
220
|
|
|
199
221
|
func load(url: URL) {
|
|
200
222
|
if pdfView.document?.documentURL == url { return }
|
|
223
|
+
lastHostWidthForPdf = 0
|
|
201
224
|
if let document = PDFDocument(url: url) {
|
|
202
225
|
pdfView.document = document
|
|
203
226
|
// New document → start from first page logically
|
|
@@ -209,6 +232,7 @@ final class ExpoPdfReaderView: ExpoView {
|
|
|
209
232
|
// Defer scaling until the view has correct bounds
|
|
210
233
|
DispatchQueue.main.async {
|
|
211
234
|
self.scaleToFitWidth()
|
|
235
|
+
self.lastHostWidthForPdf = self.bounds.width
|
|
212
236
|
// Document load хийгдсэний дараа pending annotation-уудыг load хийх
|
|
213
237
|
if let pending = self.pendingAnnotations {
|
|
214
238
|
self.pendingAnnotations = nil
|
|
@@ -286,6 +310,7 @@ final class ExpoPdfReaderView: ExpoView {
|
|
|
286
310
|
self.pdfView.go(to: page)
|
|
287
311
|
}
|
|
288
312
|
self.scaleToFitWidth()
|
|
313
|
+
self.lastHostWidthForPdf = self.bounds.width
|
|
289
314
|
}
|
|
290
315
|
}
|
|
291
316
|
|
package/package.json
CHANGED