Raw File
1 /*============================================================================
2 epub2txt v2
3 xhtml.c
4 Copyright (c)2020 Kevin Boone, GPL v3.0
5 ============================================================================*/
6
7 #define _GNU_SOURCE
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <limits.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #ifndef __APPLE__
16 #include <malloc.h>
17 #endif
18 #include "epub2txt.h"
19 #include "log.h"
20 #include "custom_string.h"
21 #include "wstring.h"
22 #include "wrap.h"
23 #include "xhtml.h"
24
25 /*============================================================================
26 Format definition stuff
27 ============================================================================*/
28
29 typedef enum { FORMAT_NONE,
30 FORMAT_BOLD_ON, FORMAT_BOLD_OFF,
31 FORMAT_ITALIC_ON, FORMAT_ITALIC_OFF,
32 FORMAT_H1_ON, FORMAT_H1_OFF,
33 FORMAT_H2_ON, FORMAT_H2_OFF,
34 FORMAT_H3_ON, FORMAT_H3_OFF,
35 FORMAT_H4_ON, FORMAT_H4_OFF,
36 FORMAT_H5_ON, FORMAT_H5_OFF } Format;
37
38 /* bitmasks for ANSI highlighting */
39 enum { FMT_BOLD = 1 << 0,
40 FMT_ITAL = 1 << 1 };
41
42 /*============================================================================
43 xhtml_is_start_format_tag
44 ============================================================================*/
45 BOOL xhtml_is_start_format_tag (const char *tag, Format *format)
46 {
47 if (strcasecmp (tag, "b") == 0)
48 {
49 *format = FORMAT_BOLD_ON;
50 return TRUE;
51 }
52 if (strcasecmp (tag, "i") == 0)
53 {
54 *format = FORMAT_ITALIC_ON;
55 return TRUE;
56 }
57 return FALSE;
58 }
59
60
61 /*============================================================================
62 xhtml_is_end_breaking_tag
63 ============================================================================*/
64 BOOL xhtml_is_end_breaking_tag (const char *tag, Format *format)
65 {
66 if (strcasecmp (tag, "/h1") == 0)
67 {
68 *format = FORMAT_BOLD_OFF;
69 return TRUE;
70 }
71 if (strcasecmp (tag, "/h2") == 0)
72 {
73 *format = FORMAT_BOLD_OFF;
74 return TRUE;
75 }
76 if (strcasecmp (tag, "/h3") == 0)
77 {
78 *format = FORMAT_BOLD_OFF;
79 return TRUE;
80 }
81 if (strcasecmp (tag, "/h4") == 0)
82 {
83 *format = FORMAT_BOLD_OFF;
84 return TRUE;
85 }
86 if (strcasecmp (tag, "/h5") == 0)
87 {
88 *format = FORMAT_BOLD_OFF;
89 return TRUE;
90 }
91 if (strcasecmp (tag, "/div") == 0)
92 {
93 *format = FORMAT_NONE;
94 return TRUE;
95 }
96 if (strcasecmp (tag, "/blockquote") == 0)
97 {
98 *format = FORMAT_NONE;
99 return TRUE;
100 }
101 return FALSE;
102 }
103
104
105 /*============================================================================
106 xhtml_is_end_format_tag
107 ============================================================================*/
108 BOOL xhtml_is_end_format_tag (const char *tag, Format *format)
109 {
110 if (strcasecmp (tag, "/b") == 0)
111 {
112 *format = FORMAT_BOLD_OFF;
113 return TRUE;
114 }
115 if (strcasecmp (tag, "/i") == 0)
116 {
117 *format = FORMAT_ITALIC_OFF;
118 return TRUE;
119 }
120 return FALSE;
121 }
122
123
124 /*============================================================================
125 xhtml_is_start_breaking_tag
126 ============================================================================*/
127 BOOL xhtml_is_start_breaking_tag (const char *tag, Format *format)
128 {
129 if (strcasecmp (tag, "h1") == 0)
130 {
131 *format = FORMAT_BOLD_ON;
132 return TRUE;
133 }
134 if (strcasecmp (tag, "h2") == 0)
135 {
136 *format = FORMAT_BOLD_ON;
137 return TRUE;
138 }
139 if (strcasecmp (tag, "h3") == 0)
140 {
141 *format = FORMAT_BOLD_ON;
142 return TRUE;
143 }
144 if (strcasecmp (tag, "h4") == 0)
145 {
146 *format = FORMAT_BOLD_ON;
147 return TRUE;
148 }
149 if (strcasecmp (tag, "h5") == 0)
150 {
151 *format = FORMAT_BOLD_ON;
152 return TRUE;
153 }
154 if (strcasecmp (tag, "div") == 0)
155 {
156 *format = FORMAT_NONE;
157 return TRUE;
158 }
159 if (strcasecmp (tag, "blockquote") == 0)
160 {
161 *format = FORMAT_NONE;
162 return TRUE;
163 }
164 return FALSE;
165 }
166
167
168
169 /*============================================================================
170 xhtml_emit_format
171 ============================================================================*/
172 void xhtml_emit_format (const Epub2TxtOptions *options, Format format)
173 {
174 IN
175
176 if (options->ansi && !options->raw)
177 {
178 switch (format)
179 {
180 case FORMAT_BOLD_ON:
181 printf ("\x1B[1m"); break;
182
183 case FORMAT_BOLD_OFF:
184 printf ("\x1B[0m"); break;
185
186 case FORMAT_ITALIC_ON:
187 printf ("\x1B[3m"); break;
188
189 case FORMAT_ITALIC_OFF:
190 printf ("\x1B[0m"); break;
191
192 case FORMAT_NONE:
193 break;
194
195 case FORMAT_H1_ON:
196 case FORMAT_H2_ON:
197 case FORMAT_H3_ON:
198 case FORMAT_H4_ON:
199 case FORMAT_H5_ON:
200 printf ("\x1B[1m"); break;
201
202 case FORMAT_H1_OFF:
203 case FORMAT_H2_OFF:
204 case FORMAT_H3_OFF:
205 case FORMAT_H4_OFF:
206 case FORMAT_H5_OFF:
207 printf ("\x1B[0m"); break;
208
209 }
210 }
211 OUT
212 }
213
214 /*============================================================================
215 xhtml_emit_fmt_eol_pre
216 ============================================================================*/
217 void xhtml_emit_fmt_eol_pre (WrapTextContext *context)
218 {
219 IN
220
221 unsigned int fmt = wraptext_context_get_fmt (context);
222 const Epub2TxtOptions *options = (Epub2TxtOptions *) wraptext_context_get_app_opts (context);
223
224 if (options->ansi && !options->raw && fmt)
225 {
226 /* reset ANSI escape-sequence at EOL. */
227 xhtml_emit_format (options, FORMAT_BOLD_OFF);
228 }
229 OUT
230 }
231
232 /*============================================================================
233 xhtml_emit_fmt_eol_post
234 ============================================================================*/
235 void xhtml_emit_fmt_eol_post (WrapTextContext *context)
236 {
237 IN
238
239 unsigned int fmt = wraptext_context_get_fmt (context);
240 const Epub2TxtOptions *options = (Epub2TxtOptions *) wraptext_context_get_app_opts (context);
241
242 if (options->ansi && !options->raw && fmt)
243 {
244 /* turn those set, back on at BOL. */
245 if (fmt & FMT_BOLD)
246 xhtml_emit_format (options, FORMAT_BOLD_ON);
247 if (fmt & FMT_ITAL)
248 {
249 xhtml_emit_format (options, FORMAT_ITALIC_ON);
250 }
251 }
252 OUT
253 }
254
255 /*============================================================================
256 xhtml_set_format
257 ============================================================================*/
258 void xhtml_set_format (const Epub2TxtOptions *options, Format format, WrapTextContext *context)
259 {
260 IN
261
262 if (options->ansi && !options->raw)
263 {
264 switch (format)
265 {
266 case FORMAT_BOLD_ON:
267 wraptext_context_set_fmt (context, FMT_BOLD);
268 break;
269
270 case FORMAT_BOLD_OFF:
271 wraptext_context_reset_fmt (context, FMT_BOLD);
272 break;
273
274 case FORMAT_ITALIC_ON:
275 wraptext_context_set_fmt (context, FMT_ITAL);
276 break;
277
278 case FORMAT_ITALIC_OFF:
279 wraptext_context_reset_fmt (context, FMT_ITAL);
280 break;
281
282 case FORMAT_NONE:
283 wraptext_context_zero_fmt (context);
284 break;
285
286 case FORMAT_H1_ON:
287 case FORMAT_H2_ON:
288 case FORMAT_H3_ON:
289 case FORMAT_H4_ON:
290 case FORMAT_H5_ON:
291 wraptext_context_set_fmt (context, FMT_BOLD);
292 break;
293
294 case FORMAT_H1_OFF:
295 case FORMAT_H2_OFF:
296 case FORMAT_H3_OFF:
297 case FORMAT_H4_OFF:
298 case FORMAT_H5_OFF:
299 wraptext_context_reset_fmt (context, FMT_BOLD);
300 break;
301
302 }
303 }
304 OUT
305 }
306
307
308
309
310 /*============================================================================
311 xhtml_transform_char
312 ============================================================================*/
313 WString *xhtml_transform_char (uint32_t c, BOOL to_ascii)
314 {
315 WString *ret = wstring_create_empty();
316 if (to_ascii && c > 127) // No ASCII chars will need transforming
317 {
318 if (c == 0x00B4) return wstring_create_from_utf8 ("\'");
319 if (c == 0x0304) return wstring_create_from_utf8 ("-");
320 if (c == 0x2010) return wstring_create_from_utf8 ("-");
321 if (c == 0x2013) return wstring_create_from_utf8 ("-");
322 if (c == 0x2014) return wstring_create_from_utf8 ("-");
323 if (c == 0x2018) return wstring_create_from_utf8 ("'");
324 if (c == 0x2019) return wstring_create_from_utf8 ("\'");
325 if (c == 0x201C) return wstring_create_from_utf8 ("\"");
326 if (c == 0x201D) return wstring_create_from_utf8 ("\"");
327 if (c == 0xC2A0) return wstring_create_from_utf8 ("(c)"); // copyright
328 if (c == 0x00A9) return wstring_create_from_utf8 ("(c)"); // ditto
329 if (c == 0xC2A9) return wstring_create_from_utf8 (" "); // nbsp
330 if (c == 0x00A0) return wstring_create_from_utf8 (" "); // nbsp
331 if (c == 0x2026) return wstring_create_from_utf8 (",,,"); // elipsis
332 if (c == 0x2022) return wstring_create_from_utf8 ("."); // dot
333 if (c == 0x00B5) return wstring_create_from_utf8 ("u"); // mu
334 if (c == 0x00C0) return wstring_create_from_utf8 ("A"); // accented A
335 if (c == 0x00C1) return wstring_create_from_utf8 ("A"); // accented A
336 if (c == 0x00C2) return wstring_create_from_utf8 ("A"); // accented A
337 if (c == 0x00C3) return wstring_create_from_utf8 ("A"); // accented A
338 if (c == 0x00C4) return wstring_create_from_utf8 ("A"); // accented A
339 if (c == 0x00C5) return wstring_create_from_utf8 ("A"); // accented A
340 if (c == 0x00C6) return wstring_create_from_utf8 ("AE"); // accented A
341 if (c == 0x00C7) return wstring_create_from_utf8 ("C"); // cedilla
342 if (c == 0x00C8) return wstring_create_from_utf8 ("E"); // accented E
343 if (c == 0x00C9) return wstring_create_from_utf8 ("E"); // accented E
344 if (c == 0x00CA) return wstring_create_from_utf8 ("E"); // accented E
345 if (c == 0x00CB) return wstring_create_from_utf8 ("E"); // accented E
346 if (c == 0x00CC) return wstring_create_from_utf8 ("I"); // accented I
347 if (c == 0x00CD) return wstring_create_from_utf8 ("I"); // accented I
348 if (c == 0x00CE) return wstring_create_from_utf8 ("I"); // accented I
349 if (c == 0x00CF) return wstring_create_from_utf8 ("I"); // accented I
350 if (c == 0x00D0) return wstring_create_from_utf8 ("D"); // accented D
351 if (c == 0x00D1) return wstring_create_from_utf8 ("N"); // accented N
352 if (c == 0x00D2) return wstring_create_from_utf8 ("O"); // accented O
353 if (c == 0x00D3) return wstring_create_from_utf8 ("O"); // accented O
354 if (c == 0x00D4) return wstring_create_from_utf8 ("O"); // accented O
355 if (c == 0x00D5) return wstring_create_from_utf8 ("O"); // accented O
356 if (c == 0x00D6) return wstring_create_from_utf8 ("O"); // accented O
357 if (c == 0x00D7) return wstring_create_from_utf8 ("x"); // Multiply
358 if (c == 0x00D8) return wstring_create_from_utf8 ("O"); // accented O
359 if (c == 0x00D9) return wstring_create_from_utf8 ("U"); // accented U
360 if (c == 0x00DA) return wstring_create_from_utf8 ("U"); // accented U
361 if (c == 0x00DB) return wstring_create_from_utf8 ("U"); // accented U
362 if (c == 0x00DC) return wstring_create_from_utf8 ("U"); // accented U
363 if (c == 0x00DD) return wstring_create_from_utf8 ("Y"); // accented Y
364 if (c == 0x00DE) return wstring_create_from_utf8 ("Y"); // thorn
365 if (c == 0x00DF) return wstring_create_from_utf8 ("sz"); // esszet
366 if (c == 0x00E0) return wstring_create_from_utf8 ("a"); // accepted a
367 if (c == 0x00E1) return wstring_create_from_utf8 ("a"); // accepted a
368 if (c == 0x00E2) return wstring_create_from_utf8 ("a"); // accepted a
369 if (c == 0x00E3) return wstring_create_from_utf8 ("a"); // accepted a
370 if (c == 0x00E4) return wstring_create_from_utf8 ("a"); // accepted a
371 if (c == 0x00E5) return wstring_create_from_utf8 ("a"); // accepted a
372 if (c == 0x00E6) return wstring_create_from_utf8 ("ae"); // ae
373 if (c == 0x00E7) return wstring_create_from_utf8 ("c"); // cedilla
374 if (c == 0x00E8) return wstring_create_from_utf8 ("e"); //a ceepnted e
375 if (c == 0x00E9) return wstring_create_from_utf8 ("e"); //a ceepnted e
376 if (c == 0x00EA) return wstring_create_from_utf8 ("e"); //a ceepnted e
377 if (c == 0x00EB) return wstring_create_from_utf8 ("e"); //a ceepnted e
378 if (c == 0x00EC) return wstring_create_from_utf8 ("i"); //a ceepnted i
379 if (c == 0x00ED) return wstring_create_from_utf8 ("i"); //a ceepnted i
380 if (c == 0x00EE) return wstring_create_from_utf8 ("i"); //a ceepnted i
381 if (c == 0x00EF) return wstring_create_from_utf8 ("i"); //a ceepnted i
382 if (c == 0x00F0) return wstring_create_from_utf8 ("o"); //a ceepnted o
383 if (c == 0x00F1) return wstring_create_from_utf8 ("n"); //a ceepnted n
384 if (c == 0x00F2) return wstring_create_from_utf8 ("o"); //a ceepnted o
385 if (c == 0x00F3) return wstring_create_from_utf8 ("o"); //a ceepnted o
386 if (c == 0x00F4) return wstring_create_from_utf8 ("o"); //a ceepnted o
387 if (c == 0x00F5) return wstring_create_from_utf8 ("o"); //a ceepnted o
388 if (c == 0x00F6) return wstring_create_from_utf8 ("o"); //a ceepnted o
389 if (c == 0x00F7) return wstring_create_from_utf8 ("/"); // divide
390 if (c == 0x00F8) return wstring_create_from_utf8 ("o"); //a ceepnted o
391 if (c == 0x00F9) return wstring_create_from_utf8 ("u"); //a ceepnted u
392 if (c == 0x00FA) return wstring_create_from_utf8 ("u"); //a ceepnted u
393 if (c == 0x00FB) return wstring_create_from_utf8 ("u"); //a ceepnted u
394 if (c == 0x00FC) return wstring_create_from_utf8 ("u"); //a ceepnted u
395 if (c == 0x00FD) return wstring_create_from_utf8 ("y"); //a ceepnted y
396 if (c == 0x00FE) return wstring_create_from_utf8 ("y"); //a thorn
397 if (c == 0x00FF) return wstring_create_from_utf8 ("y"); //a ceepnted y
398 if (c == 0x0100) return wstring_create_from_utf8 ("A"); //a ceepnted A
399 if (c == 0x0101) return wstring_create_from_utf8 ("a"); //a ceepnted a
400 if (c == 0x0102) return wstring_create_from_utf8 ("A"); //a ceepnted A
401 if (c == 0x0103) return wstring_create_from_utf8 ("a"); //a ceepnted a
402 if (c == 0x0104) return wstring_create_from_utf8 ("A"); //a ceepnted A
403 if (c == 0x0105) return wstring_create_from_utf8 ("a"); //a ceepnted a
404 if (c == 0x0106) return wstring_create_from_utf8 ("C"); //a ceepnted C
405 if (c == 0x0107) return wstring_create_from_utf8 ("c"); //a ceepnted c
406 if (c == 0x0108) return wstring_create_from_utf8 ("C"); //a ceepnted C
407 if (c == 0x0109) return wstring_create_from_utf8 ("c"); //a ceepnted c
408 if (c == 0x010A) return wstring_create_from_utf8 ("C"); //a ceepnted C
409 if (c == 0x010B) return wstring_create_from_utf8 ("c"); //a ceepnted c
410 if (c == 0x010C) return wstring_create_from_utf8 ("C"); //a ceepnted C
411 if (c == 0x010D) return wstring_create_from_utf8 ("c"); //a ceepnted c
412 if (c == 0x010E) return wstring_create_from_utf8 ("D"); //a ceepnted D
413 if (c == 0x010F) return wstring_create_from_utf8 ("d"); //a ceepnted d
414 if (c == 0x0110) return wstring_create_from_utf8 ("D"); //a ceepnted D
415 if (c == 0x0111) return wstring_create_from_utf8 ("d"); //a ceepnted d
416 if (c == 0x0112) return wstring_create_from_utf8 ("E"); //a ceepnted E
417 if (c == 0x0113) return wstring_create_from_utf8 ("e"); //a ceepnted e
418 if (c == 0x0114) return wstring_create_from_utf8 ("E"); //a ceepnted E
419 if (c == 0x0115) return wstring_create_from_utf8 ("e"); //a ceepnted e
420 if (c == 0x0116) return wstring_create_from_utf8 ("E"); //a ceepnted E
421 if (c == 0x0117) return wstring_create_from_utf8 ("e"); //a ceepnted e
422 if (c == 0x0118) return wstring_create_from_utf8 ("E"); //a ceepnted E
423 if (c == 0x0119) return wstring_create_from_utf8 ("e"); //a ceepnted e
424 if (c == 0x011A) return wstring_create_from_utf8 ("E"); //a ceepnted E
425 if (c == 0x011B) return wstring_create_from_utf8 ("e"); //a ceepnted e
426 if (c == 0x011C) return wstring_create_from_utf8 ("G"); //a ceepnted G
427 if (c == 0x011D) return wstring_create_from_utf8 ("g"); //a ceepnted g
428 if (c == 0x011E) return wstring_create_from_utf8 ("G"); //a ceepnted G
429 if (c == 0x011F) return wstring_create_from_utf8 ("g"); //a ceepnted g
430 if (c == 0x0120) return wstring_create_from_utf8 ("G"); //a ceepnted G
431 if (c == 0x0121) return wstring_create_from_utf8 ("g"); //a ceepnted g
432 if (c == 0x0122) return wstring_create_from_utf8 ("G"); //a ceepnted G
433 if (c == 0x0123) return wstring_create_from_utf8 ("g"); //a ceepnted g
434 if (c == 0x0124) return wstring_create_from_utf8 ("H"); //a ceepnted H
435 if (c == 0x0125) return wstring_create_from_utf8 ("h"); //a ceepnted h
436 if (c == 0x0126) return wstring_create_from_utf8 ("H"); //a ceepnted H
437 if (c == 0x0127) return wstring_create_from_utf8 ("h"); //a ceepnted h
438 if (c == 0x0128) return wstring_create_from_utf8 ("I"); //a ceepnted I
439 if (c == 0x0129) return wstring_create_from_utf8 ("i"); //a ceepnted i
440 if (c == 0x012A) return wstring_create_from_utf8 ("I"); //a ceepnted I
441 if (c == 0x012B) return wstring_create_from_utf8 ("i"); //a ceepnted i
442 if (c == 0x012C) return wstring_create_from_utf8 ("I"); //a ceepnted I
443 if (c == 0x012D) return wstring_create_from_utf8 ("i"); //a ceepnted i
444 if (c == 0x012E) return wstring_create_from_utf8 ("I"); //a ceepnted I
445 if (c == 0x012F) return wstring_create_from_utf8 ("i"); //a ceepnted i
446 if (c == 0x0130) return wstring_create_from_utf8 ("I"); //a ceepnted I
447 if (c == 0x0131) return wstring_create_from_utf8 ("i"); //a ceepnted i
448 if (c == 0x0132) return wstring_create_from_utf8 ("IJ");
449 if (c == 0x0133) return wstring_create_from_utf8 ("ij");
450 if (c == 0x0134) return wstring_create_from_utf8 ("J"); //a ceepnted J
451 if (c == 0x0135) return wstring_create_from_utf8 ("j"); //a ceepnted j
452 if (c == 0x0136) return wstring_create_from_utf8 ("K"); //a ceepnted K
453 if (c == 0x0138) return wstring_create_from_utf8 ("K"); //a ceepnted K
454 if (c == 0x0138) return wstring_create_from_utf8 ("k"); //a ceepnted k
455 if (c == 0x0139) return wstring_create_from_utf8 ("L"); //a ceepnted L
456 if (c == 0x013A) return wstring_create_from_utf8 ("l"); //a ceepnted l
457 if (c == 0x013B) return wstring_create_from_utf8 ("L"); //a ceepnted L
458 if (c == 0x013C) return wstring_create_from_utf8 ("l"); //a ceepnted l
459 if (c == 0x013D) return wstring_create_from_utf8 ("L"); //a ceepnted L
460 if (c == 0x013E) return wstring_create_from_utf8 ("l"); //a ceepnted l
461 if (c == 0x013F) return wstring_create_from_utf8 ("L"); //a ceepnted L
462 if (c == 0x0140) return wstring_create_from_utf8 ("l"); //a ceepnted l
463 if (c == 0x0141) return wstring_create_from_utf8 ("L"); //a ceepnted L
464 if (c == 0x0142) return wstring_create_from_utf8 ("l"); //a ceepnted l
465 if (c == 0x0143) return wstring_create_from_utf8 ("N"); //a ceepnted N
466 if (c == 0x0144) return wstring_create_from_utf8 ("n"); //a ceepnted N
467 if (c == 0x0145) return wstring_create_from_utf8 ("N"); //a ceepnted N
468 if (c == 0x0146) return wstring_create_from_utf8 ("n"); //a ceepnted N
469 if (c == 0x0147) return wstring_create_from_utf8 ("N"); //a ceepnted N
470 if (c == 0x0148) return wstring_create_from_utf8 ("n"); //a ceepnted N
471 if (c == 0x0149) return wstring_create_from_utf8 ("N"); //a ceepnted N
472 if (c == 0x014A) return wstring_create_from_utf8 ("n"); //a ceepnted N
473 if (c == 0x014B) return wstring_create_from_utf8 ("n"); //a ceepnted n
474 if (c == 0x014C) return wstring_create_from_utf8 ("O"); //a ceepnted O
475 if (c == 0x014D) return wstring_create_from_utf8 ("o"); //a ceepnted o
476 if (c == 0x014E) return wstring_create_from_utf8 ("O"); //a ceepnted O
477 if (c == 0x014F) return wstring_create_from_utf8 ("o"); //a ceepnted o
478 if (c == 0x0150) return wstring_create_from_utf8 ("O"); //a ceepnted O
479 if (c == 0x0151) return wstring_create_from_utf8 ("o"); //a ceepnted o
480 if (c == 0x0152) return wstring_create_from_utf8 ("OE");
481 if (c == 0x0153) return wstring_create_from_utf8 ("oe");
482 if (c == 0x0154) return wstring_create_from_utf8 ("R"); // accepted R
483 if (c == 0x0155) return wstring_create_from_utf8 ("r"); // accepted r
484 if (c == 0x0156) return wstring_create_from_utf8 ("R"); // accepted R
485 if (c == 0x0157) return wstring_create_from_utf8 ("r"); // accepted r
486 if (c == 0x0158) return wstring_create_from_utf8 ("R"); // accepted R
487 if (c == 0x0159) return wstring_create_from_utf8 ("r"); // accepted r
488 if (c == 0x015A) return wstring_create_from_utf8 ("S"); // accepted S
489 if (c == 0x015B) return wstring_create_from_utf8 ("s"); // accepted s
490 if (c == 0x015C) return wstring_create_from_utf8 ("S"); // accepted S
491 if (c == 0x015D) return wstring_create_from_utf8 ("s"); // accepted s
492 if (c == 0x015E) return wstring_create_from_utf8 ("S"); // accepted S
493 if (c == 0x015F) return wstring_create_from_utf8 ("s"); // accepted s
494 if (c == 0x0160) return wstring_create_from_utf8 ("S"); // accepted S
495 if (c == 0x0161) return wstring_create_from_utf8 ("s"); // accepted s
496 if (c == 0x0162) return wstring_create_from_utf8 ("T"); // accepted T
497 if (c == 0x0163) return wstring_create_from_utf8 ("t"); // accepted t
498 if (c == 0x0164) return wstring_create_from_utf8 ("T"); // accepted T
499 if (c == 0x0165) return wstring_create_from_utf8 ("t"); // accepted t
500 if (c == 0x0166) return wstring_create_from_utf8 ("T"); // accepted T
501 if (c == 0x0167) return wstring_create_from_utf8 ("t"); // accepted t
502 if (c == 0x0168) return wstring_create_from_utf8 ("U"); // accepted U
503 if (c == 0x0169) return wstring_create_from_utf8 ("u"); // accepted u
504 if (c == 0x016A) return wstring_create_from_utf8 ("U"); // accepted U
505 if (c == 0x016B) return wstring_create_from_utf8 ("u"); // accepted u
506 if (c == 0x016C) return wstring_create_from_utf8 ("U"); // accepted U
507 if (c == 0x016D) return wstring_create_from_utf8 ("u"); // accepted u
508 if (c == 0x016E) return wstring_create_from_utf8 ("U"); // accepted U
509 if (c == 0x016F) return wstring_create_from_utf8 ("u"); // accepted u
510 if (c == 0x0170) return wstring_create_from_utf8 ("U"); // accepted U
511 if (c == 0x0171) return wstring_create_from_utf8 ("u"); // accepted u
512 if (c == 0x0172) return wstring_create_from_utf8 ("U"); // accepted U
513 if (c == 0x0173) return wstring_create_from_utf8 ("u"); // accepted u
514 if (c == 0x0174) return wstring_create_from_utf8 ("W"); // accepted W
515 if (c == 0x0175) return wstring_create_from_utf8 ("w"); // accepted w
516 if (c == 0x0176) return wstring_create_from_utf8 ("Y"); // accepted Y
517 if (c == 0x0177) return wstring_create_from_utf8 ("y"); // accepted y
518 if (c == 0x0178) return wstring_create_from_utf8 ("Y"); // accepted Y
519 if (c == 0x00) return wstring_create_from_utf8 (""); //
520 wstring_append_c (ret, c);
521 }
522 else
523 wstring_append_c (ret, c);
524 return ret;
525 }
526
527
528 /*============================================================================
529 xhtml_translate_entity
530 ============================================================================*/
531 WString *xhtml_translate_entity (const WString *entity)
532 {
533 /* Program flow in this function is very ugly, and prone to memory
534 leaks when modified. The whole thing needs to be rewritten */
535 char out[20];
536 IN
537 char *in = wstring_to_utf8 (entity);
538 if (strcasecmp (in, "amp") == 0)
539 strcpy (out, "&");
540 else if (strcasecmp (in, "nbsp") == 0)
541 strcpy (out, " ");
542 else if (strcasecmp (in, "lt") == 0)
543 strcpy (out, "<");
544 else if (strcasecmp (in, "gt") == 0)
545 strcpy (out, ">");
546 else if (strcasecmp (in, "cent") == 0)
547 strcpy (out, "¢");
548 else if (strcasecmp (in, "pound") == 0)
549 strcpy (out, "£");
550 else if (strcasecmp (in, "yen") == 0)
551 strcpy (out, "£");
552 else if (strcasecmp (in, "euro") == 0)
553 strcpy (out, "€");
554 else if (strcasecmp (in, "sect") == 0)
555 strcpy (out, "§");
556 else if (strcasecmp (in, "copy") == 0)
557 strcpy (out, "©");
558 else if (strcasecmp (in, "reg") == 0)
559 strcpy (out, "®");
560 else if (strcasecmp (in, "trade") == 0)
561 strcpy (out, "™");
562 else if (strcasecmp (in, "quot") == 0)
563 strcpy (out, "\"");
564 else if (in[0] == '#')
565 {
566 char *s = strdup (in);
567 s[0] = '0';
568 int v = 0;
569 if (sscanf (s, "%d", &v) == 1)
570 {
571 WString *ret = wstring_create_empty();
572 wstring_append_c (ret, (uint32_t)v);
573 OUT
574 free (s);
575 free (in);
576 return ret;
577 }
578 free (s);
579 }
580 else
581 {
582 strncpy (out, in, sizeof (out) - 1);
583 out[sizeof (out) - 1] = 0;
584 }
585 free (in);
586 OUT
587 return wstring_create_from_utf8 (out);
588 }
589
590
591 /*============================================================================
592 xhtml_flush_line
593 ============================================================================*/
594 void xhtml_flush_line (const WString *para, const Epub2TxtOptions *options,
595 WrapTextContext *context)
596 {
597 IN
598
599 if (options->raw)
600 {
601 char *s = wstring_to_utf8 (para);
602 fputs (s, stdout);
603 free (s);
604 }
605 else
606 {
607 wraptext_wrap_utf32 (context, wstring_wstr (para));
608 wraptext_eof (context);
609 }
610
611 OUT
612 }
613
614
615 /*============================================================================
616 xhtml_flush_para
617 ============================================================================*/
618 void xhtml_flush_para (const WString *para, const Epub2TxtOptions *options,
619 WrapTextContext *context)
620 {
621 IN
622
623 xhtml_flush_line (para, options, context);
624
625 OUT
626 }
627
628
629 /*============================================================================
630 xhtml_line_break
631 ============================================================================*/
632 void xhtml_line_break (WrapTextContext *context)
633 {
634 IN
635 //static uint32_t s[2] = { '\n', 0 };
636 static uint32_t s[2] = { WT_HARD_LINE_BREAK, 0 };
637 wraptext_wrap_utf32 (context, s);
638 wraptext_eof (context);
639 OUT
640 }
641
642
643 /*============================================================================
644 xhtml_para_break
645 ============================================================================*/
646 void xhtml_para_break (WrapTextContext *context,
647 const Epub2TxtOptions *options)
648 {
649 IN
650 static uint32_t s[3] = { '\n', '\n', 0 };
651 if (options->raw)
652 {
653 printf ("\n\n");
654 }
655 else
656 {
657 wraptext_wrap_utf32 (context, s);
658 }
659 OUT
660 }
661
662
663 /*============================================================================
664 xhtml_all_white
665 Note that, for the purpses of application logic, an empty string
666 is considered to be whitespace
667 ============================================================================*/
668 BOOL xhtml_all_white (WString *s)
669 {
670 if (wstring_length (s) == 0) return TRUE;
671 return wstring_is_whitespace (s);
672 }
673
674
675 /*============================================================================
676 xhtml_utf8_to_stdout
677 ============================================================================*/
678 void xhtml_utf8_to_stdout (const char *s, const Epub2TxtOptions *options,
679 char **error)
680 {
681 IN
682 char *ss;
683 // This is all a bit ugly. The entity translation is in
684 // xhtml_to_stdout, which expects something that looks like a viable
685 // XHTML file. There's no guarantee that the input to this function
686 // will actually be a full XHTML file, so we must wrap it in a body
687 // to fool xhtml_to_stdout. Ugh.
688 asprintf (&ss, "<body>%s</body>", s);
689 WString *sw = wstring_create_from_utf8 (ss);
690 xhtml_to_stdout (sw, options, error);
691 wstring_destroy (sw);
692 free (ss);
693 OUT
694 }
695
696 /*============================================================================
697 xhtml_file_to_stdout
698 ============================================================================*/
699 void xhtml_file_to_stdout (const char *filename, const Epub2TxtOptions *options,
700 char **error)
701 {
702 IN
703 log_debug ("Process XHTML file %s", filename);
704
705 WString *s;
706 wstring_create_from_utf8_file (filename, &s, error);
707 if (*error == NULL)
708 {
709 xhtml_to_stdout (s, options, error);
710 wstring_destroy (s);
711 }
712
713 OUT
714 }
715
716 /*============================================================================
717 xhtml_to_stdout
718 ============================================================================*/
719 void xhtml_to_stdout (const WString *s, const Epub2TxtOptions *options,
720 char **error)
721 {
722 IN
723 log_debug ("Process XHTML string");
724
725 typedef enum {MODE_ANY=0, MODE_INTAG = 1, MODE_ENTITY = 2} Mode;
726
727 if (TRUE)
728 {
729 int width;
730 if (options->width <= 0)
731 width = INT_MAX;
732 else
733 width = options->width - 1;
734
735 WrapTextContext *context = wraptext_context_new();
736 wraptext_context_set_width (context, width);
737 wraptext_context_set_app_opts (context, (void *)options);
738
739 Mode mode = MODE_ANY;
740 BOOL inbody = FALSE;
741 BOOL can_newline = FALSE;
742 WString *tag = wstring_create_empty();
743 WString *entity = wstring_create_empty();
744 WString *para = wstring_create_empty();
745 WString *ruby = wstring_create_empty();
746 BOOL inruby = FALSE;
747 int i, l = wstring_length (s);
748 uint32_t last_c = 0;
749 int taglen = 0;
750
751 const uint32_t *text = wstring_wstr (s);
752 for (i = 0; i < l; i++)
753 {
754 uint32_t c = text[i];
755 if (c == 13) // DOS EOL
756 continue;
757
758 if (c == 9) // Tab
759 c = ' ';
760
761 //printf ("c=%c %04x\n", (char)c, c);
762 if (mode == MODE_ANY && c == '<')
763 {
764 taglen = 0;
765 mode = MODE_INTAG;
766 }
767 else if (mode == MODE_ANY && c == '\n')
768 {
769 if (inbody)
770 {
771 if (last_c != ' ')
772 {
773 wstring_append_c (para, ' ');
774 }
775 }
776 }
777 else if (mode == MODE_ANY && c == '&')
778 {
779 mode = MODE_ENTITY;
780 }
781 else if (mode == MODE_ANY)
782 {
783 if (inbody)
784 {
785 if (c == ' ' && last_c == ' ')
786 {
787 }
788 else
789 {
790 WString *s = xhtml_transform_char (c, options->ascii);
791 wstring_append (inruby ? ruby : para, s);
792 wstring_destroy (s);
793 }
794 }
795 }
796 else if (mode == MODE_ENTITY && c == ';')
797 {
798 if (inbody)
799 {
800 WString *trans = xhtml_translate_entity (entity);
801 wstring_append (inruby ? ruby : para, trans);
802 wstring_destroy (trans);
803 }
804 wstring_clear (entity);
805 mode = MODE_ANY;
806 }
807 else if (mode == MODE_ENTITY)
808 {
809 wstring_append_c (entity, c);
810 }
811 else if (mode == MODE_INTAG && c == '>')
812 {
813 taglen = 0;
814 Format format = FORMAT_NONE;
815 char *ss_tag = wstring_to_utf8 (tag);
816 char *p = strchr (ss_tag, ' ');
817 if (p) *p = 0;
818 if (strcasecmp (ss_tag, "body") == 0)
819 {
820 inbody = TRUE;
821 }
822 else if (strcasecmp (ss_tag, "/body") == 0)
823 {
824 if (xhtml_all_white (para))
825 can_newline = FALSE;
826 else
827 can_newline = TRUE;
828 xhtml_flush_para (para, options, context);
829 wstring_clear (para);
830 if (can_newline)
831 {
832 xhtml_para_break (context, options);
833 can_newline = FALSE;
834 }
835 inbody = FALSE;
836 }
837 else if ((strcasecmp (ss_tag, "p/") == 0)
838 || (strcasecmp (ss_tag, "/p") == 0))
839 {
840 if (inbody)
841 {
842 if (xhtml_all_white (para))
843 can_newline = FALSE;
844 else
845 {
846 can_newline = TRUE;
847 }
848 xhtml_flush_para (para, options, context);
849 wstring_clear (para);
850 if (can_newline)
851 {
852 xhtml_para_break (context, options);
853 can_newline = FALSE;
854 }
855 }
856 }
857 else if ((strcasecmp (ss_tag, "br/") == 0)
858 || (strcasecmp (ss_tag, "br") == 0)
859 || (strcasecmp (ss_tag, "br /") == 0))
860 {
861 if (inbody)
862 {
863 if (xhtml_all_white (para))
864 can_newline = FALSE;
865 else
866 can_newline = TRUE;
867 xhtml_flush_para (para, options, context);
868 wstring_clear (para);
869 if (can_newline)
870 {
871 xhtml_line_break (context);
872 can_newline = FALSE;
873 }
874 }
875 }
876 else if (xhtml_is_start_format_tag (ss_tag, &format))
877 {
878 if (inbody)
879 {
880 xhtml_flush_line (para, options, context);
881 wstring_clear (para);
882 xhtml_emit_format (options, format);
883 xhtml_set_format (options, format, context);
884 }
885 }
886 else if (xhtml_is_end_format_tag (ss_tag, &format))
887 {
888 if (inbody)
889 {
890 xhtml_flush_line (para, options, context);
891 xhtml_emit_format (options, format);
892 xhtml_set_format (options, format, context);
893 wstring_clear (para);
894 }
895 }
896 else if (xhtml_is_end_breaking_tag (ss_tag, &format))
897 {
898 xhtml_flush_line (para, options, context);
899 xhtml_emit_format (options, format);
900 xhtml_set_format (options, format, context);
901 wstring_clear (para);
902 xhtml_para_break (context, options);
903 }
904
905 else if (xhtml_is_start_breaking_tag (ss_tag, &format))
906 {
907 xhtml_flush_line (para, options, context);
908 wstring_clear (para);
909 xhtml_emit_format (options, format);
910 xhtml_set_format (options, format, context);
911 }
912
913 else if (strcasecmp(ss_tag, "ruby") == 0)
914 {
915 wstring_clear (ruby);
916 }
917 else if (strcasecmp(ss_tag, "/ruby") == 0)
918 {
919 // Append concatenated ruby annotations
920 wstring_append_c (para, '(');
921 wstring_append (para, ruby);
922 wstring_append_c (para, ')');
923 wstring_clear (ruby);
924 }
925 else if (strcasecmp(ss_tag, "rt") == 0)
926 {
927 // Start accumulating ruby annotations
928 inruby = TRUE;
929 }
930 else if (strcasecmp(ss_tag, "/rt") == 0)
931 {
932 inruby = FALSE;
933 }
934
935 free (ss_tag);
936 wstring_clear (tag);
937 mode = MODE_ANY;
938 }
939 else if (mode == MODE_INTAG)
940 {
941 taglen++;
942 // Bug #5 -- Added support to abort tag reading if tag > 1000
943 // characters. This is an arbitrary number, but it's larger than
944 // any tag that we can handle.
945 if (taglen > 1000)
946 {
947 while (i < l)
948 {
949 uint32_t c = text[i];
950 if (c == (uint32_t)'>')
951 {
952 wstring_clear (tag);
953 }
954 i++;
955 }
956 }
957 wstring_append_c (tag, c);
958 }
959 else
960 log_error ("Unexpected character %d in mode %d", c, mode);
961 last_c = c;
962 }
963 if (wstring_length (para) > 0)
964 xhtml_flush_para (para, options, context);
965
966 wstring_destroy (tag);
967 wstring_destroy (entity);
968 wstring_destroy (para);
969 wstring_destroy (ruby);
970
971 wraptext_eof (context);
972 wraptext_context_free (context);
973 }
974 OUT
975 }
976
977
978
979
980
981
982
983
Generated by GNU Enscript 1.6.6, and GophHub 1.3.