branch: elpa/pdf-tools commit 8ddf4c709829ea0225771891a7b404900768d9f1 Author: lennonhill <@> Commit: Vedang Manerikar <ved.maneri...@gmail.com>
Make selection style configurable by introducing a new variable Make selection-style configurable via `pdf-view-selection-style`. A default value of `word` might be desirable, as selection is usually intended to select entire words is perceptually faster than glyph-based selection. This affects selections and highlights, but not search. Limitation: The selection type is fixed for all currently selected regions. Selecting multiple regions with different selection styles at the same time would require storing the selection style for every region and replacing the protocol for `cmd_addannot` with a more flexible one, e.g. the keyword-based one used by `cmd_renderpage`. Closes: #73 --- lisp/pdf-annot.el | 6 ++++-- lisp/pdf-cache.el | 4 ++-- lisp/pdf-info.el | 42 +++++++++++++++++++++++++----------------- lisp/pdf-isearch.el | 2 +- lisp/pdf-view.el | 29 ++++++++++++++++++++++++----- server/epdfinfo.c | 40 +++++++++++++++++++++++++++++----------- test/pdf-info-test.el | 9 +++++---- 7 files changed, 90 insertions(+), 42 deletions(-) diff --git a/lisp/pdf-annot.el b/lisp/pdf-annot.el index 7140e9ae14..3b6359342e 100644 --- a/lisp/pdf-annot.el +++ b/lisp/pdf-annot.el @@ -1052,7 +1052,8 @@ Return the new annotation." (when (and (eq type 'text) (> (length edges) 1)) (error "Edges argument should be a single edge-list for text annotations")) - (let* ((a (apply #'pdf-info-addannot + (let* ((selection-style pdf-view-selection-style) + (a (apply #'pdf-info-addannot page (if (eq type 'text) (car edges) @@ -1060,9 +1061,10 @@ Return the new annotation." (apply #'append (mapcar (lambda (e) - (pdf-info-getselection page e)) + (pdf-info-getselection page e selection-style)) edges)))) type + selection-style nil (if (not (eq type 'text)) edges))) (id (pdf-annot-get-id a))) diff --git a/lisp/pdf-cache.el b/lisp/pdf-cache.el index 650ff36acc..9c25e07d04 100644 --- a/lisp/pdf-cache.el +++ b/lisp/pdf-cache.el @@ -332,13 +332,13 @@ See also `pdf-info-renderpage-text-regions' and `pdf-cache-renderpage'." (if pdf-cache-image-inihibit (apply #'pdf-info-renderpage-text-regions - page width single-line-p nil selection) + page width single-line-p nil nil selection) (let ((hash (sxhash (format "%S" (cons 'renderpage-text-regions (cons single-line-p selection)))))) (or (pdf-cache-get-image page width width hash) (let ((data (apply #'pdf-info-renderpage-text-regions - page width single-line-p nil selection))) + page width single-line-p nil nil selection))) (pdf-cache-put-image page width data hash) data))))) diff --git a/lisp/pdf-info.el b/lisp/pdf-info.el index 40dfc3d17f..d3e17bf07d 100644 --- a/lisp/pdf-info.el +++ b/lisp/pdf-info.el @@ -1247,6 +1247,17 @@ of this element in the tree." 'outline (pdf-info--normalize-file-or-buffer file-or-buffer))) +(defun pdf-info--selection-style (selection-style) + "SELECTION-STYLE is the smallest unit of the selected region. + +It must be one of glyph, word or line. If it is none of these, we +fallback to glyph." + (cl-case selection-style + (glyph 0) + (word 1) + (line 2) + (t 0))) + (defun pdf-info-gettext (page edges &optional selection-style file-or-buffer) "Get text on PAGE according to EDGES. @@ -1263,30 +1274,21 @@ Return the text contained in the selection." (pdf-info--normalize-file-or-buffer file-or-buffer) page (mapconcat #'number-to-string edges " ") - (cl-case selection-style - (glyph 0) - (word 1) - (line 2) - (t 0)))) - -(defun pdf-info-getselection (page edges &optional selection-style - file-or-buffer) + (pdf-info--selection-style selection-style))) + +(defun pdf-info-getselection (page edges + &optional selection-style file-or-buffer) "Return the edges of the selection EDGES on PAGE. Arguments are the same as for `pdf-info-gettext'. Return a list of edges corresponding to the text that would be returned by the aforementioned function, when called with the same arguments." - (pdf-info-query 'getselection (pdf-info--normalize-file-or-buffer file-or-buffer) page (mapconcat #'number-to-string edges " ") - (cl-case selection-style - (glyph 0) - (word 1) - (line 2) - (t 0)))) + (pdf-info--selection-style selection-style))) (defun pdf-info-textregions (page &optional file-or-buffer) "Return a list of edges describing PAGE's text-layout." @@ -1419,7 +1421,9 @@ function." (pdf-info--normalize-file-or-buffer file-or-buffer) id)) -(defun pdf-info-addannot (page edges type &optional file-or-buffer &rest markup-edges) +(defun pdf-info-addannot (page edges type + &optional selection-style file-or-buffer + &rest markup-edges) "Add a new annotation to PAGE with EDGES of TYPE. FIXME: TYPE may be one of `text', `markup-highlight', ... . @@ -1436,6 +1440,7 @@ returns." (pdf-info--normalize-file-or-buffer file-or-buffer) page type + (pdf-info--selection-style selection-style) (mapconcat 'number-to-string edges " ") (mapcar (lambda (me) (mapconcat 'number-to-string me " ")) @@ -1610,6 +1615,8 @@ Return the data of the corresponding PNG image." (pdf-util-hexcolor value)) (:alpha (number-to-string value)) + (:selection-style + (number-to-string (pdf-info--selection-style value))) (otherwise value))) (push kw transformed) (push value transformed))) @@ -1618,14 +1625,14 @@ Return the data of the corresponding PNG image." (nreverse transformed)))) (defun pdf-info-renderpage-text-regions (page width single-line-p - &optional file-or-buffer + &optional selection-style file-or-buffer &rest regions) "Highlight text on PAGE with width WIDTH using REGIONS. REGIONS is a list determining foreground and background color and the regions to render. So each element should look like \(FG BG \(LEFT TOP RIGHT BOT\) \(LEFT TOP RIGHT BOT\) ... \) . The -rendering is text-aware. +rendering is text-aware and is controlled by SELECTION-STYLE. If SINGLE-LINE-P is non-nil, the edges in REGIONS are each supposed to be limited to a single line in the document. Setting @@ -1642,6 +1649,7 @@ Return the data of the corresponding PNG image." (apply #'pdf-info-renderpage page width file-or-buffer (apply #'append + `(:selection-style ,selection-style) (mapcar (lambda (elt) `(:foreground ,(pop elt) :background ,(pop elt) diff --git a/lisp/pdf-isearch.el b/lisp/pdf-isearch.el index 4d87a2e435..f5468eb56a 100644 --- a/lisp/pdf-isearch.el +++ b/lisp/pdf-isearch.el @@ -745,7 +745,7 @@ MATCH-BG LAZY-FG LAZY-BG\)." (pdf-view-display-image (pdf-view-create-image data :width width)))))))) (pdf-info-renderpage-text-regions - page width t nil + page width t nil nil `(,fg1 ,bg1 ,@(pdf-util-scale-pixel-to-relative current)) `(,fg2 ,bg2 ,@(pdf-util-scale-pixel-to-relative diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index 849be6a713..5cf7ac7daf 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -188,6 +188,15 @@ Issue a warning, if one of them is active in a PDF buffer." :group 'pdf-view :type '(repeat symbol)) +(defcustom pdf-view-selection-style 'glyph + "The current default selection style. + +Must be one of `glyph', `word', or `line'." + :group 'pdf-view + :type '(choice (const glyph) + (const word) + (const line))) + ;; * ================================================================== * ;; * Internal variables and macros @@ -1338,13 +1347,17 @@ Deactivate the region if DEACTIVATE-P is non-nil." (pdf-view-redisplay t))) (defun pdf-view-mouse-set-region (event &optional allow-extend-p - rectangle-p) + rectangle-p + selection-style) "Select a region of text using the mouse with mouse event EVENT. Allow for stacking of regions, if ALLOW-EXTEND-P is non-nil. Create a rectangular region, if RECTANGLE-P is non-nil. +Overwrite `pdf-view-selection-style' with SELECTION-STYLE, +which is one of `glyph', `word', or `line'. + Stores the region in `pdf-view-active-region'." (interactive "@e") (setq pdf-view--have-rectangle-region rectangle-p) @@ -1366,6 +1379,7 @@ Stores the region in `pdf-view-active-region'." (setq begin-inside-image-p nil) (posn-x-y pos))) (abs-begin (posn-x-y pos)) + (selection-style (or selection-style pdf-view-selection-style)) pdf-view-continuous region) (when (pdf-util-track-mouse-dragging (event 0.05) @@ -1418,7 +1432,8 @@ Stores the region in `pdf-view-active-region'." (pdf-util-scale-pixel-to-relative iregion)) (pdf-view-display-region (cons region pdf-view-active-region) - rectangle-p) + rectangle-p + selection-style) (pdf-util-scroll-to-edges iregion))))) (setq pdf-view-active-region (append pdf-view-active-region @@ -1441,7 +1456,7 @@ This is more useful for commands like (interactive "@e") (pdf-view-mouse-set-region event nil t)) -(defun pdf-view-display-region (&optional region rectangle-p) +(defun pdf-view-display-region (&optional region rectangle-p selection-style) ;; TODO: write documentation! (unless region (pdf-view-assert-active-region) @@ -1458,7 +1473,7 @@ This is more useful for commands like page width nil `(,(car colors) ,(cdr colors) 0.35 ,@region)) (pdf-info-renderpage-text-regions - page width nil nil + page width nil selection-style nil `(,(car colors) ,(cdr colors) ,@region))) :width width)))) @@ -1483,7 +1498,11 @@ This is more useful for commands like "Return the text of the active region as a list of strings." (pdf-view-assert-active-region) (mapcar - (apply-partially 'pdf-info-gettext (pdf-view-current-page)) + (lambda (edges) + (pdf-info-gettext + (pdf-view-current-page) + edges + pdf-view-selection-style)) pdf-view-active-region)) (defun pdf-view-extract-region-image (regions &optional page size diff --git a/server/epdfinfo.c b/server/epdfinfo.c index a5c1359e00..32898fa9fc 100644 --- a/server/epdfinfo.c +++ b/server/epdfinfo.c @@ -1367,12 +1367,15 @@ annotation_markup_get_text_regions (PopplerPage *page, PopplerAnnotTextMarkup *a * * @param page The page of the annotation. This is used to get the * text regions and pagesize. + * @param selection_style The selection style. * @param region The region to add. * @param garray[in,out] An array of PopplerQuadrilateral, where the * new quadrilaterals will be appended. */ static void -annotation_markup_append_text_region (PopplerPage *page, PopplerRectangle *region, +annotation_markup_append_text_region (PopplerPage *page, + PopplerSelectionStyle selection_style, + PopplerRectangle *region, GArray *garray) { gdouble height; @@ -1380,7 +1383,7 @@ annotation_markup_append_text_region (PopplerPage *page, PopplerRectangle *regio replacement. (poppler_page_get_selected_region returns a union of rectangles.) */ GList *regions = - poppler_page_get_selection_region (page, 1.0, POPPLER_SELECTION_GLYPH, region); + poppler_page_get_selection_region (page, 1.0, selection_style, region); GList *item; poppler_page_get_size (page, NULL, &height); @@ -1409,6 +1412,7 @@ annotation_markup_append_text_region (PopplerPage *page, PopplerRectangle *regio * * @param doc The document for which to create it. * @param type The type of the annotation. + * @param selection_style The selection style. * @param r The rectangle where annotation will end up on the page. * * @return The new annotation, or NULL, if the annotation type is @@ -1416,8 +1420,9 @@ annotation_markup_append_text_region (PopplerPage *page, PopplerRectangle *regio */ static PopplerAnnot* annotation_new (const epdfinfo_t *ctx, document_t *doc, PopplerPage *page, - const char *type, PopplerRectangle *r, - const command_arg_t *rest, char **error_msg) + const char *type, PopplerSelectionStyle selection_style, + PopplerRectangle *r, const command_arg_t *rest, + char **error_msg) { PopplerAnnot *a = NULL; @@ -1448,7 +1453,7 @@ annotation_new (const epdfinfo_t *ctx, document_t *doc, PopplerPage *page, ARG_EDGES, error_msg)); rr->x1 *= width; rr->x2 *= width; rr->y1 *= height; rr->y2 *= height; - annotation_markup_append_text_region (page, rr, garray); + annotation_markup_append_text_region (page, selection_style, rr, garray); } cerror_if_not (garray->len > 0, error_msg, "%s", "Unable to create empty markup annotation"); @@ -2339,7 +2344,7 @@ cmd_gettext(const epdfinfo_t *ctx, const command_arg_t *args) double width, height; gchar *text = NULL; - selection_style = xpoppler_validate_selection_style(selection_style); + selection_style = xpoppler_validate_selection_style (selection_style); page = poppler_document_get_page (doc, pn - 1); perror_if_not (page, "No such page %d", pn); poppler_page_get_size (page, &width, &height); @@ -2381,7 +2386,7 @@ const command_arg_type_t cmd_getselection_spec[] = { ARG_DOC, ARG_NATNUM, /* page number */ - ARG_EDGES, /* selection */ + ARG_EDGES, /* selection */ ARG_NATNUM /* selection-style */ }; @@ -2397,7 +2402,7 @@ cmd_getselection (const epdfinfo_t *ctx, const command_arg_t *args) PopplerPage *page = NULL; int i; - selection_style = xpoppler_validate_selection_style(selection_style); + selection_style = xpoppler_validate_selection_style (selection_style); page = poppler_document_get_page (doc, pn - 1); perror_if_not (page, "No such page %d", pn); poppler_page_get_size (page, &width, &height); @@ -2650,6 +2655,7 @@ const command_arg_type_t cmd_addannot_spec[] = ARG_DOC, ARG_NATNUM, /* page number */ ARG_STRING, /* type */ + ARG_NATNUM, /* selection-style */ ARG_EDGES_OR_POSITION, /* edges or position (uses default size) */ ARG_REST, /* markup regions */ }; @@ -2661,7 +2667,8 @@ cmd_addannot (const epdfinfo_t *ctx, const command_arg_t *args) document_t *doc = args->value.doc; gint pn = args[1].value.natnum; const char *type_string = args[2].value.string; - PopplerRectangle r = args[3].value.rectangle; + int selection_style = args[3].value.natnum; + PopplerRectangle r = args[4].value.rectangle; int i; PopplerPage *page = NULL; double width, height; @@ -2673,6 +2680,7 @@ cmd_addannot (const epdfinfo_t *ctx, const command_arg_t *args) gdouble y2; char *error_msg = NULL; + selection_style = xpoppler_validate_selection_style (selection_style); page = poppler_document_get_page (doc->pdf, pn - 1); perror_if_not (page, "Unable to get page %d", pn); poppler_page_get_size (page, &width, &height); @@ -2686,7 +2694,8 @@ cmd_addannot (const epdfinfo_t *ctx, const command_arg_t *args) r.y2 = height - r.y1; r.y1 = height - y2; - pa = annotation_new (ctx, doc, page, type_string, &r, &args[4], &error_msg); + pa = annotation_new (ctx, doc, page, type_string, selection_style, &r, &args[5], + &error_msg); perror_if_not (pa, "Creating annotation failed: %s", error_msg ? error_msg : "Reason unknown"); amap = poppler_annot_mapping_new (); @@ -3080,6 +3089,7 @@ cmd_renderpage (const epdfinfo_t *ctx, const command_arg_t *args) PopplerColor bg = { 65535, 0, 0 }; double alpha = 1.0; double line_width = 1.5; + PopplerSelectionStyle selection_style = POPPLER_SELECTION_GLYPH; PopplerRectangle cb = {0.0, 0.0, 1.0, 1.0}; int i = 0; @@ -3208,10 +3218,18 @@ cmd_renderpage (const epdfinfo_t *ctx, const command_arg_t *args) } poppler_page_render_selection (page, cr, r, NULL, - POPPLER_SELECTION_GLYPH, &fg, &bg); + selection_style, &fg, &bg); } } } + else if (! strcmp (keyword, ":selection-style")) + { + perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, + ARG_NATNUM, &error_msg), + "%s", error_msg); + ++i; + selection_style = xpoppler_validate_selection_style (rest_arg.value.natnum); + } else perror_if_not (0, "Unknown render command: %s", keyword); } diff --git a/test/pdf-info-test.el b/test/pdf-info-test.el index a8f9de9042..bdf807413d 100644 --- a/test/pdf-info-test.el +++ b/test/pdf-info-test.el @@ -164,10 +164,11 @@ annots) (when (memq 'markup-annotations (pdf-info-features)) - (push (pdf-info-addannot 1 '(0 0 1 1) 'squiggly '(0 0 1 1)) annots) - (push (pdf-info-addannot 1 '(0 0 1 1) 'highlight '(0 0 1 1)) annots) - (push (pdf-info-addannot 1 '(0 0 1 1) 'underline '(0 0 1 1)) annots) - (push (pdf-info-addannot 1 '(0 0 1 1) 'strike-out '(0 0 1 1)) annots)) + + (push (pdf-info-addannot 1 '(0 0 1 1) 'squiggly nil nil '(0 0 1 1)) annots) + (push (pdf-info-addannot 1 '(0 0 1 1) 'highlight 'word nil '(0 0 1 1)) annots) + (push (pdf-info-addannot 1 '(0 0 1 1) 'underline 'line nil '(0 0 1 1)) annots) + (push (pdf-info-addannot 1 '(0 0 1 1) 'strike-out 'glyph nil '(0 0 1 1)) annots)) (dolist (a annots) (pdf-info-delannot (cdr (assq 'id a)))) (should (= nannots (length (pdf-info-getannots)))))))