GophHub - kevinboone/epub2txt2/src/xhtml.c


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.