libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

fulltext.h (9713B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2026 Christian Grothoff
      4 
      5      libextractor is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libextractor is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libextractor; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19  */
     20 /**
     21  * @file plugins/fulltext.h
     22  * @brief helpers shared by the plugins implementing the "fulltext" option
     23  * @author Christian Grothoff
     24  *
     25  * The full text is reported as a single #EXTRACTOR_METATYPE_FULL_TEXT
     26  * item in UTF-8 with MIME type "text/plain".  Within a paragraph, all
     27  * whitespace is collapsed into a single space; paragraphs are separated
     28  * by exactly "\n\n"; there is no leading or trailing whitespace.
     29  */
     30 #ifndef FULLTEXT_H
     31 #define FULLTEXT_H
     32 
     33 #include "extractor.h"
     34 #include <stdint.h>
     35 
     36 #ifdef __cplusplus
     37 extern "C" {
     38 #endif
     39 
     40 /**
     41  * Largest full text we are willing to emit, in bytes (including the
     42  * terminating 0).  Must stay below MAX_META_DATA in
     43  * src/main/extractor_ipc.h; longer text is truncated at a character
     44  * boundary.
     45  */
     46 #define EXTRACTOR_FULLTEXT_MAX (16 * 1024 * 1024)
     47 
     48 
     49 /**
     50  * Check whether the comma-separated plugin configuration @a config
     51  * contains the token @a key (exactly, or followed by '=' or ':').
     52  *
     53  * @param config plugin configuration, may be NULL
     54  * @param key token to look for, e.g. "fulltext"
     55  * @return NULL if @a key is absent; otherwise a pointer to the
     56  *         character after @a key in @a config (one of '\0', ',', '='
     57  *         or ':')
     58  */
     59 const char *
     60 EXTRACTOR_config_find_ (const char *config,
     61                         const char *key);
     62 
     63 
     64 /**
     65  * Check whether the "fulltext" option is set in @a config.
     66  *
     67  * @param config plugin configuration, may be NULL
     68  * @return 1 if full-text extraction was requested, 0 if not
     69  */
     70 int
     71 EXTRACTOR_fulltext_requested_ (const char *config);
     72 
     73 
     74 /**
     75  * A growing UTF-8 text buffer that normalizes whitespace and paragraph
     76  * breaks as text is appended.
     77  */
     78 struct EXTRACTOR_FullText
     79 {
     80   /**
     81    * The text so far, 0-terminated if @e len > 0.
     82    */
     83   char *buf;
     84 
     85   /**
     86    * Number of bytes in @e buf, excluding the terminating 0.
     87    */
     88   size_t len;
     89 
     90   /**
     91    * Allocated size of @e buf.
     92    */
     93   size_t cap;
     94 
     95   /**
     96    * 1 if a space must precede the next non-space text.
     97    */
     98   int pending_space;
     99 
    100   /**
    101    * 1 if a paragraph break must precede the next non-space text.
    102    */
    103   int pending_par;
    104 
    105   /**
    106    * 1 if we ran into #EXTRACTOR_FULLTEXT_MAX or out of memory;
    107    * all further text is discarded.
    108    */
    109   int full;
    110 
    111   /**
    112    * Extraction context to keep alive while we work, may be NULL; set
    113    * with #EXTRACTOR_fulltext_set_context_().
    114    */
    115   struct EXTRACTOR_ExtractContext *ec;
    116 
    117   /**
    118    * Units of work done since the last keep-alive.
    119    */
    120   unsigned int ticks;
    121 };
    122 
    123 
    124 /**
    125  * Let @a ft keep the host informed that the plugin is still working
    126  * (out of process, a plugin that is silent for too long is presumed
    127  * hung): every few thousand characters appended, or calls to
    128  * #EXTRACTOR_fulltext_tick_(), `seek (cls, 0, SEEK_CUR)` is called
    129  * on @a ec.
    130  *
    131  * @param ft buffer to modify
    132  * @param ec extraction context of the plugin
    133  */
    134 void
    135 EXTRACTOR_fulltext_set_context_ (struct EXTRACTOR_FullText *ft,
    136                                  struct EXTRACTOR_ExtractContext *ec);
    137 
    138 
    139 /**
    140  * Account for one unit of work that did not append text (e.g. a tag
    141  * that was skipped), for the keep-alive.
    142  *
    143  * @param ft buffer being filled
    144  */
    145 void
    146 EXTRACTOR_fulltext_tick_ (struct EXTRACTOR_FullText *ft);
    147 
    148 
    149 /**
    150  * Initialize @a ft to the empty text.
    151  *
    152  * @param[out] ft buffer to initialize
    153  */
    154 void
    155 EXTRACTOR_fulltext_init_ (struct EXTRACTOR_FullText *ft);
    156 
    157 
    158 /**
    159  * Append @a len bytes of @a text.  Invalid UTF-8 is replaced by U+FFFD,
    160  * control characters are dropped, and any run of whitespace (including
    161  * line breaks and U+00A0) becomes a single space.
    162  *
    163  * @param ft buffer to append to
    164  * @param text text to append, need not be 0-terminated
    165  * @param len number of bytes in @a text
    166  */
    167 void
    168 EXTRACTOR_fulltext_append_ (struct EXTRACTOR_FullText *ft,
    169                             const char *text,
    170                             size_t len);
    171 
    172 
    173 /**
    174  * Append a 0-terminated string.
    175  *
    176  * @param ft buffer to append to
    177  * @param text 0-terminated text to append
    178  */
    179 void
    180 EXTRACTOR_fulltext_append_str_ (struct EXTRACTOR_FullText *ft,
    181                                 const char *text);
    182 
    183 
    184 /**
    185  * Append a single Unicode code point, subject to the same
    186  * normalization as #EXTRACTOR_fulltext_append_().
    187  *
    188  * @param ft buffer to append to
    189  * @param cp code point to append
    190  */
    191 void
    192 EXTRACTOR_fulltext_append_cp_ (struct EXTRACTOR_FullText *ft,
    193                                uint32_t cp);
    194 
    195 
    196 /**
    197  * Request a word break: the next text is separated from what came
    198  * before by a single space (unless a paragraph break is pending).
    199  *
    200  * @param ft buffer to modify
    201  */
    202 void
    203 EXTRACTOR_fulltext_space_ (struct EXTRACTOR_FullText *ft);
    204 
    205 
    206 /**
    207  * End the current paragraph: the next text is preceded by "\n\n".
    208  * Multiple calls without text in between have the same effect as one.
    209  *
    210  * @param ft buffer to modify
    211  */
    212 void
    213 EXTRACTOR_fulltext_paragraph_ (struct EXTRACTOR_FullText *ft);
    214 
    215 
    216 /**
    217  * Emit the text (if non-empty) as #EXTRACTOR_METATYPE_FULL_TEXT.
    218  *
    219  * @param ft buffer to emit
    220  * @param ec extraction context
    221  * @param plugin_name short name of the plugin
    222  * @return return value of the `proc` callback (0 if nothing was emitted)
    223  */
    224 int
    225 EXTRACTOR_fulltext_emit_ (const struct EXTRACTOR_FullText *ft,
    226                           struct EXTRACTOR_ExtractContext *ec,
    227                           const char *plugin_name);
    228 
    229 
    230 /**
    231  * Release the memory held by @a ft and reset it to the empty text.
    232  *
    233  * @param ft buffer to clean up
    234  */
    235 void
    236 EXTRACTOR_fulltext_clear_ (struct EXTRACTOR_FullText *ft);
    237 
    238 
    239 /**
    240  * Check whether @a line (already whitespace-trimmed or not) looks like
    241  * a page number or page label, e.g. "12", "- 12 -", "xiv", "Page 3",
    242  * "3 of 17", "[4]", "Seite 7".
    243  *
    244  * @param line text to check
    245  * @param len number of bytes in @a line
    246  * @return 1 if @a line looks like a page number, 0 if not
    247  */
    248 int
    249 EXTRACTOR_fulltext_is_page_number_ (const char *line,
    250                                     size_t len);
    251 
    252 
    253 /**
    254  * One line of text on a page, as collected by a plugin for paged
    255  * formats (PDF, DVI) before margin filtering.
    256  */
    257 struct EXTRACTOR_FullTextLine
    258 {
    259   /**
    260    * UTF-8 text of the line, 0-terminated, owned by the page.
    261    */
    262   char *text;
    263 
    264   /**
    265    * Vertical position of the line's baseline, relative to the page
    266    * height, in [0,1] with 0 at the top.  Negative if unknown.
    267    */
    268   double y;
    269 
    270   /**
    271    * 1 if this line ends a paragraph (the plugin decided that from the
    272    * layout, e.g. vertical gap, indentation, short last line).
    273    */
    274   int par_end;
    275 
    276   /**
    277    * Set by #EXTRACTOR_fulltext_filter_margins_() if the line is page
    278    * furniture (page number, running header/footer) and must be skipped.
    279    */
    280   int skip;
    281 };
    282 
    283 
    284 /**
    285  * All lines of one page, in reading order.
    286  */
    287 struct EXTRACTOR_FullTextPage
    288 {
    289   /**
    290    * Lines of the page.
    291    */
    292   struct EXTRACTOR_FullTextLine *lines;
    293 
    294   /**
    295    * Number of entries in @e lines.
    296    */
    297   unsigned int num_lines;
    298 
    299   /**
    300    * Allocated size of @e lines.
    301    */
    302   unsigned int cap_lines;
    303 };
    304 
    305 
    306 /**
    307  * Append a line to @a page.  @a text is copied.
    308  *
    309  * @param page page to append to
    310  * @param text UTF-8 text of the line
    311  * @param len number of bytes in @a text
    312  * @param y relative vertical position in [0,1], or negative if unknown
    313  * @param par_end 1 if the line ends a paragraph
    314  * @return 0 on success, -1 on allocation failure
    315  */
    316 int
    317 EXTRACTOR_fulltext_page_add_ (struct EXTRACTOR_FullTextPage *page,
    318                               const char *text,
    319                               size_t len,
    320                               double y,
    321                               int par_end);
    322 
    323 
    324 /**
    325  * Mark page furniture in @a pages as `skip`: the first/last lines of
    326  * a page within the top/bottom margin band that look like page numbers,
    327  * or whose text (with digits ignored) recurs in the same band on at
    328  * least half of the pages (when there are at least 3 pages).
    329  *
    330  * @param pages array of pages
    331  * @param num_pages number of entries in @a pages
    332  */
    333 void
    334 EXTRACTOR_fulltext_filter_margins_ (struct EXTRACTOR_FullTextPage *pages,
    335                                     unsigned int num_pages);
    336 
    337 
    338 /**
    339  * Append all non-skipped lines of @a pages to @a ft.  Lines are joined
    340  * with a space (words hyphenated across a line break are re-joined);
    341  * `par_end` lines end a paragraph.  A paragraph continues across page
    342  * boundaries unless the last line of the page has `par_end`.
    343  *
    344  * @param ft buffer to append to
    345  * @param pages array of pages
    346  * @param num_pages number of entries in @a pages
    347  */
    348 void
    349 EXTRACTOR_fulltext_append_pages_ (struct EXTRACTOR_FullText *ft,
    350                                   const struct EXTRACTOR_FullTextPage *pages,
    351                                   unsigned int num_pages);
    352 
    353 
    354 /**
    355  * Release the lines of @a page.
    356  *
    357  * @param page page to clean up
    358  */
    359 void
    360 EXTRACTOR_fulltext_page_clear_ (struct EXTRACTOR_FullTextPage *page);
    361 
    362 #ifdef __cplusplus
    363 }
    364 #endif
    365 
    366 #endif