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 */
|