Given the following code:
import gradio as gr
from PIL import Image, ImageDraw, ImageFont
import pymupdf
import pytesseract
from paddleocr import PaddleOCR
from functools import lru_cache
from datetime import datetime
import numpy as np


logs_msg = ""
loaded_doc_len = 0

@lru_cache(maxsize=None)
def load_paddle_ocr_engine(ocr_version='PP-OCRv5'):
    """
    params:
        lang
            'chinese_cht' for Traditional Chinese (poorer accuracy) | 'ch' for Simplified Chinese (better accuracy)
        ocr_version
            'PP-OCRv5'
    """
    print("Loading PaddleOCR... Current datetime utc:", datetime.utcnow())
    ocr_engine = PaddleOCR(

        ocr_version=ocr_version,
    )
    print("Done... Current datetime utc:", datetime.utcnow())

    return ocr_engine

def load_pdf_as_images(file):
    """
    Load PDF file into a list of PIL images (one per page)
    """
    doc = pymupdf.open(file, filetype="pdf")
    pages = []
    for page in doc:
        pix = page.get_pixmap()
        img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
        pages.append(img)
    return pages

def image_ocr_tesseract(image):
    """
    Run Tesseract OCR on a PIL image and extract bounding boxes plus texts.
    Returns list of dicts with keys: 'bbox' and 'text'
    """
    image = image.convert("RGB")
    data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT)
    ocr_results = []
    n_boxes = len(data['level'])
    for i in range(n_boxes):
        text = data['text'][i].strip()
        if text == "":
            continue
        x, y, w, h = data['left'][i], data['top'][i], data['width'][i], data['height'][i]
        ocr_results.append({"bbox": [x, y, x+w, y+h], "text": text})
    return ocr_results

def paddle_ocr(image: Image.Image):
    """
    Runs PaddleOCR on a PIL image, returns list of dict {'bbox': [x0,y0,x1,y1], 'text': str}
    """
    global logs_msg
    model = load_paddle_ocr_engine()
    _img = image.convert("RGB")
    data = np.array(_img)
    result = model.predict(data) #Convert Image to ndarray for paddle predict
    ocr_results = []
    
    for res in result:
        res.print()
        for i in range(0, len(res["rec_polys"])):
            #print(f"Line Results: {res["rec_polys"][i]} -> {res["rec_texts"][i]}")
            _text = res["rec_texts"][i]
            _polygonData = res["rec_polys"][i]
            # Convert 4 points bbox to standard [x0, y0, x1, y1] rectangle for your renderer
            xmin = min(pt[0] for pt in _polygonData)
            xmax = max(pt[0] for pt in _polygonData)
            ymin = min(pt[1] for pt in _polygonData)
            ymax = max(pt[1] for pt in _polygonData)
            bbox = [int(xmin), int(ymin), int(xmax), int(ymax)]
            ocr_results.append({"bbox": bbox, "text": _text})
            
    return ocr_results

def render_ocr_boxes(image, ocr_results):
    global logs_msg
    draw = ImageDraw.Draw(image)
    try:
        font = ImageFont.truetype("arial.ttf", 12)
    except IOError:
        font = ImageFont.load_default()
    for res in ocr_results:
        bbox = res["bbox"]
        text = res["text"]
        draw.rectangle(bbox, outline="red", width=2)
        text_pos = (bbox[0], bbox[1] - 18 if bbox[1] > 18 else bbox[1] + 2)
        draw.text(text_pos, text, fill="red", font=font)
        logs_msg += f"{bbox} -> {text}\n"
    return image

def load_documents(uploaded_files):
    """
    Load uploaded files (images and PDFs) into list of PIL images
    """
    images = []
    for f in uploaded_files:
        if f.name.lower().endswith(".pdf"):
            images.extend(load_pdf_as_images(f))
        else:
            img = Image.open(f).convert("RGB")
            images.append(img)
    return images

def start_ocr(model, uploaded_files):
    global logs_msg, loaded_doc_len
    if uploaded_files is None or len(uploaded_files) == 0:
        logs_msg += "No files uploaded.\n"
        return None, None, logs_msg, None, 0
    
    logs_msg += "Loading documents...\n"
    images = load_documents(uploaded_files)
    logs_msg += f"{len(images)} page(s) loaded.\n"
    loaded_doc_len = len(images)
    
    ocr_results_list = []
    processed_images = []
    
    for i, img in enumerate(images):
        logs_msg += f"Processing page {i+1}...\n"
        if model == "Tesseract":
            ocr_res = image_ocr_tesseract(img)
        else:
            ocr_res = paddle_ocr(img)
        logs_msg += f"Found {len(ocr_res)} text boxes.\n"
        ocr_results_list.append(ocr_res)
        processed_img = render_ocr_boxes(img.copy(), ocr_res)
        processed_images.append(processed_img)
        
    logs_msg += "OCR processing completed.\n"
    
    return processed_images, ocr_results_list, logs_msg, images, 0  # processed_images, ocr_results, logs, original_images, page_idx

# Pagination Helpers
def on_original_prev(page_index, images):
    if images is None or len(images) == 0:
        return None, "0 / 0", 0
    new_index = max(0, page_index -1)
    return gr.update(interactive=(new_index == 0)), images[new_index], gr.update(value=f"{new_index +1} / {len(images)}"), new_index

def on_original_next(page_index, images):
    if images is None or len(images) == 0:
        return None, "0 / 0", 0
    new_index = min(len(images) -1, page_index +1)
    return gr.update(interactive=(new_index == len(images))),images[new_index], gr.update(f"{new_index +1} / {len(images)}"), new_index

