Branch data Line data Source code
1 : : /* -*- mode: C -*-
2 : : *
3 : : * File: pdf-text.c
4 : : * Date: Fri Jan 11 21:09:56 2008
5 : : *
6 : : * GNU PDF Library - Encoded Text handling utilities
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 <limits.h>
29 : : #include <stdlib.h>
30 : : #include <string.h>
31 : : #include <stdint.h>
32 : : #ifdef HAVE_MALLOC_H
33 : : #include <malloc.h>
34 : : #endif /* HAVE_MALLOC_H */
35 : : #include <math.h>
36 : :
37 : : #include <pdf-text.h>
38 : : #include <pdf-text-encoding.h>
39 : : #include <pdf-text-host-encoding.h>
40 : : #include <pdf-text-context.h>
41 : : #include <pdf-text-filter.h>
42 : : #include <pdf-text-ucd.h>
43 : :
44 : :
45 : : /* Lang/Country Minimum Length, in bytes, of the Lang/Country information within
46 : : * a UTF16BEstring (2bytes for the first marker, 2 bytes for LANG and 2 bytes
47 : : * for the last marker). */
48 : : #define PDF_TEXT_LCMINL 6
49 : :
50 : : /* Maximum size, in bytes, of the Lang/Country information within a UTF16BE
51 : : * string (Minimum size + 2 bytes for COUNTRY). */
52 : : #define PDF_TEXT_LCMAXL 8
53 : :
54 : :
55 : : /* Longest header length when requesting a unicode string with options is that
56 : : * of UTF-16BE with BOM and lang/country information: 2bytes-BOM +
57 : : * 8bytes-lang/country = 10 bytes (+ 1 NUL byte) */
58 : : #define PDF_TEXT_USHMAXL 11
59 : :
60 : : typedef struct pdf_text_repl_s {
61 : : pdf_char_t *data_ptr;
62 : : int old_pattern_i;
63 : : } pdf_text_repl_t;
64 : :
65 : : /* ---------------- Static (private) functions prototypes ------------------- */
66 : :
67 : : /* This function receives as input a valid pdf_text_t element, where the
68 : : * language and country code informations will be stored. In addition to this,
69 : : * the function receives as input the data string (starting in the first
70 : : * marker), and stores a pointer to the continuation of the data string, after
71 : : * having read the language/country information. This function really assumes
72 : : * that the input data string contains in the first bytes the country/lang
73 : : * information.
74 : : * Two options are possible:
75 : : * XXllXX (6 bytes, XX is the marker, ll the language)
76 : : * XXllccXX (8 bytes, XX is the marker, ll the language and cc the country)
77 : : */
78 : : static pdf_bool_t
79 : : pdf_text_get_lang_from_utf16be (pdf_text_t *element,
80 : : const pdf_char_t *str_in,
81 : : const pdf_size_t str_in_length,
82 : : pdf_char_t **str_out,
83 : : pdf_size_t *str_out_length,
84 : : pdf_error_t **error);
85 : :
86 : : /* Function to get the header of a unicode string as requested in the
87 : : * `options' field when calling `pdf_text_get_unicode'. The header can be:
88 : : * - BOM
89 : : * - BOM + Lang/Country info (only if UTF-16BE requested)
90 : : * - Lang/Country info (only if UTF-16BE requested)
91 : : */
92 : : static void
93 : : pdf_text_get_unicode_string_header (enum pdf_text_unicode_encoding_e enc,
94 : : pdf_u32_t options,
95 : : const pdf_char_t *language,
96 : : const pdf_char_t *country,
97 : : pdf_char_t header[PDF_TEXT_USHMAXL],
98 : : pdf_size_t *header_length);
99 : :
100 : : /* Function to convert a given Unicode Host Endian enumeration to the `real'
101 : : * endianness (BE or LE). If a non-HE enumeration is passed to the function,
102 : : * it will return the same enumeration value unchanged */
103 : : static enum pdf_text_unicode_encoding_e
104 : : pdf_text_transform_he_to_unicode_encoding (enum pdf_text_unicode_encoding_e enc);
105 : :
106 : : /* Function to compare two given words */
107 : : static pdf_i32_t pdf_text_compare_words (const pdf_char_t *word1,
108 : : const pdf_size_t size1,
109 : : const pdf_char_t *word2,
110 : : const pdf_size_t size2,
111 : : const pdf_char_t *language1,
112 : : const pdf_char_t *language2,
113 : : pdf_error_t **error);
114 : :
115 : : /* Non-Case sensitive comparison of text objects */
116 : : static pdf_i32_t pdf_text_cmp_non_case_sensitive (const pdf_text_t *text1,
117 : : const pdf_text_t *text2,
118 : : pdf_error_t **error);
119 : :
120 : : /* Clean (destroy and create empty) Word Boundaries list */
121 : : static pdf_bool_t pdf_text_clean_word_boundaries_list (pdf_list_t **p_word_boundaries,
122 : : pdf_error_t **error);
123 : : /* Fill in the Word Boundaries list using the given data */
124 : : static pdf_bool_t pdf_text_fill_word_boundaries_list (pdf_list_t *word_boundaries,
125 : : const pdf_char_t *data,
126 : : const pdf_size_t size,
127 : : pdf_error_t **error);
128 : :
129 : : /* ----------------------------- Public functions ----------------------------*/
130 : :
131 : : pdf_bool_t
132 : 745 : pdf_text_init (pdf_error_t **error)
133 : : {
134 : : /* Initiate Text module context */
135 : 745 : return pdf_text_context_init (error);
136 : : }
137 : :
138 : : void
139 : 745 : pdf_text_deinit (void)
140 : : {
141 : : /* Deinit Text module context */
142 [ + - ]: 745 : if (pdf_text_context_initialized ())
143 : 745 : pdf_text_context_deinit ();
144 : 745 : }
145 : :
146 : : pdf_text_t *
147 : 1743 : pdf_text_new (pdf_error_t **error)
148 : : {
149 : : pdf_text_t *text;
150 : :
151 : : /* The text global state should be initialized! */
152 [ - + ]: 1743 : if (!pdf_text_context_initialized ())
153 : : {
154 : 0 : pdf_set_error (error,
155 : : PDF_EDOMAIN_BASE_TEXT,
156 : : PDF_EBADCONTEXT,
157 : : "cannot create new text object: "
158 : : "context not initialized");
159 : 0 : return NULL;
160 : : }
161 : :
162 : : /* Allocate memory for the new text structure */
163 : 1743 : text = (pdf_text_t *) pdf_alloc (sizeof (struct pdf_text_s));
164 [ - + ]: 1743 : if (!text)
165 : : {
166 : 0 : pdf_set_error (error,
167 : : PDF_EDOMAIN_BASE_TEXT,
168 : : PDF_ENOMEM,
169 : : "cannot create new text object: "
170 : : "couldn't allocate %lu bytes",
171 : : (unsigned long)sizeof (struct pdf_text_s));
172 : : /* Out of memory condition */
173 : 0 : return NULL;
174 : : }
175 : :
176 : : /* Initialize all contents */
177 : 1743 : text->data = NULL;
178 : 1743 : text->size = 0;
179 : 1743 : text->printable = NULL;
180 : 1743 : text->modified = PDF_FALSE;
181 : 1743 : memset (&(text->lang[0]), 0, PDF_TEXT_CCL);
182 : 1743 : memset (&(text->country[0]), 0, PDF_TEXT_CCL);
183 : :
184 : : /* Create empty word boundaries list */
185 : 1743 : text->word_boundaries = pdf_text_create_word_boundaries_list (error);
186 [ - + ]: 1743 : if (!text->word_boundaries)
187 : : {
188 : 0 : pdf_text_destroy (text);
189 : 0 : return NULL;
190 : : }
191 : :
192 : : /* Success! */
193 : 1743 : return text;
194 : : }
195 : :
196 : : void
197 : 1539 : pdf_text_destroy (pdf_text_t *text)
198 : : {
199 [ + - ]: 1539 : if (!text)
200 : : return;
201 : :
202 : : /* Dealloc memory */
203 [ + + ]: 1539 : if (text->data != NULL)
204 : 1310 : pdf_dealloc (text->data);
205 : :
206 [ - + ]: 1539 : if (text->printable != NULL)
207 : 0 : pdf_dealloc (text->printable);
208 : :
209 : : /* Destroy word boundaries list */
210 : 1539 : pdf_text_destroy_word_boundaries_list (&text->word_boundaries);
211 : :
212 : 1539 : pdf_dealloc (text);
213 : : }
214 : :
215 : : pdf_text_t *
216 : 30 : pdf_text_dup (const pdf_text_t *text,
217 : : pdf_error_t **error)
218 : : {
219 : : pdf_text_t *element;
220 : :
221 [ - + ]: 30 : PDF_ASSERT_POINTER_RETURN_VAL (text, NULL);
222 : :
223 : : /* Allocate and initialize element */
224 : 30 : element = pdf_text_new (error);
225 [ - + ]: 30 : if (!element)
226 : : {
227 : 0 : pdf_prefix_error (error, "cannot duplicate text object: ");
228 : 0 : return NULL;
229 : : }
230 : :
231 : : /* Duplicate size */
232 : 30 : element->size = text->size;
233 : :
234 : : /* Duplicate contents (if size > 0) */
235 [ + + ]: 30 : if (element->size > 0)
236 : : {
237 : 29 : element->data = (pdf_char_t *) pdf_alloc (element->size);
238 [ - + ]: 29 : if (!element->data)
239 : : {
240 : 0 : pdf_set_error (error,
241 : : PDF_EDOMAIN_BASE_TEXT,
242 : : PDF_ENOMEM,
243 : : "cannot duplicate text object: "
244 : : "couldn't allocate %lu bytes",
245 : : (unsigned long)element->size);
246 : 0 : return NULL;
247 : : }
248 : :
249 : 29 : memcpy (element->data, text->data, (size_t)element->size);
250 : : }
251 : :
252 : : /* Duplicate Language code and Country code (if available) */
253 : 30 : memcpy (element->lang, text->lang, (size_t) PDF_TEXT_CCL);
254 : 30 : memcpy (element->country, text->country, (size_t) PDF_TEXT_CCL);
255 : :
256 : : /* We don't really need to duplicate the contents of the word
257 : : * boundaries list, as it is a side product, same with printable */
258 : :
259 : 30 : return element;
260 : : }
261 : :
262 : : pdf_text_t *
263 : 5 : pdf_text_new_from_host (const pdf_char_t *str,
264 : : const pdf_size_t size,
265 : : const pdf_char_t *enc,
266 : : pdf_error_t **error)
267 : : {
268 : : pdf_text_t *element;
269 : :
270 [ - + ]: 5 : PDF_ASSERT_POINTER_RETURN_VAL (str, NULL);
271 [ - + ]: 5 : PDF_ASSERT_RETURN_VAL (size > 0, NULL);
272 [ - + ]: 5 : PDF_ASSERT_POINTER_RETURN_VAL (enc, NULL);
273 : :
274 : : /* Allocate and initialize element */
275 : 5 : element = pdf_text_new (error);
276 [ - + ]: 5 : if (!element)
277 : : {
278 : 0 : pdf_prefix_error (error,
279 : : "cannot create text object from string "
280 : : "encoded in '%s' host encoding: ",
281 : : enc);
282 : 0 : return NULL;
283 : : }
284 : :
285 : : /* Set Host Encoding contents */
286 [ + + ]: 5 : if (!pdf_text_set_host (element, str, size, enc, error))
287 : : {
288 : 2 : pdf_prefix_error (error,
289 : : "cannot create text object from string "
290 : : "encoded in '%s' host encoding: ",
291 : : enc);
292 : : /* Conversion went wrong... so destroy object contents */
293 : 2 : pdf_text_destroy (element);
294 : 2 : return NULL;
295 : : }
296 : :
297 : 5 : return element;
298 : : }
299 : :
300 : : pdf_text_t *
301 : 47 : pdf_text_new_from_pdf_string (const pdf_char_t *str,
302 : : const pdf_size_t size,
303 : : pdf_char_t **remaining_str,
304 : : pdf_size_t *remaining_length,
305 : : pdf_error_t **error)
306 : : {
307 : : pdf_text_t *element;
308 : 47 : pdf_bool_t bom_found = PDF_FALSE;
309 : 47 : pdf_bool_t lang_found = PDF_FALSE;
310 : :
311 [ - + ]: 47 : PDF_ASSERT_POINTER_RETURN_VAL (str, NULL);
312 [ - + ]: 47 : PDF_ASSERT_RETURN_VAL (size > 0, NULL);
313 : :
314 : : /* Allocate and initialize element */
315 : 47 : element = pdf_text_new (error);
316 [ - + ]: 47 : if (!element)
317 : : {
318 : 0 : pdf_prefix_error (error,
319 : : "cannot create text object "
320 : : "from PDF string: ");
321 : 0 : return NULL;
322 : : }
323 : :
324 : : /* First of all, check first two bytes to detect UTF-16BE BOM or lang/country
325 : : * code initializer.
326 : : * If length of the text is less than 2, then we can assume it is encoded in
327 : : * PDF Doc Encoding */
328 [ + - ]: 47 : if (size >= 2)
329 : : {
330 : : /* Check Unicode Byte Order Marker encoded in UTF-16BE */
331 [ + + ]: 47 : if (pdf_text_check_unicode_bom (str, size, PDF_TEXT_UTF16_BE, 0))
332 : : {
333 : 21 : bom_found = PDF_TRUE;
334 : : /* Check Lang/Country Code initializer */
335 [ + - ][ + + ]: 21 : if ((size >= 4) &&
[ + - ]
336 : 21 : (str[3] == PDF_TEXT_LCI_1) &&
337 : 16 : (str[2] == PDF_TEXT_LCI_0))
338 : : {
339 : 16 : lang_found = PDF_TRUE;
340 : : }
341 : : }
342 : : /* Check Lang/Country Code initializer (if this is the nth call to the
343 : : * function parsing a single UTF-16BE string.*/
344 [ + + ][ + - ]: 26 : else if ((str[1] == PDF_TEXT_LCI_1) &&
345 : 12 : (str[0] == PDF_TEXT_LCI_0))
346 : : {
347 : 12 : lang_found = PDF_TRUE;
348 : : }
349 : : }
350 : :
351 : : /* If either BOM or Lang Marker are found, process PDF string as encoded
352 : : * in UTF16-BE */
353 [ + + ][ + + ]: 47 : if (bom_found || lang_found)
354 : : {
355 : 33 : pdf_char_t *string_start = (pdf_char_t *)str;
356 : 33 : pdf_size_t string_length = size;
357 : :
358 : : /* Skip 2-bytes BOM */
359 [ + + ]: 33 : if (bom_found)
360 : : {
361 : 21 : string_start += 2;
362 : 21 : string_length -= 2;
363 : : }
364 : :
365 : : /* If lang/country code available, obtain and store the information */
366 [ + + ][ - + ]: 61 : if ((lang_found) &&
367 : : (!pdf_text_get_lang_from_utf16be (element,
368 : : string_start,
369 : : string_length,
370 : : &string_start,
371 : : &string_length,
372 : : error)))
373 : : {
374 : 0 : pdf_prefix_error (error,
375 : : "cannot create text object "
376 : : "from PDF string: ");
377 : 0 : pdf_text_destroy (element);
378 : 0 : return NULL;
379 : : }
380 : :
381 : : /* And finally convert to UTF-32... */
382 [ - + ]: 33 : if (!pdf_text_utf16be_to_utf32he (string_start,
383 : : string_length,
384 : : &(element->data),
385 : : &(element->size),
386 : : remaining_str,
387 : : remaining_length,
388 : : error))
389 : : {
390 : 0 : pdf_prefix_error (error,
391 : : "cannot create text object "
392 : : "from PDF string: ");
393 : 0 : pdf_text_destroy (element);
394 : 0 : return NULL;
395 : : }
396 : :
397 : : /* Return newly created element */
398 : 33 : return element;
399 : : }
400 : :
401 : : /* Else, process PDF string as encoded in PDF Doc Encoding */
402 : :
403 : : /* We already know that this string will be fully stored, without
404 : : * splitting in chunks */
405 [ + - ]: 14 : if (remaining_length != NULL)
406 : 14 : *remaining_length = 0;
407 [ + - ]: 14 : if (remaining_str != NULL)
408 : 14 : *remaining_str = NULL;
409 : :
410 : : /* And perform the conversion */
411 [ + + ]: 14 : if (!pdf_text_pdfdocenc_to_utf32he (str,
412 : : size,
413 : : &(element->data),
414 : : &(element->size),
415 : : error))
416 : : {
417 : 11 : pdf_prefix_error (error,
418 : : "cannot create text object "
419 : : "from PDF string: ");
420 : 11 : pdf_text_destroy (element);
421 : 11 : return NULL;
422 : : }
423 : :
424 : 47 : return element;
425 : : }
426 : :
427 : : pdf_text_t *
428 : 1387 : pdf_text_new_from_unicode (const pdf_char_t *str,
429 : : const pdf_size_t size,
430 : : const enum pdf_text_unicode_encoding_e enc,
431 : : pdf_error_t **error)
432 : : {
433 : : pdf_text_t *element;
434 : :
435 [ - + ]: 1387 : PDF_ASSERT_POINTER_RETURN_VAL (str, NULL);
436 : : /* size == 0 is actually valid */
437 [ - + ]: 1387 : PDF_ASSERT_RETURN_VAL (enc >= PDF_TEXT_UTF8, NULL);
438 [ - + ]: 1387 : PDF_ASSERT_RETURN_VAL (enc < PDF_TEXT_MAX_UNICODE_ENC, NULL);
439 : :
440 : : /* Allocate and initialize element */
441 : 1387 : element = pdf_text_new (error);
442 [ - + ]: 1387 : if (!element)
443 : : {
444 : 0 : pdf_prefix_error (error,
445 : : "cannot create text object "
446 : : "from '%s' encoded Unicode string: ",
447 : : pdf_text_get_unicode_encoding_name (enc));
448 : 0 : return NULL;
449 : : }
450 : :
451 : : /* Set Unicode contents */
452 [ + + ][ + + ]: 1387 : if (size > 0 &&
453 : : !pdf_text_set_unicode (element, str, size, enc, error))
454 : : {
455 : 80 : pdf_prefix_error (error,
456 : : "cannot create text object "
457 : : "from '%s' encoded Unicode string: ",
458 : : pdf_text_get_unicode_encoding_name (enc));
459 : 80 : pdf_text_destroy (element);
460 : 80 : return NULL;
461 : : }
462 : :
463 : 1387 : return element;
464 : : }
465 : :
466 : : pdf_text_t *
467 : 2 : pdf_text_new_from_u32 (const pdf_u32_t number,
468 : : pdf_error_t **error)
469 : : {
470 : 2 : pdf_error_t *inner_error = NULL;
471 : : pdf_text_t *element;
472 : :
473 : : /* Longest number to hold in 32bit: 2^32 = 4294967296 (10 chars) */
474 : : pdf_char_t temp[11];
475 : : int n;
476 : :
477 : : /* Print number in temporal char array, and get number of output chars */
478 : 2 : n = sprintf (&temp[0], "%u", (unsigned int)number);
479 : :
480 : : /* Treat the generated string as UTF-8 encoded (just numbers in ASCII) */
481 : 2 : element = pdf_text_new_from_unicode (&temp[0], n, PDF_TEXT_UTF8, &inner_error);
482 [ - + ]: 2 : if (!element)
483 : : {
484 : : /* We should never get a PDF_ETEXTENC error */
485 [ # # ]: 0 : PDF_ASSERT (pdf_error_get_status (inner_error) != PDF_ETEXTENC);
486 : 0 : pdf_propagate_error (error, inner_error);
487 : 0 : pdf_prefix_error (error,
488 : : "cannot create text object "
489 : : "from u32 (%u): ",
490 : : (unsigned int)number);
491 : 0 : return NULL;
492 : : }
493 : 2 : return element;
494 : : }
495 : :
496 : : /* Return the country associated with a text variable */
497 : : const pdf_char_t *
498 : 332 : pdf_text_get_country (const pdf_text_t *text)
499 : : {
500 [ - + ]: 332 : PDF_ASSERT_POINTER_RETURN_VAL (text, NULL);
501 : :
502 : 332 : return (const pdf_char_t *)text->country;
503 : : }
504 : :
505 : : /* Return the language associated with a text variable */
506 : : const pdf_char_t *
507 : 442 : pdf_text_get_language (const pdf_text_t *text)
508 : : {
509 [ - + ]: 442 : PDF_ASSERT_POINTER_RETURN_VAL (text, NULL);
510 : :
511 : 442 : return (const pdf_char_t *)text->lang;
512 : : }
513 : :
514 : : /* Associate a text variable (full text) with a country code */
515 : : void
516 : 152 : pdf_text_set_country (pdf_text_t *text,
517 : : const pdf_char_t *code)
518 : : {
519 [ - + ]: 152 : PDF_ASSERT_POINTER_RETURN (text);
520 [ - + ]: 152 : PDF_ASSERT_POINTER_RETURN (code);
521 [ - + ]: 152 : PDF_ASSERT_RETURN (strlen (code) == (PDF_TEXT_CCL - 1));
522 : :
523 : 152 : memcpy (&(text->country[0]), code, PDF_TEXT_CCL - 1);
524 : : /* Make sure that last byte is NUL */
525 : 152 : text->country[PDF_TEXT_CCL - 1] = '\0';
526 : : }
527 : :
528 : : /* Associate a text variable (full text) with a language code */
529 : : void
530 : 291 : pdf_text_set_language (pdf_text_t *text,
531 : : const pdf_char_t *code)
532 : : {
533 [ - + ]: 291 : PDF_ASSERT_POINTER_RETURN (text);
534 [ - + ]: 291 : PDF_ASSERT_POINTER_RETURN (code);
535 [ - + ]: 291 : PDF_ASSERT_RETURN (strlen (code) == (PDF_TEXT_CCL - 1));
536 : :
537 : 291 : memcpy (&(text->lang[0]), code, PDF_TEXT_CCL - 1);
538 : : /* Make sure that last byte is NUL */
539 : 291 : text->lang[PDF_TEXT_CCL - 1] = '\0';
540 : : }
541 : :
542 : : /* Determine if a given text variable is empty (contains no text) */
543 : : pdf_bool_t
544 : 41 : pdf_text_empty_p (const pdf_text_t *text)
545 : : {
546 [ - + ]: 41 : PDF_ASSERT_POINTER_RETURN_VAL (text, PDF_TRUE);
547 : :
548 : 41 : return ((text->size != 0) ? PDF_FALSE : PDF_TRUE);
549 : : }
550 : :
551 : : /* Get default system host encoding */
552 : : const pdf_char_t *
553 : 28 : pdf_text_get_host_encoding (void)
554 : : {
555 : 28 : return pdf_text_context_get_host_encoding ();
556 : : }
557 : :
558 : : /* Check if host encoding is available */
559 : : pdf_bool_t
560 : 3 : pdf_text_check_host_encoding (const pdf_char_t *encoding_name,
561 : : pdf_error_t **error)
562 : : {
563 [ - + ]: 3 : PDF_ASSERT_POINTER_RETURN_VAL (encoding_name, PDF_FALSE);
564 : :
565 : 3 : return pdf_text_host_encoding_is_available (encoding_name,
566 : : error);
567 : : }
568 : :
569 : : const pdf_char_t *
570 : 1 : pdf_text_get_best_encoding (const pdf_text_t *text,
571 : : const pdf_char_t *preferred_encoding)
572 : : {
573 : : int i;
574 : : #ifdef PDF_HOST_WIN32
575 : : static const pdf_char_t *to_check [3] = {
576 : : (pdf_char_t *) "CP1200", /* UTF-16LE */
577 : : (pdf_char_t *) "CP65001", /* UTF-8 */
578 : : (pdf_char_t *) "CP12000" /* UTF-32LE */
579 : : };
580 : : #else
581 : : static const pdf_char_t *to_check [3] = {
582 : : (pdf_char_t *) "UTF-8",
583 : : (pdf_char_t *) "UTF-16",
584 : : (pdf_char_t *) "UTF-32"
585 : : };
586 : : #endif
587 : :
588 [ - + ]: 1 : PDF_ASSERT_POINTER_RETURN_VAL (text, NULL);
589 : :
590 : : /* TODO: In the case of win32, we could actually check if all characters
591 : : * in the text are ASCII-7, and if so, return for example CP1252 instead
592 : : * of UTF-16. */
593 : :
594 [ - + ]: 1 : PDF_ASSERT_POINTER_RETURN_VAL (text, NULL);
595 : :
596 : : /* Check for Unicode support as host encoding */
597 [ + - ]: 1 : for (i = 0; i < 3; i++)
598 : : {
599 [ + - ]: 1 : if (pdf_text_check_host_encoding (to_check[i], NULL))
600 : 1 : return to_check[i];
601 : : }
602 : :
603 : : /* If host does not support any Unicode encoding conversion, return the
604 : : * preferred one directly */
605 : 1 : return preferred_encoding;
606 : : }
607 : :
608 : : pdf_char_t *
609 : 33 : pdf_text_get_host (const pdf_text_t *text,
610 : : const pdf_char_t *enc,
611 : : pdf_size_t *length,
612 : : pdf_error_t **error)
613 : : {
614 : : pdf_char_t *contents;
615 : :
616 [ - + ]: 33 : PDF_ASSERT_POINTER_RETURN_VAL (text, NULL);
617 [ - + ]: 33 : PDF_ASSERT_POINTER_RETURN_VAL (enc, NULL);
618 : : /* You should really get and check the length of the returned string */
619 [ - + ]: 33 : PDF_ASSERT_POINTER_RETURN_VAL (length, NULL);
620 : :
621 [ + + ]: 33 : if (!pdf_text_utf32he_to_host (text->data,
622 : : text->size,
623 : : enc,
624 : : &contents,
625 : : length,
626 : : error))
627 : : {
628 : 3 : pdf_prefix_error (error,
629 : : "cannot get contents encoded in "
630 : : "'%s' host encoding: ",
631 : : enc);
632 : 3 : return NULL;
633 : : }
634 : 33 : return contents;
635 : : }
636 : :
637 : : /* Get the contents of a text variable encoded in PDFDocEncoding, as a NUL
638 : : * terminated string */
639 : : pdf_char_t *
640 : 20 : pdf_text_get_pdfdocenc (const pdf_text_t *text,
641 : : pdf_error_t **error)
642 : : {
643 : 20 : pdf_char_t *data = NULL;
644 : : pdf_char_t *resized;
645 : 20 : pdf_size_t size = -1;
646 : :
647 [ - + ]: 20 : PDF_ASSERT_POINTER_RETURN_VAL (text, NULL);
648 : :
649 [ - + ]: 20 : if (!pdf_text_utf32he_to_pdfdocenc (text->data,
650 : : text->size,
651 : : &data,
652 : : &size,
653 : : error))
654 : : {
655 : 0 : pdf_prefix_error (error,
656 : : "cannot get contents encoded in"
657 : : " PDF Doc Encoding: ");
658 : 0 : return NULL;
659 : : }
660 : :
661 : : /* Add NUL character at the end of the array */
662 : 20 : resized = pdf_realloc (data, size + 1);
663 [ - + ]: 20 : if (!resized)
664 : : {
665 : 0 : pdf_dealloc (data);
666 : 0 : pdf_set_error (error,
667 : : PDF_EDOMAIN_BASE_TEXT,
668 : : PDF_ENOMEM,
669 : : "cannot get contents encoded in"
670 : : " PDF Doc Encoding: "
671 : : "couldn't reallocate %lu bytes",
672 : : (unsigned long)(size + 1));
673 : 0 : return NULL;
674 : : }
675 : :
676 : 20 : resized[size] = '\0';
677 : :
678 : 20 : return resized;
679 : : }
680 : :
681 : : pdf_char_t *
682 : 661 : pdf_text_get_unicode (const pdf_text_t *text,
683 : : enum pdf_text_unicode_encoding_e enc,
684 : : pdf_u32_t options,
685 : : pdf_size_t *length,
686 : : pdf_error_t **error)
687 : : {
688 : 661 : pdf_char_t *out_data = NULL;
689 : 661 : pdf_size_t out_length = 0;
690 : : pdf_bool_t ret;
691 : :
692 [ - + ]: 661 : PDF_ASSERT_POINTER_RETURN_VAL (text, NULL);
693 [ - + ]: 661 : PDF_ASSERT_RETURN_VAL (enc >= PDF_TEXT_UTF8, NULL);
694 [ - + ]: 661 : PDF_ASSERT_RETURN_VAL (enc < PDF_TEXT_MAX_UNICODE_ENC, NULL);
695 : :
696 : : /* Lang/Country info only available for UTF-16BE */
697 [ + + ]: 661 : if ((options & PDF_TEXT_UTF16BE_WITH_LANGCODE) &&
698 : 661 : (enc != PDF_TEXT_UTF16_BE))
699 : : {
700 : 164 : pdf_set_error (error,
701 : : PDF_EDOMAIN_BASE_TEXT,
702 : : PDF_ETEXTENC,
703 : : "cannot get text contents unicode encoded: "
704 : : "language-code request only possible for UTF-16BE");
705 : 164 : return NULL;
706 : : }
707 : :
708 : : /* If host endianness required, check it and convert input encoding */
709 : 497 : enc = pdf_text_transform_he_to_unicode_encoding (enc);
710 : :
711 : : /* If text is empty, set empty string */
712 [ + + ][ + + ]: 497 : if ((text->data == NULL) ||
713 : 465 : (text->size == 0))
714 : : {
715 : 38 : ret = PDF_TRUE;
716 : 38 : out_data = NULL;
717 : : /* Length is optional because you can ask for unicode output with NUL trailer */
718 [ + - ]: 38 : if (length)
719 : 38 : *length = 0;
720 : : }
721 : : else
722 : : {
723 : : /* Perform conversion */
724 [ + + + + + : 459 : switch (enc)
- ]
725 : : {
726 : : case PDF_TEXT_UTF8: /* UTF-8 */
727 : 86 : ret = pdf_text_utf32he_to_utf8 (text->data,
728 : : text->size,
729 : : &out_data,
730 : : &out_length,
731 : : error);
732 : 86 : break;
733 : : case PDF_TEXT_UTF16_LE: /* UTF-16LE */
734 : 20 : ret = pdf_text_utf32he_to_utf16le (text->data,
735 : : text->size,
736 : : &out_data,
737 : : &out_length,
738 : : error);
739 : 20 : break;
740 : : case PDF_TEXT_UTF16_BE: /* UTF-16BE */
741 : 60 : ret = pdf_text_utf32he_to_utf16be (text->data,
742 : : text->size,
743 : : &out_data,
744 : : &out_length,
745 : : error);
746 : 60 : break;
747 : : case PDF_TEXT_UTF32_LE: /* UTF-32LE */
748 : 217 : ret = pdf_text_utf32he_to_utf32le (text->data,
749 : : text->size,
750 : : &out_data,
751 : : &out_length,
752 : : error);
753 : 217 : break;
754 : : case PDF_TEXT_UTF32_BE: /* UTF-32BE */
755 : 76 : ret = pdf_text_utf32he_to_utf32be (text->data,
756 : : text->size,
757 : : &out_data,
758 : : &out_length,
759 : : error);
760 : 76 : break;
761 : : default:
762 : 0 : pdf_set_error (error,
763 : : PDF_EDOMAIN_BASE_TEXT,
764 : : PDF_ETEXTENC,
765 : : "couldn't get text contents in the given unicode "
766 : : "encoding (%d)",
767 : : enc);
768 : 0 : ret = PDF_FALSE;
769 : : break;
770 : : }
771 : : }
772 : :
773 [ - + ]: 497 : if (!ret)
774 : : {
775 [ # # ]: 0 : if (out_data)
776 : 0 : pdf_dealloc (out_data);
777 : 0 : return NULL;
778 : : }
779 : :
780 : : /* Check if specific options were requested */
781 [ + + ]: 497 : if (options != PDF_TEXT_UNICODE_NO_OPTION)
782 : : {
783 : : pdf_char_t header[PDF_TEXT_USHMAXL];
784 : 201 : pdf_size_t header_size = 0;
785 : 201 : pdf_size_t trailer_size = 0;
786 : :
787 : : /* Compute header if needed */
788 [ + + ]: 201 : if ((options & PDF_TEXT_UNICODE_WITH_BOM) ||
789 : : (options & PDF_TEXT_UTF16BE_WITH_LANGCODE))
790 : : {
791 : : /* Clear header array */
792 : 108 : memset (&(header[0]), 0, PDF_TEXT_USHMAXL);
793 : : /* Get requested header (BOM and/or lang/country info) */
794 : 108 : pdf_text_get_unicode_string_header (enc,
795 : : options,
796 : : pdf_text_get_language(text),
797 : : pdf_text_get_country(text),
798 : : header,
799 : : &header_size);
800 : : }
801 : :
802 : : /* Compute trailer if needed */
803 [ + + ]: 201 : if (options & PDF_TEXT_UNICODE_WITH_NUL_SUFFIX)
804 : : {
805 [ + + + - ]: 147 : switch (enc)
806 : : {
807 : : case PDF_TEXT_UTF8:
808 : 75 : trailer_size = 1;
809 : 75 : break;
810 : : case PDF_TEXT_UTF16_BE:
811 : : case PDF_TEXT_UTF16_LE:
812 : : case PDF_TEXT_UTF16_HE:
813 : 48 : trailer_size = 2;
814 : 48 : break;
815 : : case PDF_TEXT_UTF32_BE:
816 : : case PDF_TEXT_UTF32_LE:
817 : : case PDF_TEXT_UTF32_HE:
818 : 24 : trailer_size = 4;
819 : 24 : break;
820 : : default:
821 : 0 : trailer_size = 0;
822 : : break;
823 : : }
824 : : }
825 : :
826 [ + - ]: 201 : if ((header_size > 0) ||
827 : : (trailer_size > 0))
828 : : {
829 : : pdf_char_t *new_out_data;
830 : : pdf_size_t new_size;
831 : :
832 : : /* Allocate memory for new string */
833 : 201 : new_size = out_length + header_size + trailer_size;
834 : 201 : new_out_data = (pdf_char_t *) pdf_alloc (new_size);
835 [ - + ]: 201 : if (!new_out_data)
836 : : {
837 : 0 : pdf_set_error (error,
838 : : PDF_EDOMAIN_BASE_TEXT,
839 : : PDF_ENOMEM,
840 : : "couldn't get text contents in the given unicode "
841 : : "encoding: "
842 : : "couldn't reallocate %lu bytes",
843 : : (unsigned long)new_size);
844 : 0 : return NULL;
845 : : }
846 : :
847 : : /* Store header */
848 : 201 : memcpy (new_out_data, &header[0], header_size);
849 : :
850 [ + + ][ + - ]: 201 : if ((out_data != NULL) &&
851 : 178 : (out_length != 0))
852 : : {
853 : : /* Store unicode data, if any */
854 : 178 : memcpy (&new_out_data[header_size], out_data, out_length);
855 : : /* Reset output data array, if any */
856 : 178 : pdf_dealloc (out_data);
857 : : }
858 : :
859 : : /* Store trailer (N-byte NUL) */
860 [ + + ]: 201 : if (trailer_size > 0)
861 : 147 : memset (&new_out_data[out_length+header_size], 0, trailer_size);
862 : :
863 : 201 : out_data = new_out_data;
864 : 201 : out_length += (header_size + trailer_size);
865 : : }
866 : : }
867 : :
868 : : /* Length is optional because you can ask for unicode output with NUL trailer */
869 [ + + ]: 497 : if (length)
870 : 466 : *length = out_length;
871 : 661 : return out_data;
872 : : }
873 : :
874 : : pdf_char_t *
875 : 2 : pdf_text_get_hex (const pdf_text_t *text,
876 : : const pdf_char_t delimiter,
877 : : pdf_error_t **error)
878 : : {
879 : : pdf_char_t *new_str;
880 : :
881 [ - + ]: 2 : PDF_ASSERT_POINTER_RETURN_VAL (text, NULL);
882 : :
883 [ + + ]: 2 : if (text->size > 0)
884 : : {
885 : : int i;
886 : : int j;
887 : : unsigned int new_str_length;
888 : : char new_hex_char [3];
889 : :
890 : : /* Get new string length. If input string has N bytes, we need:
891 : : * - 1 byte for last NUL char
892 : : * - 2N bytes for hexadecimal char representation of each byte...
893 : : * - N-1 bytes for the separator ':'
894 : : * So... a total of (1+2N+N-1) = 3N bytes are needed... */
895 : 1 : new_str_length = 3 * text->size;
896 : :
897 : : /* Allocate memory for new array and initialize contents to NUL */
898 : 1 : new_str = (pdf_char_t *) pdf_alloc (new_str_length);
899 [ - + ]: 1 : if (!new_str)
900 : : {
901 : 0 : pdf_set_error (error,
902 : : PDF_EDOMAIN_BASE_TEXT,
903 : : PDF_ENOMEM,
904 : : "cannot get hexadecimal representation of the text "
905 : : "object contents: "
906 : : "couldn't allocate %lu bytes",
907 : : (unsigned long)new_str_length);
908 : 0 : return NULL;
909 : : }
910 : :
911 : 1 : memset (new_str, 0, new_str_length);
912 : :
913 : : /* Print hexadecimal representation of each byte... */
914 [ + + ]: 9 : for (i=0, j=0; i < text->size; i++, j+=3)
915 : : {
916 : : /* Clear helper array... */
917 : 8 : memset (&new_hex_char[0], 0, 3);
918 : : /* Print character in helper array... */
919 : 8 : sprintf (new_hex_char, "%02X", (unsigned int)text->data[i]);
920 : : /* Copy to output string... */
921 : 8 : memcpy (&new_str[j], &new_hex_char[0], 2);
922 : : /* And if needed, add separator */
923 [ + + ]: 8 : if (i != (text->size - 1))
924 : 7 : new_str[j+2] = delimiter;
925 : : }
926 : : }
927 : : else
928 : : {
929 : 1 : new_str = (pdf_char_t *) pdf_alloc (1);
930 [ - + ]: 1 : if (!new_str)
931 : : {
932 : 0 : pdf_set_error (error,
933 : : PDF_EDOMAIN_BASE_TEXT,
934 : : PDF_ENOMEM,
935 : : "cannot get hexadecimal representation of the text "
936 : : "object contents: "
937 : : "couldn't allocate 1 byte");
938 : 0 : return NULL;
939 : : }
940 : 1 : new_str[0] = '\0';
941 : : }
942 : :
943 : : /* Set output string */
944 : 2 : return new_str;
945 : : }
946 : :
947 : : pdf_bool_t
948 : 10 : pdf_text_set_host (pdf_text_t *text,
949 : : const pdf_char_t *str,
950 : : pdf_size_t size,
951 : : const pdf_char_t *enc,
952 : : pdf_error_t **error)
953 : : {
954 : : pdf_char_t *temp_data;
955 : : pdf_size_t temp_size;
956 : :
957 [ - + ]: 10 : PDF_ASSERT_POINTER_RETURN_VAL (str, PDF_FALSE);
958 [ - + ]: 10 : PDF_ASSERT_POINTER_RETURN_VAL (enc, PDF_FALSE);
959 [ - + ]: 10 : PDF_ASSERT_RETURN_VAL (size > 0, PDF_FALSE);
960 : :
961 [ + + ]: 10 : if (!pdf_text_host_to_utf32he (str,
962 : : size,
963 : : enc,
964 : : &temp_data,
965 : : &temp_size,
966 : : error))
967 : : {
968 : 4 : pdf_prefix_error (error,
969 : : "cannot set string encoded in "
970 : : "'%s' host encoding: ",
971 : : enc);
972 : 4 : return PDF_FALSE;
973 : : }
974 : :
975 : : /* Destroy previous contents of text variable, if any */
976 [ - + ]: 6 : if (!pdf_text_clean_contents (text, error))
977 : : {
978 : 0 : pdf_prefix_error (error,
979 : : "cannot set string encoded in "
980 : : "'%s' host encoding: ",
981 : : enc);
982 : 0 : pdf_dealloc (temp_data);
983 : 0 : return PDF_FALSE;
984 : : }
985 : :
986 : : /* Really set contents */
987 : 6 : text->data = temp_data;
988 : 6 : text->size = temp_size;
989 : :
990 : 10 : return PDF_TRUE;
991 : : }
992 : :
993 : : /* Set PDF Doc Endoded string */
994 : : pdf_bool_t
995 : 20 : pdf_text_set_pdfdocenc (pdf_text_t *text,
996 : : const pdf_char_t *str,
997 : : pdf_error_t **error)
998 : : {
999 : : pdf_char_t *temp_data;
1000 : : pdf_size_t temp_size;
1001 : :
1002 [ - + ]: 20 : PDF_ASSERT_POINTER_RETURN_VAL (text, PDF_FALSE);
1003 [ - + ]: 20 : PDF_ASSERT_POINTER_RETURN_VAL (str, PDF_FALSE);
1004 : :
1005 [ + + ]: 20 : if (!pdf_text_pdfdocenc_to_utf32he (str,
1006 : : strlen (str),
1007 : : &temp_data,
1008 : : &temp_size,
1009 : : error))
1010 : : {
1011 : 1 : pdf_prefix_error (error,
1012 : : "cannot set string encoded in"
1013 : : " PDF Doc Encoding: ");
1014 : 1 : return PDF_FALSE;
1015 : : }
1016 : :
1017 : : /* Destroy previous contents of text variable, if any */
1018 [ - + ]: 19 : if (!pdf_text_clean_contents (text, error))
1019 : : {
1020 : 0 : pdf_prefix_error (error,
1021 : : "cannot set string encoded in"
1022 : : " PDF Doc Encoding: ");
1023 : 0 : pdf_dealloc (temp_data);
1024 : 0 : return PDF_FALSE;
1025 : : }
1026 : :
1027 : : /* Really set contents */
1028 : 19 : text->data = temp_data;
1029 : 19 : text->size = temp_size;
1030 : 20 : return PDF_TRUE;
1031 : : }
1032 : :
1033 : : pdf_bool_t
1034 : 1544 : pdf_text_set_unicode (pdf_text_t *text,
1035 : : const pdf_char_t *str,
1036 : : pdf_size_t size,
1037 : : enum pdf_text_unicode_encoding_e enc,
1038 : : pdf_error_t **error)
1039 : : {
1040 : 1544 : pdf_char_t *temp_data = NULL;
1041 : : pdf_size_t temp_size;
1042 : : pdf_bool_t ret;
1043 : :
1044 [ - + ]: 1544 : PDF_ASSERT_POINTER_RETURN_VAL (text, PDF_FALSE);
1045 [ - + ]: 1544 : PDF_ASSERT_POINTER_RETURN_VAL (str, PDF_FALSE);
1046 [ - + ]: 1544 : PDF_ASSERT_RETURN_VAL (size > 0, PDF_FALSE);
1047 [ - + ]: 1544 : PDF_ASSERT_RETURN_VAL (enc >= PDF_TEXT_UTF8, NULL);
1048 [ - + ]: 1544 : PDF_ASSERT_RETURN_VAL (enc < PDF_TEXT_MAX_UNICODE_ENC, NULL);
1049 : :
1050 : : /* If host endianness required, check it and convert input encoding */
1051 : 1544 : enc = pdf_text_transform_he_to_unicode_encoding (enc);
1052 : :
1053 [ + + + + + : 1544 : switch (enc)
- ]
1054 : : {
1055 : : case PDF_TEXT_UTF8: /* UTF-8 */
1056 : 948 : ret = pdf_text_utf8_to_utf32he (str,
1057 : : size,
1058 : : &temp_data,
1059 : : &temp_size,
1060 : : error);
1061 : 948 : break;
1062 : : case PDF_TEXT_UTF16_LE: /* UTF-16LE */
1063 : 80 : ret = pdf_text_utf16le_to_utf32he (str,
1064 : : size,
1065 : : &temp_data,
1066 : : &temp_size,
1067 : : error);
1068 : 80 : break;
1069 : : case PDF_TEXT_UTF16_BE: /* UTF-16BE */
1070 : 50 : ret = pdf_text_utf16be_to_utf32he (str,
1071 : : size,
1072 : : &temp_data,
1073 : : &temp_size,
1074 : : NULL,
1075 : : NULL,
1076 : : error);
1077 : 50 : break;
1078 : : case PDF_TEXT_UTF32_LE: /* UTF-32LE */
1079 : 80 : ret = pdf_text_utf32le_to_utf32he (str,
1080 : : size,
1081 : : &temp_data,
1082 : : &temp_size,
1083 : : error);
1084 : 80 : break;
1085 : : case PDF_TEXT_UTF32_BE: /* UTF-32BE */
1086 : 386 : ret = pdf_text_utf32be_to_utf32he (str,
1087 : : size,
1088 : : &temp_data,
1089 : : &temp_size,
1090 : : error);
1091 : 386 : break;
1092 : : default:
1093 : 0 : ret = PDF_TRUE;
1094 : : }
1095 : :
1096 [ + + ]: 1544 : if (!ret)
1097 : 160 : return PDF_FALSE;
1098 : :
1099 : : /* Destroy previous contents of text variable, if any */
1100 [ - + ]: 1384 : if (!pdf_text_clean_contents (text, error))
1101 : : {
1102 : 0 : pdf_dealloc (temp_data);
1103 : 0 : return PDF_FALSE;
1104 : : }
1105 : :
1106 : : /* Really set contents */
1107 : 1384 : text->data = temp_data;
1108 : 1384 : text->size = temp_size;
1109 : 1544 : return PDF_TRUE;
1110 : : }
1111 : :
1112 : : /* Concatenate the two text variables, only if country/lang info is equal */
1113 : : pdf_bool_t
1114 : 16 : pdf_text_concat (pdf_text_t *text1,
1115 : : const pdf_text_t *text2,
1116 : : pdf_bool_t override_langinfo,
1117 : : pdf_error_t **error)
1118 : : {
1119 : : pdf_char_t *tmp;
1120 : :
1121 [ - + ]: 16 : PDF_ASSERT_POINTER_RETURN_VAL (text1, PDF_FALSE);
1122 [ - + ]: 16 : PDF_ASSERT_POINTER_RETURN_VAL (text2, PDF_FALSE);
1123 : :
1124 [ + + ]: 16 : if (!override_langinfo)
1125 : : {
1126 : : /* An error will be returned if lang code is different */
1127 [ + + ]: 6 : if (strcmp (text1->lang, text2->lang) != 0)
1128 : : {
1129 : 2 : pdf_set_error (error,
1130 : : PDF_EDOMAIN_BASE_TEXT,
1131 : : PDF_ETEXTENC,
1132 : : "cannot concatenate two text objects: "
1133 : : "different language codes ('%s' and '%s')",
1134 : : text1->lang,
1135 : : text2->lang);
1136 : 2 : return PDF_FALSE;
1137 : : }
1138 : :
1139 : : /* An error will be returned if country code is different */
1140 [ - + ]: 4 : if (strcmp (text1->country, text2->country) != 0)
1141 : : {
1142 : 0 : pdf_set_error (error,
1143 : : PDF_EDOMAIN_BASE_TEXT,
1144 : : PDF_ETEXTENC,
1145 : : "cannot concatenate two text objects: "
1146 : : "different country codes ('%s' and '%s')",
1147 : : text1->country,
1148 : : text2->country);
1149 : 0 : return PDF_FALSE;
1150 : : }
1151 : : }
1152 : :
1153 : : /* Ok, so language/country info is equal or non-existent, start
1154 : : * concatenation */
1155 : :
1156 [ + + ]: 14 : if (text2->size == 0)
1157 : 5 : return PDF_TRUE;
1158 : :
1159 : : /* Re-allocate memory in first text element */
1160 : 9 : tmp = (pdf_char_t *) pdf_realloc (text1->data,
1161 : : text1->size + text2->size);
1162 [ - + ]: 9 : if (!tmp)
1163 : : {
1164 : 0 : pdf_set_error (error,
1165 : : PDF_EDOMAIN_BASE_TEXT,
1166 : : PDF_ENOMEM,
1167 : : "cannot concatenate two text objects: "
1168 : : "couldn't reallocate %lu bytes",
1169 : : (unsigned long)(text1->size + text2->size));
1170 : 0 : return PDF_FALSE;
1171 : : }
1172 : :
1173 : 9 : text1->data = tmp;
1174 : :
1175 : : /* Copy contents of second element after the first one */
1176 : 9 : memcpy (&(text1->data[text1->size]), text2->data, text2->size);
1177 : :
1178 : : /* Update size of first element */
1179 : 9 : text1->size += text2->size;
1180 : :
1181 : 9 : text1->modified = PDF_TRUE;
1182 : :
1183 : 16 : return PDF_TRUE;
1184 : : }
1185 : :
1186 : : /* Concatenate a text variable with an ascii string */
1187 : : pdf_bool_t
1188 : 4 : pdf_text_concat_ascii (pdf_text_t *text1,
1189 : : const pdf_char_t *ascii_str,
1190 : : pdf_error_t **error)
1191 : : {
1192 : : pdf_size_t len;
1193 : : pdf_char_t *tmp;
1194 : : pdf_char_t *tmp_data;
1195 : : pdf_size_t tmp_size;
1196 : :
1197 [ - + ]: 4 : PDF_ASSERT_POINTER_RETURN_VAL (text1, PDF_FALSE);
1198 [ - + ]: 4 : PDF_ASSERT_POINTER_RETURN_VAL (ascii_str, PDF_FALSE);
1199 : :
1200 : 4 : len = (pdf_size_t) strlen (ascii_str);
1201 : :
1202 [ + + ]: 4 : if (len == 0)
1203 : 2 : return PDF_TRUE;
1204 : :
1205 : : /* Check if input string is ascii */
1206 [ - + ]: 2 : if (!pdf_text_is_ascii7 (ascii_str, len))
1207 : : {
1208 : 0 : pdf_set_error (error,
1209 : : PDF_EDOMAIN_BASE_TEXT,
1210 : : PDF_ETEXTENC,
1211 : : "cannot concatenate ASCII string: "
1212 : : "not an ASCII input string");
1213 : 0 : return PDF_FALSE;
1214 : : }
1215 : :
1216 : : /* ascii string is valid utf8 */
1217 [ - + ]: 2 : if (!pdf_text_utf8_to_utf32he (ascii_str,
1218 : : len,
1219 : : &tmp_data,
1220 : : &tmp_size,
1221 : : error))
1222 : 0 : return PDF_FALSE;
1223 : :
1224 : 2 : tmp = (pdf_char_t *) pdf_realloc (text1->data, text1->size + tmp_size);
1225 [ - + ]: 2 : if (!tmp)
1226 : : {
1227 : 0 : pdf_set_error (error,
1228 : : PDF_EDOMAIN_BASE_TEXT,
1229 : : PDF_ENOMEM,
1230 : : "cannot concatenate ASCII string: "
1231 : : "couldn't reallocate %lu bytes",
1232 : : (unsigned long)(text1->size + tmp_size));
1233 : 0 : return PDF_FALSE;
1234 : : }
1235 : :
1236 : 2 : text1->data = tmp;
1237 : 2 : memcpy (&(text1->data[text1->size]), tmp_data, tmp_size);
1238 : 2 : text1->size += tmp_size;
1239 : 2 : pdf_dealloc (tmp_data);
1240 : :
1241 : 4 : return PDF_TRUE;
1242 : : }
1243 : :
1244 : : /* Default initial size of the list of replacements */
1245 : : #define PDF_TEXT_ISLR 32
1246 : :
1247 : : /* Replace a given pattern in a text object */
1248 : : pdf_bool_t
1249 : 20 : pdf_text_replace (pdf_text_t *text,
1250 : : const pdf_text_t *new_pattern,
1251 : : const pdf_text_t *old_pattern,
1252 : : pdf_error_t **error)
1253 : : {
1254 [ - + ]: 20 : PDF_ASSERT_POINTER_RETURN_VAL (text, PDF_FALSE);
1255 [ - + ]: 20 : PDF_ASSERT_POINTER_RETURN_VAL (new_pattern, PDF_FALSE);
1256 [ - + ]: 20 : PDF_ASSERT_POINTER_RETURN_VAL (old_pattern, PDF_FALSE);
1257 : :
1258 : 20 : return pdf_text_replace_multiple (text,
1259 : : new_pattern,
1260 : : &old_pattern,
1261 : : 1,
1262 : : error);
1263 : : }
1264 : :
1265 : : /* Check replacement patterns and get minimum size */
1266 : : static pdf_bool_t
1267 : : pdf_text_check_replacement_patterns (const pdf_text_t **p_old_patterns,
1268 : : int n_old_patterns,
1269 : : pdf_size_t *p_min_old_pattern_size,
1270 : : pdf_error_t **error)
1271 : : {
1272 : 24 : pdf_size_t minimum_old_pattern_size = -1;
1273 : : int i_pattern;
1274 : :
1275 [ + + ]: 60 : for (i_pattern = 0; i_pattern < n_old_patterns; ++i_pattern)
1276 : : {
1277 : : /* Get minimum old pattern size */
1278 [ + + ][ + + ]: 36 : if ((i_pattern == 0) ||
1279 : 12 : ((p_old_patterns[i_pattern])->size < minimum_old_pattern_size))
1280 : : {
1281 : 28 : minimum_old_pattern_size = (p_old_patterns[i_pattern])->size;
1282 : : }
1283 : : /* Empty old pattern is not allowed */
1284 [ - + ]: 36 : if (pdf_text_empty_p (p_old_patterns[i_pattern]))
1285 : : {
1286 : 0 : pdf_set_error (error,
1287 : : PDF_EDOMAIN_BASE_TEXT,
1288 : : PDF_ETEXTENC,
1289 : : "cannot check replacement patterns: "
1290 : : "input empty pattern found");
1291 : 0 : return PDF_FALSE;
1292 : : }
1293 : : }
1294 : :
1295 : : /* Set output var and exit correctly */
1296 : 24 : *p_min_old_pattern_size = minimum_old_pattern_size;
1297 : 24 : return PDF_TRUE;
1298 : : }
1299 : :
1300 : : static pdf_bool_t
1301 : 20 : pdf_text_get_replacement_pointers (pdf_text_repl_t **p_rep_ptrs,
1302 : : long *p_n_replacements,
1303 : : pdf_size_t *p_new_size,
1304 : : const pdf_text_t *text,
1305 : : pdf_size_t minimum_old_pattern_size,
1306 : : const pdf_text_t *new_pattern,
1307 : : const pdf_text_t **p_old_patterns,
1308 : : int n_old_patterns,
1309 : : pdf_error_t **error)
1310 : : {
1311 : : pdf_size_t new_size;
1312 : : int i_pattern;
1313 : : long i;
1314 : : long n_replacements;
1315 : 20 : pdf_text_repl_t *rep_ptrs = NULL;
1316 : 20 : long rep_ptrs_size = PDF_TEXT_ISLR/2;
1317 : :
1318 : 20 : n_replacements = 0;
1319 : 20 : i = 0;
1320 : 20 : new_size = 0;
1321 [ + + ]: 633 : while (i <= (text->size - minimum_old_pattern_size))
1322 : : {
1323 : : /* If old pattern found... */
1324 : 613 : int old_pattern_found = 0;
1325 : :
1326 : 613 : i_pattern = 0;
1327 [ + + ]: 1604 : while ((!old_pattern_found) &&
1328 : 1604 : (i_pattern < n_old_patterns))
1329 : : {
1330 [ + + ][ + + ]: 1025 : if (((text->size - i) >= ((p_old_patterns[i_pattern])->size)) &&
1331 : 1848 : (memcmp (&(text->data[i]),
1332 : 924 : (p_old_patterns[i_pattern])->data,
1333 : 924 : (p_old_patterns[i_pattern])->size) == 0))
1334 : : {
1335 : 34 : old_pattern_found = 1;
1336 : : /* Duplicate size of replacement pointers list, if needed */
1337 [ + + ]: 34 : if ((!rep_ptrs) ||
1338 : 34 : (rep_ptrs_size == n_replacements))
1339 : : {
1340 : 12 : rep_ptrs = (pdf_text_repl_t *) pdf_realloc (rep_ptrs,
1341 : : (2 * rep_ptrs_size *
1342 : : sizeof (pdf_text_repl_t)));
1343 [ - + ]: 12 : if (!rep_ptrs)
1344 : : {
1345 : 0 : pdf_set_error (error,
1346 : : PDF_EDOMAIN_BASE_TEXT,
1347 : : PDF_ENOMEM,
1348 : : "cannot get replacement pointers: "
1349 : : "couldn't reallocate %lu bytes",
1350 : : (unsigned long)(2 * rep_ptrs_size *
1351 : : sizeof (pdf_text_repl_t)));
1352 : 0 : return PDF_FALSE;
1353 : : }
1354 : : }
1355 : :
1356 : : /* Store pointer to old pattern */
1357 : 34 : rep_ptrs[n_replacements].data_ptr = &(text->data[i]);
1358 : 34 : rep_ptrs[n_replacements].old_pattern_i = i_pattern;
1359 : 34 : n_replacements++;
1360 : :
1361 : : /* The index must be updated to skip the replacement */
1362 : 34 : i += (p_old_patterns[i_pattern])->size;
1363 : :
1364 : : /* Update new size */
1365 : 34 : new_size += new_pattern->size;
1366 : : }
1367 : : else
1368 : : {
1369 : 957 : i_pattern++;
1370 : : }
1371 : : }
1372 : :
1373 [ + + ]: 613 : if(!old_pattern_found)
1374 : : {
1375 : 579 : i += 4;
1376 : 579 : new_size += 4;
1377 : : }
1378 : : }
1379 : :
1380 : : /* Udpate new size with remaining data in old array */
1381 : 20 : new_size += (text->size - i);
1382 : :
1383 : : /* Set output data and exit correctly */
1384 : 20 : *p_new_size = new_size;
1385 : 20 : *p_rep_ptrs = rep_ptrs;
1386 : 20 : *p_n_replacements = n_replacements;
1387 : :
1388 : 20 : return PDF_TRUE;
1389 : : }
1390 : :
1391 : : static pdf_bool_t
1392 : 12 : pdf_text_perform_replacements (pdf_text_t *text,
1393 : : pdf_size_t new_size,
1394 : : const pdf_text_t *new_pattern,
1395 : : const pdf_text_t **p_old_patterns,
1396 : : int n_old_patterns,
1397 : : const pdf_text_repl_t *rep_ptrs,
1398 : : long n_replacements,
1399 : : pdf_error_t **error)
1400 : : {
1401 : : int k;
1402 : : pdf_char_t *new_data;
1403 : : pdf_char_t *new_walker;
1404 : : pdf_char_t *old_walker;
1405 : :
1406 : : /* Allocate new memory chunk */
1407 : 12 : new_data = (pdf_char_t *) pdf_alloc (new_size);
1408 [ - + ]: 12 : if (!new_data)
1409 : : {
1410 : 0 : pdf_set_error (error,
1411 : : PDF_EDOMAIN_BASE_TEXT,
1412 : : PDF_ENOMEM,
1413 : : "cannot perform replacements: "
1414 : : "couldn't reallocate %lu bytes",
1415 : : (unsigned long)(new_size));
1416 : 0 : return PDF_FALSE;
1417 : : }
1418 : :
1419 : : /* Walk the list of replacements */
1420 : 12 : new_walker = new_data;
1421 : 12 : old_walker = text->data;
1422 [ + + ]: 46 : for (k = 0; k < n_replacements; ++k)
1423 : : {
1424 : : pdf_size_t prev_size;
1425 : :
1426 : : /* Store the data previous to the pointer */
1427 : 34 : prev_size = (rep_ptrs[k].data_ptr - old_walker);
1428 [ + + ]: 34 : if (prev_size > 0)
1429 : : {
1430 : 26 : memcpy (new_walker, old_walker, prev_size);
1431 : 26 : new_walker += prev_size;
1432 : 26 : old_walker += prev_size;
1433 : : }
1434 : : /* Perform the replacement */
1435 : 34 : memcpy (new_walker, new_pattern->data, new_pattern->size);
1436 : 34 : new_walker += (new_pattern->size);
1437 : 34 : old_walker += (p_old_patterns[rep_ptrs[k].old_pattern_i]->size);
1438 : : }
1439 : :
1440 : : /* Add final data */
1441 [ + + ]: 12 : if (((&(text->data[text->size])) - old_walker) > 0)
1442 : : {
1443 : 2 : memcpy (new_walker,
1444 : : old_walker,
1445 : 2 : ((&(text->data[text->size])) - old_walker));
1446 : : }
1447 : :
1448 : : /* Set correct final size and final content */
1449 : 12 : pdf_dealloc (text->data);
1450 : 12 : text->data = new_data;
1451 : 12 : text->size = new_size;
1452 : :
1453 : 12 : return PDF_TRUE;
1454 : : }
1455 : :
1456 : : pdf_bool_t
1457 : 24 : pdf_text_replace_multiple (pdf_text_t *text,
1458 : : const pdf_text_t *new_pattern,
1459 : : const pdf_text_t **p_old_patterns,
1460 : : int n_old_patterns,
1461 : : pdf_error_t **error)
1462 : : {
1463 : : pdf_size_t new_size;
1464 : : pdf_size_t minimum_old_pattern_size;
1465 : : long n_replacements;
1466 : 24 : pdf_text_repl_t *rep_ptrs = NULL;
1467 : :
1468 [ - + ]: 24 : PDF_ASSERT_POINTER_RETURN_VAL (text, PDF_FALSE);
1469 [ - + ]: 24 : PDF_ASSERT_POINTER_RETURN_VAL (new_pattern, PDF_FALSE);
1470 [ - + ]: 24 : PDF_ASSERT_POINTER_RETURN_VAL (p_old_patterns, PDF_FALSE);
1471 [ - + ]: 24 : PDF_ASSERT_RETURN_VAL (n_old_patterns > 0, PDF_FALSE);
1472 : :
1473 [ - + ]: 24 : if (!pdf_text_check_replacement_patterns (p_old_patterns,
1474 : : n_old_patterns,
1475 : : &minimum_old_pattern_size,
1476 : : error))
1477 : : {
1478 : 0 : pdf_prefix_error (error, "cannot replace text pattern: ");
1479 : 0 : return PDF_FALSE;
1480 : : }
1481 : :
1482 : : /* If input text is shorter than the smallest old pattern, there is no
1483 : : * replacement to be done */
1484 [ + + ]: 24 : if (minimum_old_pattern_size > text->size)
1485 : 4 : return PDF_TRUE;
1486 : :
1487 : : /* First, count number of replacements to be done... a replacement pointer
1488 : : * will be stored for each replacement needed */
1489 : 20 : new_size = 0;
1490 : 20 : minimum_old_pattern_size = -1;
1491 [ - + ]: 20 : if (!pdf_text_get_replacement_pointers (&rep_ptrs,
1492 : : &n_replacements,
1493 : : &new_size,
1494 : : text,
1495 : : minimum_old_pattern_size,
1496 : : new_pattern,
1497 : : p_old_patterns,
1498 : : n_old_patterns,
1499 : : error))
1500 : : {
1501 : 0 : pdf_prefix_error (error, "cannot replace text pattern: ");
1502 : 0 : return PDF_FALSE;
1503 : : }
1504 : :
1505 : : /* Now, really perform replacements, if required */
1506 [ + + ]: 20 : if (n_replacements > 0)
1507 : : {
1508 [ - + ]: 12 : if (!pdf_text_perform_replacements (text,
1509 : : new_size,
1510 : : new_pattern,
1511 : : p_old_patterns,
1512 : : n_old_patterns,
1513 : : rep_ptrs,
1514 : : n_replacements,
1515 : : error))
1516 : : {
1517 : 0 : pdf_prefix_error (error, "cannot replace text pattern: ");
1518 : 0 : return PDF_FALSE;
1519 : : }
1520 : :
1521 : : /* Dealloc list of pointers to replacements */
1522 [ + - ]: 12 : if (rep_ptrs)
1523 : 12 : pdf_dealloc (rep_ptrs);
1524 : :
1525 : 12 : text->modified = PDF_TRUE;
1526 : : }
1527 : :
1528 : 24 : return PDF_TRUE;
1529 : : }
1530 : :
1531 : : /* Replace a given ASCII-7 pattern in a text object */
1532 : : pdf_bool_t
1533 : 12 : pdf_text_replace_ascii (pdf_text_t *text,
1534 : : const pdf_char_t *new_pattern,
1535 : : const pdf_char_t *old_pattern,
1536 : : pdf_error_t **error)
1537 : : {
1538 : : pdf_text_t *new_pattern_text;
1539 : : pdf_text_t *old_pattern_text;
1540 : : pdf_bool_t ret_code;
1541 : :
1542 [ - + ]: 12 : PDF_ASSERT_POINTER_RETURN_VAL (text, PDF_FALSE);
1543 [ - + ]: 12 : PDF_ASSERT_POINTER_RETURN_VAL (new_pattern, PDF_FALSE);
1544 [ - + ]: 12 : PDF_ASSERT_POINTER_RETURN_VAL (old_pattern, PDF_FALSE);
1545 : :
1546 : : /* Check if patterns are real ASCII-7 valid strings */
1547 [ + - ][ - + ]: 12 : if ((!pdf_text_is_ascii7 (old_pattern,
1548 : : (pdf_size_t) strlen (old_pattern))) ||
1549 : : (!pdf_text_is_ascii7 (new_pattern,
1550 : : (pdf_size_t) strlen (new_pattern))))
1551 : : {
1552 : 0 : pdf_set_error (error,
1553 : : PDF_EDOMAIN_BASE_TEXT,
1554 : : PDF_ETEXTENC,
1555 : : "cannot replace ASCII pattern: "
1556 : : "at least one of the requested patterns is not ASCII-7");
1557 : 0 : return PDF_FALSE;
1558 : : }
1559 : :
1560 : : /* Ok, so load ASCII strings as if it were UTF-8 strings */
1561 : :
1562 : : /* Create intermediate pdf_text_t variables */
1563 : 12 : new_pattern_text = pdf_text_new_from_unicode (new_pattern,
1564 : : (pdf_size_t) strlen (new_pattern),
1565 : : PDF_TEXT_UTF8,
1566 : : error);
1567 [ - + ]: 12 : if (!new_pattern_text)
1568 : : {
1569 : 0 : pdf_prefix_error (error, "cannot replace ASCII pattern: ");
1570 : 0 : return PDF_FALSE;
1571 : : }
1572 : :
1573 : 12 : old_pattern_text = pdf_text_new_from_unicode (old_pattern,
1574 : : (pdf_size_t) strlen (old_pattern),
1575 : : PDF_TEXT_UTF8,
1576 : : error);
1577 [ - + ]: 12 : if (!old_pattern_text)
1578 : : {
1579 : 0 : pdf_prefix_error (error, "cannot replace ASCII pattern: ");
1580 : 0 : pdf_text_destroy (new_pattern_text);
1581 : 0 : return PDF_FALSE;
1582 : : }
1583 : :
1584 : : /* Perform replacement */
1585 : 12 : ret_code = pdf_text_replace (text,
1586 : : new_pattern_text,
1587 : : old_pattern_text,
1588 : : error);
1589 [ - + ]: 12 : if (!ret_code)
1590 : : {
1591 : 0 : pdf_prefix_error (error, "cannot replace ASCII pattern: ");
1592 : : }
1593 : :
1594 : : /* Destroy used intermediate variables */
1595 : 12 : pdf_text_destroy (new_pattern_text);
1596 : 12 : pdf_text_destroy (old_pattern_text);
1597 : :
1598 : 12 : return ret_code;
1599 : : }
1600 : :
1601 : : pdf_bool_t
1602 : 69 : pdf_text_filter (pdf_text_t *text,
1603 : : pdf_u32_t filter,
1604 : : pdf_error_t **error)
1605 : : {
1606 [ - + ]: 69 : PDF_ASSERT_POINTER_RETURN_VAL (text, PDF_FALSE);
1607 : :
1608 : : /* More than one filter at the same time can be requested! But Caution!
1609 : : * UpperCase filter, LowerCase filter and TitleCase filter are mutually
1610 : : * exclusive (at most only one of them must be enabled) */
1611 : :
1612 [ - + ]: 69 : if ((((filter & PDF_TEXT_FILTER_UPPER_CASE) ? 1 : 0) +
1613 : 69 : ((filter & PDF_TEXT_FILTER_LOWER_CASE) ? 1 : 0) +
1614 : 69 : ((filter & PDF_TEXT_FILTER_TITLE_CASE) ? 1 : 0)) > 1)
1615 : : {
1616 : 0 : pdf_set_error (error,
1617 : : PDF_EDOMAIN_BASE_TEXT,
1618 : : PDF_ETEXTENC,
1619 : : "cannot apply filters to text: "
1620 : : "at most only one case conversion filter can be applied");
1621 : 0 : return PDF_FALSE;
1622 : : }
1623 : :
1624 : : /* 0x00000001 */
1625 [ + + ][ - + ]: 69 : if ((filter & PDF_TEXT_FILTER_LINE_ENDINGS) &&
1626 : : (!pdf_text_filter_normalize_line_endings (text, error)))
1627 : : {
1628 : 0 : pdf_prefix_error (error, "cannot apply filters to text: ");
1629 : 0 : return PDF_FALSE;
1630 : : }
1631 : :
1632 : : /* 0x00000010 */
1633 [ + + ][ - + ]: 69 : if ((filter & PDF_TEXT_FILTER_UPPER_CASE) &&
1634 : : (!pdf_text_filter_upper_case (text, error)))
1635 : : {
1636 : 0 : pdf_prefix_error (error, "cannot apply filters to text: ");
1637 : 0 : return PDF_FALSE;
1638 : : }
1639 : : /* 0x00000100 */
1640 [ + + ][ - + ]: 69 : else if ((filter & PDF_TEXT_FILTER_LOWER_CASE) &&
1641 : : (!pdf_text_filter_lower_case (text, error)))
1642 : : {
1643 : 0 : pdf_prefix_error (error, "cannot apply filters to text: ");
1644 : 0 : return PDF_FALSE;
1645 : : }
1646 : : /* 0x00001000 */
1647 [ + + ][ - + ]: 69 : else if ((filter & PDF_TEXT_FILTER_TITLE_CASE) &&
1648 : : (!pdf_text_filter_title_case (text, error)))
1649 : : {
1650 : 0 : pdf_prefix_error (error, "cannot apply filters to text: ");
1651 : 0 : return PDF_FALSE;
1652 : : }
1653 : :
1654 : : /* 0x00010000 */
1655 [ + + ][ - + ]: 69 : if ((filter & PDF_TEXT_FILTER_REMOVE_AMP) &&
1656 : : (!pdf_text_filter_remove_amp (text, error)))
1657 : : {
1658 : 0 : pdf_prefix_error (error, "cannot apply filters to text: ");
1659 : 0 : return PDF_FALSE;
1660 : : }
1661 : :
1662 : : /* 0x00100000 */
1663 [ + + ][ - + ]: 69 : if ((filter & PDF_TEXT_FILTER_NORM_WITH_FULL_WIDTH) &&
1664 : : (!pdf_text_filter_normalize_full_width_ascii (text, error)))
1665 : : {
1666 : 0 : pdf_prefix_error (error, "cannot apply filters to text: ");
1667 : 0 : return PDF_FALSE;
1668 : : }
1669 : :
1670 : : /* 0x01000000 */
1671 [ + + ][ - + ]: 69 : if ((filter & PDF_TEXT_FILTER_REMOVE_LINE_ENDINGS) &&
1672 : : (!pdf_text_filter_remove_line_endings (text, error)))
1673 : : {
1674 : 0 : pdf_prefix_error (error, "cannot apply filters to text: ");
1675 : 0 : return PDF_FALSE;
1676 : : }
1677 : :
1678 : 69 : text->modified = PDF_TRUE;
1679 : 69 : return PDF_TRUE;
1680 : : }
1681 : :
1682 : : const pdf_char_t *
1683 : 0 : pdf_text_get_printable (pdf_text_t *text)
1684 : : {
1685 : : pdf_size_t size;
1686 : :
1687 [ # # ]: 0 : PDF_ASSERT_POINTER_RETURN_VAL (text, NULL);
1688 : :
1689 [ # # ]: 0 : if (text->printable != NULL)
1690 : : {
1691 [ # # ]: 0 : if (!text->modified)
1692 : 0 : return text->printable;
1693 : 0 : pdf_dealloc (text->printable);
1694 : : }
1695 : :
1696 : : #ifdef PDF_HOST_WIN32
1697 : : text->printable = pdf_text_get_unicode (text,
1698 : : PDF_TEXT_UTF16_LE,
1699 : : PDF_TEXT_UNICODE_WITH_NUL_SUFFIX,
1700 : : &size,
1701 : : NULL);
1702 : : return NULL;
1703 : : #else
1704 : 0 : text->printable = pdf_text_get_unicode (text,
1705 : : PDF_TEXT_UTF8,
1706 : : PDF_TEXT_UNICODE_WITH_NUL_SUFFIX,
1707 : : &size,
1708 : : NULL);
1709 : : #endif /*PDF_HOST_WIN32*/
1710 : :
1711 [ # # ]: 0 : if (text->printable)
1712 : 0 : text->modified = PDF_FALSE;
1713 : :
1714 : 0 : return text->printable;
1715 : : }
1716 : :
1717 : : pdf_i32_t
1718 : 9 : pdf_text_cmp (const pdf_text_t *text1,
1719 : : const pdf_text_t *text2,
1720 : : pdf_bool_t case_sensitive,
1721 : : pdf_error_t **error)
1722 : : {
1723 : 9 : pdf_error_t *inner_error = NULL;
1724 : : pdf_i32_t ret;
1725 : :
1726 [ - + ]: 9 : PDF_ASSERT_POINTER_RETURN_VAL (text1, -1);
1727 [ - + ]: 9 : PDF_ASSERT_POINTER_RETURN_VAL (text2, -1);
1728 : :
1729 : : /* Compare sizes of the texts */
1730 [ + + ]: 9 : if (text1->size != text2->size)
1731 [ + - ]: 2 : return ((text1->size > text2->size) ? 1 : -1);
1732 : :
1733 [ + + ]: 7 : if (case_sensitive)
1734 : 5 : return memcmp (text1->data, text2->data, text1->size);
1735 : :
1736 : 2 : ret = pdf_text_cmp_non_case_sensitive (text1, text2, &inner_error);
1737 [ - + ]: 2 : if (inner_error)
1738 : : {
1739 : 0 : pdf_propagate_error (error, inner_error);
1740 : 0 : pdf_prefix_error (error, "cannot compare text objects: ");
1741 : : }
1742 : 9 : return ret;
1743 : : }
1744 : :
1745 : : /* -------------------------- Private functions ----------------------------- */
1746 : :
1747 : : static pdf_i32_t
1748 : 2 : pdf_text_cmp_non_case_sensitive (const pdf_text_t *text1,
1749 : : const pdf_text_t *text2,
1750 : : pdf_error_t **error)
1751 : : {
1752 : : pdf_list_iterator_t it1;
1753 : : pdf_list_iterator_t it2;
1754 : : pdf_i32_t cmpret;
1755 : :
1756 : : /* Generate word boundaries list, if not already done */
1757 [ + - ][ - + ]: 2 : if ((!pdf_text_fill_word_boundaries_list (text1->word_boundaries,
1758 : 2 : text1->data,
1759 : : text1->size,
1760 : : error)) ||
1761 : : (!pdf_text_fill_word_boundaries_list (text2->word_boundaries,
1762 : 2 : text2->data,
1763 : : text2->size,
1764 : : error)))
1765 : : {
1766 : 0 : pdf_prefix_error (error,
1767 : : "cannot compare text non-case-sensitive: ");
1768 : 0 : return -1; /* An error happened computing word boundaries! */
1769 : : }
1770 : :
1771 : : /* Perform a word-per-word lower case comparison */
1772 : 2 : pdf_list_iterator_init (&it1, text1->word_boundaries);
1773 : 2 : pdf_list_iterator_init (&it2, text2->word_boundaries);
1774 : :
1775 : : while (PDF_TRUE)
1776 : : {
1777 : 20 : const struct pdf_text_wb_s *p_word1 = NULL;
1778 : 20 : const struct pdf_text_wb_s *p_word2 = NULL;
1779 : :
1780 : 20 : pdf_list_iterator_next (&it1, (const void **)&p_word1, NULL);
1781 : 20 : pdf_list_iterator_next (&it2, (const void **)&p_word2, NULL);
1782 : :
1783 [ + + ]: 20 : if (p_word1)
1784 : : {
1785 [ - + ]: 18 : if (!p_word2)
1786 : : {
1787 : 0 : cmpret = 1;
1788 : 0 : break;
1789 : : }
1790 : : else
1791 : : {
1792 : 18 : pdf_error_t *inner_error = NULL;
1793 : :
1794 : : /* We got 2 words to compare */
1795 : 18 : cmpret = pdf_text_compare_words (p_word1->word_start,
1796 : 18 : p_word1->word_size,
1797 : 18 : p_word2->word_start,
1798 : 18 : p_word2->word_size,
1799 : : pdf_text_get_language (text1),
1800 : : pdf_text_get_language (text2),
1801 : : &inner_error);
1802 [ - + ]: 18 : if (inner_error)
1803 : : {
1804 : 0 : pdf_propagate_error (error, inner_error);
1805 : 0 : pdf_prefix_error (error,
1806 : : "cannot compare text non-case-sensitive: ");
1807 : 0 : cmpret = -1;
1808 : 0 : break;
1809 : : }
1810 : :
1811 : : /* If words are not equal, return the code */
1812 [ + - ]: 18 : if (cmpret != 0)
1813 : : break;
1814 : : /* else, continue looping... */
1815 : : }
1816 : : }
1817 : : else
1818 : : {
1819 [ - + ]: 2 : cmpret = p_word2 ? -1 : 0;
1820 : 2 : break;
1821 : : }
1822 : : }
1823 : :
1824 : 2 : pdf_list_iterator_deinit (&it1);
1825 : 2 : pdf_list_iterator_deinit (&it2);
1826 : :
1827 : 2 : return cmpret;
1828 : : }
1829 : :
1830 : : static pdf_i32_t
1831 : 18 : pdf_text_compare_words (const pdf_char_t *word1,
1832 : : const pdf_size_t size1,
1833 : : const pdf_char_t *word2,
1834 : : const pdf_size_t size2,
1835 : : const pdf_char_t *language1,
1836 : : const pdf_char_t *language2,
1837 : : pdf_error_t **error)
1838 : : {
1839 : : pdf_char_t *lower1;
1840 : : pdf_char_t *lower2;
1841 : : pdf_size_t new_size1;
1842 : : pdf_size_t new_size2;
1843 : : pdf_size_t worst_size;
1844 : : pdf_i32_t ret_val;
1845 : :
1846 : : /* Compare sizes of words */
1847 [ - + ]: 18 : if (size1 != size2)
1848 [ # # ]: 0 : return ((size1 > size2) ? 1 : -1);
1849 : :
1850 : : /* Compute new worst word length */
1851 : 18 : worst_size = size1 * UCD_SC_MAX_EXPAND;
1852 : :
1853 : : /* Allocate memory for lowercases */
1854 : 18 : lower1 = (pdf_char_t *) pdf_alloc (worst_size);
1855 : 18 : lower2 = (pdf_char_t *) pdf_alloc (worst_size);
1856 : :
1857 [ - + ]: 18 : if ((!lower1) || (!lower2))
1858 : : {
1859 : 0 : pdf_set_error (error,
1860 : : PDF_EDOMAIN_BASE_TEXT,
1861 : : PDF_ENOMEM,
1862 : : "Couldn't allocate 2 chunks of %lu bytes",
1863 : : (unsigned long)worst_size);
1864 : 0 : pdf_dealloc (lower1);
1865 : 0 : pdf_dealloc (lower2);
1866 : 0 : return -1;
1867 : : }
1868 : :
1869 : : /* Lowercase words */
1870 [ - + ]: 18 : if (!pdf_text_ucd_word_change_case (lower1,
1871 : : &new_size1,
1872 : : UNICODE_CASE_INFO_LOWER_CASE,
1873 : : word1,
1874 : : size1,
1875 : : language1,
1876 : : error))
1877 : : {
1878 : 0 : pdf_dealloc (lower1);
1879 : 0 : pdf_dealloc (lower2);
1880 : 0 : return -1;
1881 : : }
1882 : :
1883 [ - + ]: 18 : if (!pdf_text_ucd_word_change_case (lower2,
1884 : : &new_size2,
1885 : : UNICODE_CASE_INFO_LOWER_CASE,
1886 : : word2,
1887 : : size2,
1888 : : language2,
1889 : : error))
1890 : : {
1891 : 0 : pdf_dealloc (lower1);
1892 : 0 : pdf_dealloc (lower2);
1893 : 0 : return -1;
1894 : : }
1895 : :
1896 : : /* Compare NEW sizes of words */
1897 [ - + ]: 18 : if (new_size1 != new_size2)
1898 : : {
1899 : 0 : pdf_dealloc (lower1);
1900 : 0 : pdf_dealloc (lower2);
1901 [ # # ]: 0 : return ((new_size1 > new_size2) ? 1 : -1);
1902 : : }
1903 : :
1904 : : /* Compare contents of words */
1905 : 18 : ret_val = memcmp (lower1, lower2, new_size1);
1906 : 18 : pdf_dealloc (lower1);
1907 : 18 : pdf_dealloc (lower2);
1908 : 18 : return ret_val;
1909 : : }
1910 : :
1911 : : /* Function to clean all contents of a given pdf_text_t variable */
1912 : : pdf_bool_t
1913 : 1409 : pdf_text_clean_contents (pdf_text_t *text,
1914 : : pdf_error_t **error)
1915 : : {
1916 : : /* Clean list of word breaks (destroy and create empty) */
1917 [ - + ]: 1409 : if (!pdf_text_clean_word_boundaries_list (&(text->word_boundaries), error))
1918 : 0 : return PDF_FALSE;
1919 : :
1920 : : /* Clear all contents */
1921 [ - + ]: 1409 : if (text->data != NULL)
1922 : : {
1923 : 0 : pdf_dealloc (text->data);
1924 : 0 : text->data = NULL;
1925 : : }
1926 : :
1927 : : /* Clean country and language info */
1928 : 1409 : memset (&(text->lang[0]), 0, PDF_TEXT_CCL);
1929 : 1409 : memset (&(text->country[0]), 0, PDF_TEXT_CCL);
1930 : : /* Reset data size */
1931 : 1409 : text->size = 0;
1932 : :
1933 : 1409 : text->modified = PDF_FALSE;
1934 [ - + ]: 1409 : if (text->printable != NULL)
1935 : : {
1936 : 0 : pdf_dealloc (text->printable);
1937 : 0 : text->printable = NULL;
1938 : : }
1939 : 1409 : return PDF_TRUE;
1940 : : }
1941 : :
1942 : : static pdf_bool_t
1943 : : pdf_text_get_lang_from_utf16be (pdf_text_t *element,
1944 : : const pdf_char_t *str_in,
1945 : : const pdf_size_t str_in_length,
1946 : : pdf_char_t **str_out,
1947 : : pdf_size_t *str_out_length,
1948 : : pdf_error_t **error)
1949 : : {
1950 : : /* Country code is optional */
1951 : 28 : pdf_bool_t country_available = PDF_FALSE;
1952 : : pdf_char_t aux[PDF_TEXT_CCL];
1953 : :
1954 : : /* Check last code marker position and MAXIMUM length of array.
1955 : : * Additionally, set `str_out' and `str_out_length' */
1956 [ + + ][ - + ]: 28 : if ((str_in[5] != PDF_TEXT_LCI_1) ||
1957 : 14 : (str_in[4] != PDF_TEXT_LCI_0))
1958 : : {
1959 : : /* Check last marker in bytes 6 and 7... */
1960 [ + - ][ + - ]: 14 : if ((str_in_length >= PDF_TEXT_LCMAXL) &&
[ + - ]
1961 : 14 : (str_in[7] == PDF_TEXT_LCI_1) &&
1962 : 14 : (str_in[6] == PDF_TEXT_LCI_0))
1963 : : {
1964 : 14 : country_available = PDF_TRUE;
1965 : 14 : *str_out = (pdf_char_t *)str_in + PDF_TEXT_LCMAXL;
1966 : 14 : *str_out_length = str_in_length - PDF_TEXT_LCMAXL;
1967 : : }
1968 : : }
1969 : : else
1970 : : {
1971 : : /* There is no optional country code info */
1972 : 14 : *str_out = (pdf_char_t *)str_in + PDF_TEXT_LCMINL;
1973 : 14 : *str_out_length = str_in_length - PDF_TEXT_LCMINL;
1974 : : }
1975 : :
1976 : : /* Store 2-bytes ISO 639 language code */
1977 : 28 : memcpy (&aux[0], &str_in[2], PDF_TEXT_CCL-1);
1978 : 28 : aux[PDF_TEXT_CCL-1] = '\0';
1979 : 28 : pdf_text_set_language (element, (pdf_char_t *)aux);
1980 : :
1981 : : /* If optional country code is also available, store it... */
1982 [ + + ]: 28 : if (country_available)
1983 : : {
1984 : 14 : memcpy (&aux[0], &str_in[4], PDF_TEXT_CCL-1);
1985 : : /* Last NUL byte is already set */
1986 : : /* Store 2-bytes ISO 3166 country code */
1987 : 14 : pdf_text_set_country (element, (pdf_char_t *)aux);
1988 : : }
1989 : :
1990 : 28 : return PDF_TRUE;
1991 : : }
1992 : :
1993 : : static enum pdf_text_unicode_encoding_e
1994 : : pdf_text_transform_he_to_unicode_encoding (enum pdf_text_unicode_encoding_e enc)
1995 : : {
1996 [ + + ][ + + ]: 2041 : if ((enc == PDF_TEXT_UTF16_HE) || (enc == PDF_TEXT_UTF32_HE))
1997 : 277 : enc += (PDF_IS_BIG_ENDIAN ? PDF_TEXT_HE_TO_BE : PDF_TEXT_HE_TO_LE);
1998 : 2041 : return enc;
1999 : : }
2000 : :
2001 : : static void
2002 : 108 : pdf_text_get_unicode_string_header (enum pdf_text_unicode_encoding_e enc,
2003 : : pdf_u32_t options,
2004 : : const pdf_char_t *language,
2005 : : const pdf_char_t *country,
2006 : : pdf_char_t header[PDF_TEXT_USHMAXL],
2007 : : pdf_size_t *header_length)
2008 : : {
2009 : : short bom_bytes;
2010 : : short lang_bytes;
2011 : : pdf_text_bom_t bom;
2012 : :
2013 : 108 : bom = pdf_text_get_unicode_bom (enc);
2014 : :
2015 : : /* Check if BOM really requested */
2016 : 108 : bom_bytes = 0;
2017 [ + + ]: 108 : if (options & PDF_TEXT_UNICODE_WITH_BOM)
2018 : 84 : bom_bytes = bom.bom_bytes;
2019 : :
2020 : : /* Check if Lang/Country code really requested (only for UTF16BE!!) */
2021 : 108 : lang_bytes = 0;
2022 [ + + ][ + + ]: 108 : if ((enc == PDF_TEXT_UTF16_BE) &&
[ + - ]
2023 : 60 : (options & PDF_TEXT_UTF16BE_WITH_LANGCODE) &&
2024 : 48 : (strlen (language) == 2))
2025 : : {
2026 : : /* At least language is available, but country may also be
2027 : : * available */
2028 [ + + ]: 48 : lang_bytes = (strlen (country) == 2) ? PDF_TEXT_LCMAXL : PDF_TEXT_LCMINL;
2029 : : }
2030 : :
2031 : : /* Modify header array, if needed, to add Language/Country info and/or
2032 : : * BOM */
2033 : 108 : *header_length = lang_bytes + bom_bytes;
2034 [ + - ]: 108 : if ((*header_length > 0) &&
2035 : : (*header_length < PDF_TEXT_USHMAXL)) /* (just in case) */
2036 : : {
2037 : : pdf_char_t *walker;
2038 : :
2039 : 108 : walker = &header[0];
2040 : : /* Add BOM */
2041 [ + + ]: 108 : if (bom_bytes > 0)
2042 : : {
2043 : 84 : memcpy (walker, bom.bom_data, bom_bytes);
2044 : : /* Update walker */
2045 : 84 : walker += bom_bytes;
2046 : : }
2047 : :
2048 : : /* Add Lang/Country */
2049 [ + + ]: 108 : if (lang_bytes > 0)
2050 : : {
2051 : : /* Language and Country */
2052 [ + + ]: 48 : if (lang_bytes == PDF_TEXT_LCMAXL)
2053 : : {
2054 : 24 : sprintf (walker, "%c%c%2s%2s%c%c",
2055 : : PDF_TEXT_LCI_0,PDF_TEXT_LCI_1,
2056 : : language, country,
2057 : : PDF_TEXT_LCI_0,PDF_TEXT_LCI_1);
2058 : : }
2059 : : /* Language only */
2060 : : else
2061 : : {
2062 : 24 : sprintf (walker, "%c%c%2s%c%c",
2063 : : PDF_TEXT_LCI_0,PDF_TEXT_LCI_1,
2064 : : language,
2065 : : PDF_TEXT_LCI_0,PDF_TEXT_LCI_1);
2066 : : }
2067 : : }
2068 : : }
2069 : 108 : }
2070 : :
2071 : : pdf_bool_t
2072 : 26 : pdf_text_is_ascii7 (const pdf_char_t *utf8data,
2073 : : const pdf_size_t size)
2074 : : {
2075 : : pdf_size_t i;
2076 : :
2077 [ + + ]: 140 : for (i = 0; i < size; ++i)
2078 : : {
2079 : : /* Just check the MSB. In ASCII-7 it must be 0 */
2080 [ - + ]: 114 : if (utf8data[i] & 0x80)
2081 : 0 : return PDF_FALSE;
2082 : : }
2083 : 26 : return PDF_TRUE;
2084 : : }
2085 : :
2086 : : /* Generate Word Boundaries list from text object */
2087 : : pdf_bool_t
2088 : 60 : pdf_text_generate_word_boundaries (pdf_text_t *text,
2089 : : pdf_error_t **error)
2090 : : {
2091 [ + - ]: 120 : return ((pdf_list_size (text->word_boundaries) == 0) ?
2092 : 60 : pdf_text_fill_word_boundaries_list (text->word_boundaries,
2093 : 60 : text->data,
2094 : : text->size,
2095 : : error) :
2096 : : PDF_TRUE);
2097 : : }
2098 : :
2099 : : void
2100 : 1599 : pdf_text_destroy_word_boundaries_list (pdf_list_t **p_word_boundaries)
2101 : : {
2102 : : pdf_list_iterator_t itr;
2103 : : const void *element;
2104 : :
2105 : 1599 : pdf_list_iterator_init (&itr, *p_word_boundaries);
2106 : :
2107 [ + + ]: 1947 : while (pdf_list_iterator_next (&itr, &element, NULL))
2108 : : {
2109 : : /* Dealloc word (pointed by the list element) */
2110 : 348 : pdf_dealloc (element);
2111 : : }
2112 : :
2113 : 1599 : pdf_list_iterator_deinit (&itr);
2114 : :
2115 : : /* Destroy list */
2116 : 1599 : pdf_list_destroy (*p_word_boundaries);
2117 : 1599 : *p_word_boundaries = NULL;
2118 : 1599 : }
2119 : :
2120 : : /* Create empty Word Boundaries list */
2121 : : pdf_list_t *
2122 : 1803 : pdf_text_create_word_boundaries_list (pdf_error_t **error)
2123 : : {
2124 : : /* Initialize word boundaries list */
2125 : 1803 : return pdf_list_new (NULL,
2126 : : NULL,
2127 : : PDF_TRUE,
2128 : : error);
2129 : : }
2130 : :
2131 : : /* Clean (destroy and create empty) Word Boundaries list */
2132 : : static pdf_bool_t
2133 : : pdf_text_clean_word_boundaries_list (pdf_list_t **p_word_boundaries,
2134 : : pdf_error_t **error)
2135 : : {
2136 : : /* Only destroy+create if list is not empty! */
2137 [ + - ]: 1409 : if (pdf_list_size (*p_word_boundaries) == 0)
2138 : : /* List is already empty */
2139 : 1409 : return PDF_TRUE;
2140 : :
2141 : : /* Destroy element contents */
2142 : 0 : pdf_text_destroy_word_boundaries_list (p_word_boundaries);
2143 : :
2144 : : /* Create empty list */
2145 : 0 : *p_word_boundaries = pdf_text_create_word_boundaries_list (error);
2146 : :
2147 : 0 : return (*p_word_boundaries != NULL ?
2148 : : PDF_TRUE :
2149 : : PDF_FALSE);
2150 : : }
2151 : :
2152 : : /* Fill in the Word Boundaries list using the given data */
2153 : : static pdf_bool_t
2154 : 64 : pdf_text_fill_word_boundaries_list (pdf_list_t *word_boundaries,
2155 : : const pdf_char_t *data,
2156 : : const pdf_size_t size,
2157 : : pdf_error_t **error)
2158 : : {
2159 : : pdf_char_t *walker;
2160 : : pdf_size_t n_bytes_left;
2161 : :
2162 : : /* Perform a basic check of data length */
2163 [ - + ]: 64 : if (size % 4 != 0)
2164 : : {
2165 : 0 : pdf_set_error (error,
2166 : : PDF_EDOMAIN_BASE_TEXT,
2167 : : PDF_EBADDATA,
2168 : : "cannot fill word boundaries list: "
2169 : : "wrong stream size (%lu), must be multiple of 4",
2170 : : (unsigned long) size);
2171 : 0 : return PDF_FALSE;
2172 : : }
2173 : :
2174 : : /* Only try to find word boundaries if length is greater than 0! */
2175 [ + + ]: 64 : if (size == 0)
2176 : 6 : return PDF_TRUE;
2177 : :
2178 : : /* Initialize walker and number of bytes left */
2179 : 58 : walker = (pdf_char_t *)data;
2180 : 58 : n_bytes_left = size;
2181 : :
2182 [ + + ]: 250 : while (n_bytes_left > 0)
2183 : : {
2184 : : struct pdf_text_wb_s *p_word;
2185 : :
2186 : : /* Allocate new word */
2187 : 192 : p_word = (struct pdf_text_wb_s *) pdf_alloc (sizeof (struct pdf_text_wb_s));
2188 [ - + ]: 192 : if (!p_word)
2189 : : {
2190 : 0 : pdf_set_error (error,
2191 : : PDF_EDOMAIN_BASE_TEXT,
2192 : : PDF_ENOMEM,
2193 : : "cannot fill word boundaries list: "
2194 : : "couldn't allocate %lu bytes",
2195 : : sizeof (struct pdf_text_wb_s));
2196 : 0 : return PDF_FALSE;
2197 : : }
2198 : :
2199 : : /* RULE WB1: Break at the start of text ( SOT % ) */
2200 : 192 : p_word->word_start = walker;
2201 : :
2202 [ - + ]: 192 : if (pdf_text_ucd_wb_detect_next (walker,
2203 : : n_bytes_left,
2204 : : &(p_word->word_stop),
2205 : : &n_bytes_left) != PDF_TRUE)
2206 : : {
2207 : 0 : pdf_set_error (error,
2208 : : PDF_EDOMAIN_BASE_TEXT,
2209 : : PDF_ETEXTENC,
2210 : : "cannot fill word boundaries list: "
2211 : : "couldn't detect next word break");
2212 : 0 : return PDF_FALSE;
2213 : : }
2214 : :
2215 : : /* Compute word size in bytes */
2216 : 192 : p_word->word_size = (p_word->word_stop - p_word->word_start) + 4;
2217 : :
2218 : : /* Add new word boundary to list */
2219 [ - + ]: 192 : if (!pdf_list_add_last (word_boundaries,
2220 : : p_word,
2221 : : error))
2222 : 0 : return PDF_FALSE;
2223 : :
2224 : : /* Update walker */
2225 : 192 : walker = p_word->word_stop + 4;
2226 : : }
2227 : :
2228 : 64 : return PDF_TRUE;
2229 : : }
2230 : :
2231 : : /* End of pdf-text.c */
|