From ccde4c175c289d22840a4abf5ff76c1dd5f70e3f Mon Sep 17 00:00:00 2001 From: t29mato Date: Tue, 3 Dec 2024 21:22:23 +0900 Subject: [PATCH 1/3] Callback for clicking on annotations #76 --- streamlit_pdf_viewer/__init__.py | 12 +++++++-- .../frontend/src/PdfViewer.vue | 27 ++++++++++++++++--- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/streamlit_pdf_viewer/__init__.py b/streamlit_pdf_viewer/__init__.py index 886be9e8..54fff1a0 100644 --- a/streamlit_pdf_viewer/__init__.py +++ b/streamlit_pdf_viewer/__init__.py @@ -1,7 +1,7 @@ import base64 import os from pathlib import Path -from typing import Union, List, Optional +from typing import Union, List, Optional, Callable import streamlit.components.v1 as components import json @@ -39,7 +39,8 @@ def pdf_viewer( resolution_boost: int = 1, scroll_to_page: int = None, scroll_to_annotation: int = None, -): + on_annotation_click: Optional[Callable[[dict], None]] = None, + ): """ pdf_viewer function to display a PDF file in a Streamlit app. @@ -59,6 +60,7 @@ def pdf_viewer( :param resolution_boost: Boost the resolution by a factor from 2 to 10. Defaults to 1. :param scroll_to_page: Scroll to a specific page in the PDF. The parameter is an integer, which represent the positional value of the page. E.g. 1, will be the first page. Defaults to None. :param scroll_to_annotation: Scroll to a specific annotation in the PDF. The parameter is an integer, which represent the positional value of the annotation. E.g. 1, will be the first annotation. Defaults to None. + :param on_annotation_click: A callback function that will be called when an annotation is clicked. The function should accept a single argument, which is the annotation that was clicked. Defaults to None. The function reads the PDF file (from a file path, URL, or binary data), encodes it in base64, and uses a Streamlit component to render it in the app. It supports optional annotations and adjustable margins. @@ -118,6 +120,12 @@ def pdf_viewer( scroll_to_page=scroll_to_page, scroll_to_annotation=scroll_to_annotation ) + + # Execute the custom callback function + if component_value and 'clicked_annotation' in component_value: + clicked_annotation = component_value['clicked_annotation'] + if on_annotation_click is not None and callable(on_annotation_click): + on_annotation_click(clicked_annotation) return component_value diff --git a/streamlit_pdf_viewer/frontend/src/PdfViewer.vue b/streamlit_pdf_viewer/frontend/src/PdfViewer.vue index 48810165..9900b381 100644 --- a/streamlit_pdf_viewer/frontend/src/PdfViewer.vue +++ b/streamlit_pdf_viewer/frontend/src/PdfViewer.vue @@ -2,12 +2,20 @@
-
-
-
+
+
+
+
-
@@ -55,6 +63,16 @@ export default { }) }); + const handleAnnotationClick = (annotation, index) => { + // Convert the Proxy object to a plain object + const serializedAnnotation = JSON.parse(JSON.stringify(annotation)); + + // Send data to Streamlit + Streamlit.setComponentValue({ + clicked_annotation: { index, ...serializedAnnotation }, + }); + }; + const renderText = props.args.render_text === true const pdfContainerStyle = computed(() => ({ @@ -355,6 +373,7 @@ export default { return { filteredAnnotations, + handleAnnotationClick, getAnnotationStyle, pdfContainerStyle, pdfViewerStyle, From 6a44f5e4c3696f494ac8abddbd8472fe88ad2201 Mon Sep 17 00:00:00 2001 From: t29mato Date: Thu, 12 Dec 2024 19:30:05 +0900 Subject: [PATCH 2/3] added documentation for on_annotation_click on README.md --- README.md | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 79bc73b3..7f3bbcad 100644 --- a/README.md +++ b/README.md @@ -67,8 +67,8 @@ In the following table the list of parameters that can be provided to the `pdf_v | pages_to_render | Filter the rendering to a specific set of pages. By default, all pages are rendered. | | render_text | Enable a layer of text on top of the PDF document. The text may be selected and copied. **NOTE** to avoid breaking existing deployments, we made this optional at first, also considering that having many annotations might interfere with the copy-paste. | scroll_to_page | Scroll to a specific page when the component is rendered. Default is None. Require ints and ignores the parameters below zero. | -| scroll_to_annotation | Scroll to a specific annotation when the component is rendered. Default is None. Mutually exclusive with `scroll_to_page`. Raise an exception if used with `scroll_to_page` | - +| scroll_to_annotation | Scroll to a specific annotation when the component is rendered. Default is None. Mutually exclusive with `scroll_to_page`. Raise an exception if used with `scroll_to_page` | +| on_annotation_click | Callback function that is called when an annotation is clicked. The function receives the annotation as a parameter. | ### Annotation format The annotations format has been derived from the [Grobid's coordinate formats](https://grobid.readthedocs.io/en/latest/Coordinates-in-PDF/), which are described as a list of "bounding boxes". @@ -91,6 +91,44 @@ Here an example: The example shown in our screenshot can be found [here](resources/annotations.json). +### How to use on_annotation_click + +```python +from streamlit_pdf_viewer import pdf_viewer + +annotations = [ + { + "page": 1, + "x": 220, + "y": 155, + "height": 22, + "width": 65, + "color": "red" + }, + { + "page": 1, + "x": 220, + "y": 155, + "height": 22, + "width": 65, + "color": "red" + } +] + +def my_custom_annotation_handler(annotation): + print(f"Annotation {annotation} clicked.") + +pdf_viewer( + "path/to/pdf", + on_annotation_click=my_custom_annotation_handler, + annotations=annotations +) + +``` + + +### + ## Developers notes ### Environment From 571fbc5ce09e5ff68cdc3d5e5f9e1990da107fe6 Mon Sep 17 00:00:00 2001 From: t29mato Date: Thu, 12 Dec 2024 19:38:48 +0900 Subject: [PATCH 3/3] added the relationship between the render_text and on_annotation_click on README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f3bbcad..2d7d3351 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ In the following table the list of parameters that can be provided to the `pdf_v | input | The source of the PDF file. Accepts a file path, URL, or binary data. | | width | Width of the PDF viewer in pixels. It defaults to 700 pixels. | | height | Height of the PDF viewer in pixels. If not provided, the viewer shows the whole content. | -| annotations | A list of annotations to be overlaid on the PDF. Format is described [here](#annotation-format). | +| annotations | A list of annotations to be overlaid on the PDF. Format is described [here](#annotation-format). | | pages_vertical_spacing | The vertical space (in pixels) between each page of the PDF. Defaults to 2 pixels. | | annotation_outline_size | Size of the outline around each annotation in pixels. Defaults to 1 pixel. | | rendering | Type of rendering: `unwrap` (default), `legacy_iframe`, or `legacy_embed`. The default value, `unwrap` shows the PDF document using pdf.js, and supports the visualisation of annotations. Other values are `legacy_iframe` and `legacy_embed` which use the legacy approach of injecting the document into an `` or `