def on_processed_prev(page_index, processed_images):
    if processed_images is None or len(processed_images) == 0:
        return None, "0 / 0", 0
    new_index = max(0, page_index -1)
    return gr.update(interactive=(new_index == 0)),processed_images[new_index], gr.update(f"{new_index +1} / {len(processed_images)}"), new_index

def on_processed_next(page_index, processed_images):
    if processed_images is None or len(processed_images) == 0:
        return None, "0 / 0", 0
    new_index = min(len(processed_images) -1, page_index +1)
    return gr.update(interactive=(new_index == len(processed_images))),processed_images[new_index], gr.update(f"{new_index +1} / {len(processed_images)}"), new_index

# Enable Run OCR button only if files uploaded
def update_run_ocr_btn(files):
    if files is None or len(files) == 0:
        return gr.update(interactive=False)
    return gr.update(interactive=True)

# Reset everything
def on_reset():
    return (
        None, None, "", None, None, None,
        0, 0,
        "0 / 0", "0 / 0",
        gr.update(visible=False), gr.update(visible=False),
        gr.update(visible=False), gr.update(visible=False),
        gr.update(interactive=False),  # Run OCR button
        gr.update(interactive=False),   # Reset button
        gr.File(file_types=['.png', '.jpg', '.jpeg', '.bmp', '.pdf'], file_count="multiple", label="Upload Document (Image or PDF)", value=None),
    )

# Main OCR trigger
def on_run_ocr(model, uploaded_files):
    global loaded_doc_len
    processed_images, ocr_results_list, logs_str, original_images, page_index = start_ocr(model, uploaded_files)
    if original_images is None:
        # Clean state if none
        return on_reset()

    n_pages = loaded_doc_len
    show_pagination = n_pages > 1
    page_text = f"1 / {n_pages}"

    return (
        processed_images[0],         # processed image to display
        original_images[0],          # original image to display (for first page)
        logs_str,
        processed_images,
        ocr_results_list,
        original_images,
        0, 0,                       # reset indexes
        gr.update(value=page_text, visible=show_pagination), gr.update(value=page_text, visible=show_pagination),       # pagination texts for both
        gr.update(visible=show_pagination), gr.update(visible=show_pagination),
        gr.update(visible=show_pagination), gr.update(visible=show_pagination),
        gr.update(interactive=True), # Run OCR button enabled
        gr.update(interactive=True)  # Reset button enabled
    )


with gr.Blocks() as demo:
    gr.Markdown("## OCR Demo")

    with gr.Row():
        model_selector = gr.Dropdown(choices=["Tesseract", "Paddle"], label="Select OCR Model", value="Tesseract")
        upload = gr.File(file_types=['.png', '.jpg', '.jpeg', '.bmp', '.pdf'], file_count="multiple", label="Upload Document (Image or PDF)")
        with gr.Column():
            btn_ocr = gr.Button("Run OCR", interactive=False)
            reset_btn = gr.Button("Reset", interactive=False)

    with gr.Row():
        with gr.Column():
            gr.Markdown("**Original Document**")
            original_img = gr.Image(label="Original Document", interactive=False)
            with gr.Row():
                original_prev_btn = gr.Button("Previous Page", visible=False)
                original_next_btn = gr.Button("Next Page", visible=False)
            original_page_num = gr.Textbox(label="Page", interactive=False, max_lines=1, value="0 / 0", visible=False)
        with gr.Column():
            gr.Markdown("**OCR Processed Document**")
            processed_img = gr.Image(label="OCR Processed Document", interactive=False)
            with gr.Row():
                processed_prev_btn = gr.Button("Previous Page", visible=False)
                processed_next_btn = gr.Button("Next Page", visible=False)
            processed_page_num = gr.Textbox(label="Page", interactive=False, max_lines=1, value="0 / 0", visible=False)

    logs = gr.Textbox(label="Process Logs", interactive=False, lines=10)

    # States
    state_ocr_results = gr.State()
    state_images = gr.State()
    state_processed_images = gr.State()
    state_original_page_index = gr.State(value=0)
    state_processed_page_index = gr.State(value=0)

    # Events
    upload.change(update_run_ocr_btn, inputs=upload, outputs=btn_ocr)

    btn_ocr.click(
        on_run_ocr,
        inputs=[model_selector, upload],
        outputs=[
            processed_img, original_img, logs,
            state_processed_images, state_ocr_results, state_images,
            state_processed_page_index, state_original_page_index,
            processed_page_num, original_page_num,
            processed_prev_btn, processed_next_btn,
            original_prev_btn, original_next_btn,
            btn_ocr, reset_btn
        ]
    )

    processed_prev_btn.click(
        on_processed_prev,
        inputs=[state_processed_page_index, state_processed_images],
        outputs=[processed_prev_btn, processed_img, processed_page_num, state_processed_page_index]
    )
    processed_next_btn.click(
        on_processed_next,
        inputs=[state_processed_page_index, state_processed_images],
        outputs=[processed_next_btn, processed_img, processed_page_num, state_processed_page_index]
    )

    original_prev_btn.click(
        on_original_prev,
        inputs=[state_original_page_index, state_images],
        outputs=[original_prev_btn, original_img, original_page_num, state_original_page_index]
    )
    original_next_btn.click(
        on_original_next,
        inputs=[state_original_page_index, state_images],
        outputs=[original_next_btn, original_img, original_page_num, state_original_page_index]
    )

    reset_btn.click(
        on_reset,
        inputs=[],
        outputs=[
            processed_img, original_img, logs,
            state_processed_images, state_ocr_results, state_images,
            state_processed_page_index, state_original_page_index,
            processed_page_num, original_page_num,
            processed_prev_btn, processed_next_btn,
            original_prev_btn, original_next_btn,
            btn_ocr, reset_btn,
            upload,
        ]
    )
    
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
demo.launch()

Please fix the page number display (problems: page number not update when page change in both original and processed)