LCOV - code coverage report
Current view: top level - src/base - pdf-text-filter.c (source / functions) Hit Total Coverage
Test: libgnupdf.info Lines: 73 102 71.6 %
Date: 2011-12-09 Functions: 9 9 100.0 %
Branches: 28 44 63.6 %

           Branch data     Line data    Source code
       1                 :            : /* -*- mode: C -*-
       2                 :            :  *
       3                 :            :  *       File:         pdf-text-filter.c
       4                 :            :  *       Date:         Fri Feb 25 23:58:56 2008
       5                 :            :  *
       6                 :            :  *       GNU PDF Library - Encoded Text Filters
       7                 :            :  *
       8                 :            :  */
       9                 :            : 
      10                 :            : /* Copyright (C) 2008-2011 Free Software Foundation, Inc. */
      11                 :            : 
      12                 :            : /* This program is free software: you can redistribute it and/or modify
      13                 :            :  * it under the terms of the GNU General Public License as published by
      14                 :            :  * the Free Software Foundation, either version 3 of the License, or
      15                 :            :  * (at your option) any later version.
      16                 :            :  *
      17                 :            :  * This program is distributed in the hope that it will be useful,
      18                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      19                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      20                 :            :  * GNU General Public License for more details.
      21                 :            :  *
      22                 :            :  * You should have received a copy of the GNU General Public License
      23                 :            :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      24                 :            :  */
      25                 :            : 
      26                 :            : #include <config.h>
      27                 :            : 
      28                 :            : #include <stdio.h>
      29                 :            : #include <string.h>
      30                 :            : 
      31                 :            : #include <pdf-text-filter.h>
      32                 :            : #include <pdf-text-context.h>
      33                 :            : #include <pdf-text-ucd.h>
      34                 :            : 
      35                 :            : /* Change Case of text */
      36                 :            : static pdf_bool_t
      37                 :         60 : pdf_text_filter_change_case (pdf_text_t              *text,
      38                 :            :                              enum unicode_case_type   new_case,
      39                 :            :                              pdf_error_t            **error)
      40                 :            : {
      41                 :            :   pdf_size_t i;
      42                 :            :   pdf_size_t n_words;
      43                 :            :   pdf_size_t worst_length;
      44                 :            :   pdf_size_t new_length;
      45                 :            :   pdf_char_t *new_data;
      46                 :            :   pdf_list_t *new_wb_list;
      47                 :            : 
      48                 :            :   const pdf_char_t *language;
      49                 :            : 
      50                 :            :   /* Generate original word boundaries list, if not already done */
      51         [ -  + ]:         60 :   if (!pdf_text_generate_word_boundaries (text, error))
      52                 :          0 :     return PDF_FALSE;
      53                 :            : 
      54                 :            :   /* Get text language ID. First, try to get it from the pdf_text_t element */
      55                 :         60 :   language = pdf_text_get_language (text);
      56                 :            :   /* If text element doesn't have a language ID, get it from the text context */
      57         [ +  + ]:         60 :   if (strlen (language) == 0)
      58                 :         32 :     language = pdf_text_context_get_host_language ();
      59                 :            : 
      60                 :            :   /* Worst length will be having 3 output UTF-32 characters per each input
      61                 :            :    *  UTF-32 character. First of all, allocate memory for the worst length */
      62                 :         60 :   worst_length = text->size * UCD_SC_MAX_EXPAND;
      63                 :         60 :   new_data = (pdf_char_t *) pdf_alloc (worst_length);
      64         [ -  + ]:         60 :   if (!new_data)
      65                 :            :     {
      66                 :          0 :       pdf_set_error (error,
      67                 :            :                      PDF_EDOMAIN_BASE_TEXT,
      68                 :            :                      PDF_ENOMEM,
      69                 :            :                      "cannot change case of text: "
      70                 :            :                      "couldn't allocate %lu bytes",
      71                 :            :                      (unsigned long)worst_length);
      72                 :          0 :       return PDF_FALSE;
      73                 :            :     }
      74                 :            : 
      75                 :            :   /* Create new empty word boundaries list */
      76                 :         60 :   new_wb_list = pdf_text_create_word_boundaries_list (error);
      77         [ -  + ]:         60 :   if (new_wb_list == NULL)
      78                 :            :     {
      79                 :          0 :       pdf_dealloc (new_data);
      80                 :          0 :       return PDF_FALSE;
      81                 :            :     }
      82                 :            : 
      83                 :            :   /* Walk list of words, uppercasing all of them */
      84                 :         60 :   n_words = pdf_list_size (text->word_boundaries);
      85                 :         60 :   new_length = 0;
      86         [ +  + ]:        216 :   for (i = 0; i < n_words; ++i)
      87                 :            :     {
      88                 :            :       struct pdf_text_wb_s *p_new_word;
      89                 :            :       const struct pdf_text_wb_s *p_word;
      90                 :        156 :       pdf_size_t new_word_length = 0;
      91                 :            : 
      92                 :            :       /* Allocate new word */
      93                 :        156 :       p_new_word = (struct pdf_text_wb_s *) pdf_alloc (sizeof (struct pdf_text_wb_s));
      94         [ -  + ]:        156 :       if (!p_new_word)
      95                 :            :         {
      96                 :          0 :           pdf_set_error (error,
      97                 :            :                          PDF_EDOMAIN_BASE_TEXT,
      98                 :            :                          PDF_ENOMEM,
      99                 :            :                          "cannot change case of text: "
     100                 :            :                          "couldn't allocate %lu bytes",
     101                 :            :                          (unsigned long)sizeof (struct pdf_text_wb_s));
     102                 :          0 :           return PDF_FALSE;
     103                 :            :         }
     104                 :            : 
     105                 :            :       /* Get word to process from list of words */
     106                 :        156 :       p_word = pdf_list_get_at (text->word_boundaries, i, error);
     107         [ -  + ]:        156 :       if (!p_word)
     108                 :            :         {
     109                 :          0 :           pdf_dealloc (new_data);
     110                 :          0 :           pdf_list_destroy (new_wb_list);
     111                 :          0 :           return PDF_FALSE;
     112                 :            :         }
     113                 :            : 
     114                 :            :       /* Apply the case algorithm to the full word */
     115         [ -  + ]:        156 :       if (!pdf_text_ucd_word_change_case (&new_data[new_length],
     116                 :            :                                           &new_word_length,
     117                 :            :                                           new_case,
     118                 :        156 :                                           p_word->word_start,
     119                 :            :                                           p_word->word_size,
     120                 :            :                                           language,
     121                 :            :                                           error))
     122                 :            :         {
     123                 :          0 :           pdf_list_destroy (new_wb_list);
     124                 :          0 :           pdf_dealloc (new_data);
     125                 :          0 :           pdf_dealloc (p_new_word);
     126                 :          0 :           return PDF_FALSE;
     127                 :            :         }
     128                 :            : 
     129                 :            :       /* Create new word info */
     130                 :        156 :       p_new_word->word_start = &new_data[new_length];
     131                 :        156 :       p_new_word->word_size = new_word_length;
     132                 :        156 :       p_new_word->word_stop = &new_data[new_length + new_word_length -4];
     133                 :            : 
     134                 :            :       /* Add word to new list */
     135         [ -  + ]:        156 :       if (!pdf_list_add_last (new_wb_list, p_new_word, error))
     136                 :          0 :         return PDF_FALSE;
     137                 :            : 
     138                 :            :       /* Update new length */
     139                 :        156 :       new_length += new_word_length;
     140                 :            :     }
     141                 :            : 
     142                 :            :   /* Finally, reset the buffer length to its correct size */
     143         [ +  + ]:         60 :   if (new_length != worst_length)
     144                 :            :     {
     145                 :         54 :       new_data = (pdf_char_t *) pdf_realloc (new_data, new_length);
     146         [ -  + ]:         54 :       if (!new_data)
     147                 :            :         {
     148                 :          0 :           pdf_set_error (error,
     149                 :            :                          PDF_EDOMAIN_BASE_TEXT,
     150                 :            :                          PDF_ENOMEM,
     151                 :            :                          "cannot change case of text: "
     152                 :            :                          "couldn't allocate %lu bytes",
     153                 :            :                          (unsigned long)new_length);
     154                 :          0 :           pdf_text_destroy_word_boundaries_list (&new_wb_list);
     155                 :          0 :           return PDF_FALSE;
     156                 :            :         }
     157                 :            :     }
     158                 :            : 
     159                 :            :   /* Replace contents (data and word boundary list) */
     160                 :         60 :   pdf_dealloc (text->data);
     161                 :         60 :   text->data = new_data;
     162                 :         60 :   text->size = new_length;
     163                 :         60 :   pdf_text_destroy_word_boundaries_list (&(text->word_boundaries));
     164                 :         60 :   text->word_boundaries = new_wb_list;
     165                 :            : 
     166                 :         60 :   return PDF_TRUE;
     167                 :            : }
     168                 :            : 
     169                 :            : /* Make all text Upper Case */
     170                 :            : pdf_bool_t
     171                 :         20 : pdf_text_filter_upper_case (pdf_text_t   *text,
     172                 :            :                             pdf_error_t **error)
     173                 :            : {
     174                 :         20 :   return pdf_text_filter_change_case (text,
     175                 :            :                                       UNICODE_CASE_INFO_UPPER_CASE,
     176                 :            :                                       error);
     177                 :            : }
     178                 :            : 
     179                 :            : /* Make all text Lower Case */
     180                 :            : pdf_bool_t
     181                 :         20 : pdf_text_filter_lower_case (pdf_text_t   *text,
     182                 :            :                             pdf_error_t **error)
     183                 :            : {
     184                 :         20 :   return pdf_text_filter_change_case (text,
     185                 :            :                                       UNICODE_CASE_INFO_LOWER_CASE,
     186                 :            :                                       error);
     187                 :            : }
     188                 :            : 
     189                 :            : /* Make all text Title Case */
     190                 :            : pdf_bool_t
     191                 :         20 : pdf_text_filter_title_case (pdf_text_t   *text,
     192                 :            :                             pdf_error_t **error)
     193                 :            : {
     194                 :         20 :   return pdf_text_filter_change_case (text,
     195                 :            :                                       UNICODE_CASE_INFO_TITLE_CASE,
     196                 :            :                                       error);
     197                 :            : }
     198                 :            : 
     199                 :            : /* Remove all single ampersands, and turn ' && ' into ' & ' */
     200                 :            : pdf_bool_t
     201                 :          2 : pdf_text_filter_remove_amp (pdf_text_t   *text,
     202                 :            :                             pdf_error_t **error)
     203                 :            : {
     204 [ +  - ][ +  - ]:          2 :   return (((!pdf_text_replace_ascii (text, " ", " & ", error)) ||
     205                 :            :            (!pdf_text_replace_ascii (text, " & ", " && ", error))) ?
     206                 :            :           PDF_FALSE : PDF_TRUE);
     207                 :            : }
     208                 :            : 
     209                 :            : /* Convert all ASCII code points to their Full-Width variants. These Full-Width
     210                 :            :  *   variants are located in the FF00-FF60 range as follows:
     211                 :            :  * - The range U+FF01-U+FF5E contains the full width variants of the ASCII
     212                 :            :  *   characters in the range U+0021-U+007E.
     213                 :            :  * - The range U+FF5F-U+FF60 contains the full width variants of double
     214                 :            :  *   parentheses in the range U+2985-U+2986
     215                 :            :  */
     216                 :            : pdf_bool_t
     217                 :          3 : pdf_text_filter_normalize_full_width_ascii (pdf_text_t   *text,
     218                 :            :                                             pdf_error_t **error)
     219                 :            : {
     220                 :            :   pdf_size_t i;
     221                 :          3 :   const pdf_u32_t delta = 0xFF01 - 0x0021;
     222                 :          3 :   const pdf_u32_t delta2 = 0xFF5F - 0x2985;
     223                 :            : 
     224         [ +  + ]:         11 :   for (i = 0; i < text->size; i += 4)
     225                 :            :     {
     226                 :            :       pdf_u32_t unicode_point;
     227                 :            : 
     228                 :            :       /* Get unicode point in UTF-32HE */
     229                 :          8 :       memcpy (&unicode_point, &(text->data[i]), 4);
     230                 :            : 
     231                 :            :       /* Check ranges */
     232         [ +  + ]:          8 :       if ((unicode_point <= 0x007E) &&
     233                 :            :           (unicode_point >= 0x0021))
     234                 :            :         {
     235                 :          3 :           unicode_point += delta;
     236                 :          3 :           memcpy (&(text->data[i]), &unicode_point, 4);
     237                 :            :         }
     238         [ -  + ]:          5 :       else if ((unicode_point >= 0x2985) &&
     239                 :            :                (unicode_point <= 0x2986))
     240                 :            :         {
     241                 :          0 :           unicode_point += delta2;
     242                 :          0 :           memcpy(&(text->data[i]), &unicode_point, 4);
     243                 :            :         }
     244                 :            :     }
     245                 :            : 
     246                 :          3 :   return PDF_TRUE;
     247                 :            : }
     248                 :            : 
     249                 :            : /* Substitute line endings with a given UTF-8 pattern. */
     250                 :            : static pdf_bool_t
     251                 :          4 : pdf_text_substitute_line_ending (pdf_text_t            *text,
     252                 :            :                                  const pdf_text_eol_t   new_eol,
     253                 :            :                                  pdf_error_t          **error)
     254                 :            : {
     255                 :            :   pdf_bool_t ret;
     256                 :            :   int i;
     257                 :            :   pdf_text_t *new_text_pattern;
     258                 :            :   pdf_text_t **eols;
     259                 :            : 
     260                 :            :   /* Allocate memory for pdf_text_t old eols */
     261                 :          4 :   eols = (pdf_text_t **) pdf_alloc (PDF_TEXT_EOLMAX * sizeof (pdf_text_t *));
     262         [ -  + ]:          4 :   if (!eols)
     263                 :            :     {
     264                 :          0 :       pdf_set_error (error,
     265                 :            :                      PDF_EDOMAIN_BASE_TEXT,
     266                 :            :                      PDF_ENOMEM,
     267                 :            :                      "cannot substitute line endings: "
     268                 :            :                      "couldn't allocate %lu bytes",
     269                 :            :                      (unsigned long)(PDF_TEXT_EOLMAX * sizeof (pdf_text_t)));
     270                 :          0 :       return PDF_FALSE;
     271                 :            :     }
     272                 :            : 
     273                 :            :   /* Create text new pattern */
     274                 :          4 :   new_text_pattern = pdf_text_new_from_unicode (new_eol->sequence,
     275                 :            :                                                 strlen (new_eol->sequence),
     276                 :            :                                                 PDF_TEXT_UTF8,
     277                 :            :                                                 error);
     278         [ -  + ]:          4 :   if (!new_text_pattern)
     279                 :            :     {
     280                 :          0 :       pdf_dealloc (eols);
     281                 :          0 :       return PDF_FALSE;
     282                 :            :     }
     283                 :            : 
     284                 :            :   /* For each possible EOL type, create a pdf_text_t */
     285         [ +  + ]:         20 :   for (i = PDF_TEXT_EOL_WINDOWS; i < PDF_TEXT_EOLMAX; ++i)
     286                 :            :     {
     287                 :            :       pdf_text_eol_t requested_eol;
     288                 :            : 
     289                 :            :       /* Get Host EOL */
     290                 :         16 :       requested_eol = pdf_text_context_get_host_eol ((enum pdf_text_eol_types)i);
     291                 :            : 
     292                 :            :       /* Create text old pattern */
     293                 :         16 :       eols[i] = pdf_text_new_from_unicode (requested_eol->sequence,
     294                 :            :                                            strlen (requested_eol->sequence),
     295                 :            :                                            PDF_TEXT_UTF8,
     296                 :            :                                            error);
     297         [ -  + ]:         16 :       if (!eols[i])
     298                 :            :         {
     299                 :            :           int j;
     300                 :            : 
     301                 :          0 :           pdf_text_destroy (new_text_pattern);
     302         [ #  # ]:          0 :           for (j = 0; j < i; ++j)
     303                 :          0 :             pdf_text_destroy (eols[j]);
     304                 :          0 :           pdf_dealloc (eols);
     305                 :          0 :           return PDF_FALSE;
     306                 :            :         }
     307                 :            :     }
     308                 :            : 
     309                 :            :   /* Perform the replacement */
     310                 :          4 :   ret = pdf_text_replace_multiple (text,
     311                 :            :                                    new_text_pattern,
     312                 :            :                                    (const pdf_text_t **)eols,
     313                 :            :                                    PDF_TEXT_EOLMAX,
     314                 :            :                                    error);
     315                 :            : 
     316                 :            :   /* Destroy used patterns */
     317         [ +  + ]:         20 :   for (i = PDF_TEXT_EOL_WINDOWS; i < PDF_TEXT_EOLMAX; i++)
     318                 :         16 :     pdf_text_destroy (eols[i]);
     319                 :          4 :   pdf_dealloc (eols);
     320                 :          4 :   pdf_text_destroy (new_text_pattern);
     321                 :            : 
     322                 :          4 :   return ret;
     323                 :            : }
     324                 :            : 
     325                 :            : /* Normalize all EOL sequences to the default host EOL */
     326                 :            : pdf_bool_t
     327                 :          2 : pdf_text_filter_normalize_line_endings (pdf_text_t   *text,
     328                 :            :                                         pdf_error_t **error)
     329                 :            : {
     330                 :            :   pdf_text_eol_t host_eol;
     331                 :            : 
     332                 :            :   /* Get this host EOL */
     333                 :          2 :   host_eol = pdf_text_context_get_host_eol (PDF_TEXT_EOL_HOST);
     334                 :            :   /* Finally, substitute line endings */
     335                 :          2 :   return pdf_text_substitute_line_ending (text,
     336                 :            :                                           host_eol,
     337                 :            :                                           error);
     338                 :            : }
     339                 :            : 
     340                 :            : /* Replace EOL sequences with white spaces */
     341                 :            : pdf_bool_t
     342                 :          2 : pdf_text_filter_remove_line_endings (pdf_text_t   *text,
     343                 :            :                                      pdf_error_t **error)
     344                 :            : {
     345                 :          2 :   const struct pdf_text_eol_s empty_eol =  { { 0x00, 0x00, 0x00 } };
     346                 :            : 
     347                 :            :   /* Substitute line endings */
     348                 :          2 :   return pdf_text_substitute_line_ending (text,
     349                 :            :                                           (pdf_text_eol_t)&empty_eol,
     350                 :            :                                           error);
     351                 :            : }
     352                 :            : 
     353                 :            : /* End of pdf-text-filter.c */

Generated by: LCOV version 1.8