1 /* Output stream for CSS styled text, producing HTML output.
2 Copyright (C) 2006-2007, 2019-2020 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2006.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17
18 #include <config.h>
19
20 /* Specification. */
21 #include "html-styled-ostream.h"
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27
28 #include "html-ostream.h"
29
30 #include "binary-io.h"
31 #ifndef O_TEXT
32 # define O_TEXT 0
33 #endif
34
35 #include "error.h"
36 #include "safe-read.h"
37 #include "xalloc.h"
38 #include "gettext.h"
39
40 #define _(str) gettext (str)
41
42
43 struct html_styled_ostream : struct styled_ostream
44 {
45 fields:
46 /* The destination stream. */
47 ostream_t destination;
48 /* The CSS filename. */
49 char *css_filename;
50 /* A HTML aware wrapper around the destination stream. */
51 html_ostream_t html_destination;
52 /* The current hyperlink id. */
53 char *hyperlink_id;
54 };
55
56 /* Implementation of ostream_t methods. */
57
58 static void
59 html_styled_ostream::write_mem (html_styled_ostream_t stream,
60 const void *data, size_t len)
61 {
62 html_ostream_write_mem (stream->html_destination, data, len);
63 }
64
65 static void
66 html_styled_ostream::flush (html_styled_ostream_t stream, ostream_flush_scope_t scope)
67 {
68 html_ostream_flush (stream->html_destination, scope);
69 }
70
71 static void
72 html_styled_ostream::free (html_styled_ostream_t stream)
73 {
74 html_ostream_free (stream->html_destination);
75 ostream_write_str (stream->destination, "</body>\n");
76 ostream_write_str (stream->destination, "</html>\n");
77 free (stream->hyperlink_id);
78 free (stream->css_filename);
79 free (stream);
80 }
81
82 /* Implementation of styled_ostream_t methods. */
83
84 static void
85 html_styled_ostream::begin_use_class (html_styled_ostream_t stream,
86 const char *classname)
87 {
88 html_ostream_begin_span (stream->html_destination, classname);
89 }
90
91 static void
92 html_styled_ostream::end_use_class (html_styled_ostream_t stream,
93 const char *classname)
94 {
95 html_ostream_end_span (stream->html_destination, classname);
96 }
97
98 static const char *
99 html_styled_ostream::get_hyperlink_ref (html_styled_ostream_t stream)
100 {
101 return html_ostream_get_hyperlink_ref (stream->html_destination);
102 }
103
104 static const char *
105 html_styled_ostream::get_hyperlink_id (html_styled_ostream_t stream)
106 {
107 return stream->hyperlink_id;
108 }
109
110 static void
111 html_styled_ostream::set_hyperlink (html_styled_ostream_t stream,
112 const char *ref, const char *id)
113 {
114 char *id_copy = (id != NULL ? xstrdup (id) : NULL);
115
116 html_ostream_set_hyperlink_ref (stream->html_destination, ref);
117 free (stream->hyperlink_id);
118 stream->hyperlink_id = id_copy;
119 }
120
121 static void
122 html_styled_ostream::flush_to_current_style (html_styled_ostream_t stream)
123 {
124 html_ostream_flush_to_current_style (stream->html_destination);
125 }
126
127 /* Constructor. */
128
129 html_styled_ostream_t
130 html_styled_ostream_create (ostream_t destination, const char *css_filename)
131 {
132 html_styled_ostream_t stream =
133 XMALLOC (struct html_styled_ostream_representation);
134
135 stream->base.base.vtable = &html_styled_ostream_vtable;
136 stream->destination = destination;
137 stream->css_filename = xstrdup (css_filename);
138 stream->html_destination = html_ostream_create (destination);
139 stream->hyperlink_id = NULL;
140
141 ostream_write_str (stream->destination, "<?xml version=\"1.0\"?>\n");
142 /* HTML 4.01 or XHTML 1.0?
143 Use HTML 4.01. This is conservative. Before switching to XHTML 1.0,
144 verify that in the output
145 - all HTML element names are in lowercase,
146 - all empty elements are denoted like <br/> or <p></p>,
147 - every attribute specification is in assignment form, like
148 <table border="1">,
149 - every <a name="..."> element also has an 'id' attribute,
150 - special characters like < > & " are escaped in the <style> and
151 <script> elements. */
152 ostream_write_str (stream->destination,
153 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n");
154 ostream_write_str (stream->destination, "<html>\n");
155 ostream_write_str (stream->destination, "<head>\n");
156 if (css_filename != NULL)
157 {
158 ostream_write_str (stream->destination, "<style type=\"text/css\">\n"
159 "<!--\n");
160
161 /* Include the contents of CSS_FILENAME literally. */
162 {
163 int fd;
164 char buf[4096];
165
166 fd = open (css_filename, O_RDONLY | O_TEXT);
167 if (fd < 0)
168 error (EXIT_FAILURE, errno,
169 _("error while opening \"%s\" for reading"),
170 css_filename);
171
172 for (;;)
173 {
174 size_t n_read = safe_read (fd, buf, sizeof (buf));
175 if (n_read == SAFE_READ_ERROR)
176 error (EXIT_FAILURE, errno, _("error reading \"%s\""),
177 css_filename);
178 if (n_read == 0)
179 break;
180
181 ostream_write_mem (stream->destination, buf, n_read);
182 }
183
184 if (close (fd) < 0)
185 error (EXIT_FAILURE, errno, _("error after reading \"%s\""),
186 css_filename);
187 }
188
189 ostream_write_str (stream->destination, "-->\n"
190 "</style>\n");
191 }
192 ostream_write_str (stream->destination, "</head>\n");
193 ostream_write_str (stream->destination, "<body>\n");
194
195 return stream;
196 }
197
198 /* Accessors. */
199
200 static ostream_t
201 html_styled_ostream::get_destination (html_styled_ostream_t stream)
202 {
203 return stream->destination;
204 }
205
206 static html_ostream_t
207 html_styled_ostream::get_html_destination (html_styled_ostream_t stream)
208 {
209 return stream->html_destination;
210 }
211
212 static const char *
213 html_styled_ostream::get_css_filename (html_styled_ostream_t stream)
214 {
215 return stream->css_filename;
216 }
217
218 /* Instanceof test. */
219
220 bool
221 is_instance_of_html_styled_ostream (ostream_t stream)
222 {
223 return IS_INSTANCE (stream, ostream, html_styled_ostream);
224 }