1 /* font-manager-xml-writer.c
2 *
3 * Copyright (C) 2009 - 2021 Jerry Casiano
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.
17 *
18 * If not, see <http://www.gnu.org/licenses/gpl-3.0.txt>.
19 */
20
21 #include <libxml/xmlwriter.h>
22
23 #include "font-manager-xml-writer.h"
24
25 /**
26 * SECTION: font-manager-xml-writer
27 * @short_description: Convenience class for generating Fontconfig xml files
28 * @title: Xml Writer
29 * @include: font-manager-xml-writer.h
30 * @see_also: https://www.freedesktop.org/software/fontconfig/fontconfig-user.html
31 *
32 * Convenience class for generating fontconfig configuration files.
33 */
34
35 struct _FontManagerXmlWriter
36 {
37 GObject parent_instance;
38
39 gchar *filepath;
40 xmlTextWriter *writer;
41 };
42
43 G_DEFINE_TYPE(FontManagerXmlWriter, font_manager_xml_writer, G_TYPE_OBJECT)
44
45 enum
46 {
47 PROP_RESERVED,
48 PROP_FILEPATH,
49 N_PROPERTIES
50 };
51
52 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
53
54 static const gchar *DOCTYPE = "<!DOCTYPE fontconfig SYSTEM \"urn:fontconfig:fonts.dtd\">\n";
55 static const gchar *DOCGEN = " Generated by Font Manager. Do NOT edit this file. ";
56 static const gchar *DOCROOT = "fontconfig";
57
58 static void
font_manager_xml_writer_set_default_options(FontManagerXmlWriter * self)59 font_manager_xml_writer_set_default_options (FontManagerXmlWriter *self)
60 {
61 xmlTextWriterSetIndent(self->writer, TRUE);
62 xmlTextWriterSetIndentString(self->writer, (const xmlChar *) " ");
63 xmlTextWriterStartDocument(self->writer, NULL, NULL, NULL);
64 xmlTextWriterWriteString(self->writer, (xmlChar *) DOCTYPE);
65 xmlTextWriterWriteComment(self->writer, (xmlChar *) DOCGEN);
66 xmlTextWriterStartElement(self->writer, (xmlChar *) DOCROOT);
67 return;
68 }
69
70 static void
font_manager_xml_writer_reset(FontManagerXmlWriter * self)71 font_manager_xml_writer_reset (FontManagerXmlWriter *self)
72 {
73 g_clear_pointer(&self->writer, xmlFreeTextWriter);
74 g_clear_pointer(&self->filepath, g_free);
75 return;
76 }
77
78 static void
font_manager_xml_writer_dispose(GObject * gobject)79 font_manager_xml_writer_dispose (GObject *gobject)
80 {
81 g_return_if_fail(gobject != NULL);
82 font_manager_xml_writer_reset(FONT_MANAGER_XML_WRITER(gobject));
83 G_OBJECT_CLASS(font_manager_xml_writer_parent_class)->dispose(gobject);
84 return;
85 }
86
87 static void
font_manager_xml_writer_get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * pspec)88 font_manager_xml_writer_get_property (GObject *gobject,
89 guint property_id,
90 GValue *value,
91 GParamSpec *pspec)
92 {
93 g_return_if_fail(gobject != NULL);
94 switch (property_id)
95 {
96 case PROP_FILEPATH:
97 g_value_set_string(value, FONT_MANAGER_XML_WRITER(gobject)->filepath);
98 break;
99 default:
100 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
101 break;
102 }
103 return;
104 }
105
106 static void
font_manager_xml_writer_class_init(FontManagerXmlWriterClass * klass)107 font_manager_xml_writer_class_init (FontManagerXmlWriterClass *klass)
108 {
109 GObjectClass *object_class = G_OBJECT_CLASS(klass);
110 object_class->dispose = font_manager_xml_writer_dispose;
111 object_class->get_property = font_manager_xml_writer_get_property;
112
113 /**
114 * FontManagerXmlWriter:filepath:
115 *
116 * Path to file.
117 */
118 obj_properties[PROP_FILEPATH] = g_param_spec_string("filepath",
119 NULL,
120 "Filepath",
121 NULL,
122 G_PARAM_STATIC_STRINGS |
123 G_PARAM_READABLE |
124 G_PARAM_EXPLICIT_NOTIFY);
125
126 g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties);
127 return;
128 }
129
130 static void
font_manager_xml_writer_init(FontManagerXmlWriter * self)131 font_manager_xml_writer_init (FontManagerXmlWriter *self)
132 {
133 self->writer = NULL;
134 self->filepath = NULL;
135 return;
136 }
137
138 /**
139 * font_manager_xml_writer_open:
140 * @self: a #FontManagerXmlWriter
141 * @filepath: filepath to open for editing
142 *
143 * Returns: %TRUE on success
144 */
145 gboolean
font_manager_xml_writer_open(FontManagerXmlWriter * self,const gchar * filepath)146 font_manager_xml_writer_open (FontManagerXmlWriter *self, const gchar *filepath)
147 {
148 g_return_val_if_fail(self != NULL, FALSE);
149 g_return_val_if_fail(self->writer == NULL && self->filepath == NULL, FALSE);
150 self->writer = xmlNewTextWriterFilename(filepath, FALSE);
151 if (self->writer == NULL) {
152 g_critical(G_STRLOC ": Error opening %s", filepath);
153 return FALSE;
154 }
155 self->filepath = g_strdup(filepath);
156 font_manager_xml_writer_set_default_options(self);
157 return TRUE;
158 }
159
160 /**
161 * font_manager_xml_writer_close:
162 * @self: a #FontManagerXmlWriter
163 *
164 * Save and close current document
165 *
166 * Returns: %TRUE if document was successfully saved
167 */
168 gboolean
font_manager_xml_writer_close(FontManagerXmlWriter * self)169 font_manager_xml_writer_close (FontManagerXmlWriter *self)
170 {
171 g_return_val_if_fail(self != NULL, FALSE);
172 g_return_val_if_fail(self->writer != NULL, FALSE);
173 if (xmlTextWriterEndDocument(self->writer) < 0) {
174 g_critical(G_STRLOC ": Error closing %s", self->filepath);
175 return FALSE;
176 }
177 font_manager_xml_writer_reset(self);
178 return TRUE;
179 }
180
181 /**
182 * font_manager_xml_writer_discard:
183 * @self: a #FontManagerXmlWriter
184 *
185 * Close current document without saving.
186 */
187 void
font_manager_xml_writer_discard(FontManagerXmlWriter * self)188 font_manager_xml_writer_discard (FontManagerXmlWriter *self)
189 {
190 g_return_if_fail(self != NULL);
191 font_manager_xml_writer_reset(self);
192 return;
193 }
194
195 /**
196 * font_manager_xml_writer_start_element:
197 * @self: a #FontManagerXmlWriter
198 * @name: element name
199 *
200 * Start an xml element.
201 *
202 * Returns: The number of bytes written (may be 0 because of buffering) or -1 in case of error.
203 */
204 gint
font_manager_xml_writer_start_element(FontManagerXmlWriter * self,const gchar * name)205 font_manager_xml_writer_start_element (FontManagerXmlWriter *self, const gchar *name)
206 {
207 g_return_val_if_fail(self != NULL, -1);
208 g_return_val_if_fail(self->writer != NULL, -1);
209 g_return_val_if_fail(name != NULL, -1);
210 return xmlTextWriterStartElement(self->writer, (const xmlChar *) name);
211 }
212
213 /**
214 * font_manager_xml_writer_end_element:
215 * @self: a #FontManagerXmlWriter
216 *
217 * End the current xml element.
218 *
219 * Returns: The number of bytes written (may be 0 because of buffering) or -1 in case of error.
220 */
221 gint
font_manager_xml_writer_end_element(FontManagerXmlWriter * self)222 font_manager_xml_writer_end_element (FontManagerXmlWriter *self)
223 {
224 g_return_val_if_fail(self != NULL, -1);
225 g_return_val_if_fail(self->writer != NULL, -1);
226 return xmlTextWriterEndElement(self->writer);
227 }
228
229 /**
230 * font_manager_xml_writer_write_element:
231 * @self: a #FontManagerXmlWriter
232 * @name: element name
233 * @content: element content
234 *
235 * Returns: The number of bytes written (may be 0 because of buffering) or -1 in case of error.
236 */
237 gint
font_manager_xml_writer_write_element(FontManagerXmlWriter * self,const gchar * name,const gchar * content)238 font_manager_xml_writer_write_element (FontManagerXmlWriter *self,
239 const gchar *name,
240 const gchar *content)
241 {
242 g_return_val_if_fail(self != NULL, -1);
243 g_return_val_if_fail(self->writer != NULL, -1);
244 g_return_val_if_fail(name != NULL && content != NULL, -1);
245 return xmlTextWriterWriteElement(self->writer, (const xmlChar *) name, (const xmlChar *) content);
246 }
247
248 /**
249 * font_manager_xml_writer_write_attribute:
250 * @self: a #FontManagerXmlWriter
251 * @name: attribute name
252 * @content: attribute content
253 *
254 * Returns: The number of bytes written (may be 0 because of buffering) or -1 in case of error.
255 */
256 gint
font_manager_xml_writer_write_attribute(FontManagerXmlWriter * self,const gchar * name,const gchar * content)257 font_manager_xml_writer_write_attribute (FontManagerXmlWriter *self,
258 const gchar *name,
259 const gchar *content)
260 {
261 g_return_val_if_fail(self != NULL, -1);
262 g_return_val_if_fail(self->writer != NULL, -1);
263 g_return_val_if_fail(name != NULL && content != NULL, -1);
264 return xmlTextWriterWriteAttribute(self->writer, (const xmlChar *) name, (const xmlChar *) content);
265 }
266
267 /**
268 * font_manager_xml_writer_add_assignment:
269 * @self: a #FontManagerXmlWriter
270 * @a_name: name of property to edit
271 * @a_type: type of the property
272 * @a_val: new value to assign
273 *
274 * Assign a new value to a Fontconfig property.
275 * Valid types are int, double, bool and string.
276 */
277 void
font_manager_xml_writer_add_assignment(FontManagerXmlWriter * self,const gchar * a_name,const gchar * a_type,const gchar * a_val)278 font_manager_xml_writer_add_assignment (FontManagerXmlWriter *self,
279 const gchar *a_name,
280 const gchar *a_type,
281 const gchar *a_val)
282 {
283 g_return_if_fail(self != NULL);
284 g_return_if_fail(self->writer != NULL);
285 g_return_if_fail(a_name != NULL && a_type != NULL && a_val != NULL);
286 xmlTextWriterStartElement(self->writer, (xmlChar *) "edit");
287 xmlTextWriterWriteAttribute(self->writer, (xmlChar *) "name", (xmlChar *) a_name);
288 xmlTextWriterWriteAttribute(self->writer, (xmlChar *) "mode", (xmlChar *) "assign");
289 xmlTextWriterWriteAttribute(self->writer, (xmlChar *) "binding", (xmlChar *) "strong");
290 xmlTextWriterWriteElement(self->writer, (xmlChar *) a_type, (xmlChar *) a_val);
291 xmlTextWriterEndElement(self->writer);
292 return;
293 }
294
295 /**
296 * font_manager_xml_writer_add_elements:
297 * @self: an #FontManagerXmlWriter
298 * @e_type: element type
299 * @elements: (element-type utf8) (transfer none): a #GList
300 *
301 * Add simple elements to a fontconfig configuration file.
302 */
303 void
font_manager_xml_writer_add_elements(FontManagerXmlWriter * self,const gchar * e_type,GList * elements)304 font_manager_xml_writer_add_elements (FontManagerXmlWriter *self,
305 const gchar *e_type,
306 GList *elements)
307 {
308 g_return_if_fail(self != NULL);
309 g_return_if_fail(self->writer != NULL);
310 g_return_if_fail(e_type != NULL);
311 GList *iter;
312 for (iter = elements; iter != NULL; iter = iter->next) {
313 g_autofree gchar *element = g_markup_escape_text(g_strstrip(iter->data), -1);
314 xmlTextWriterWriteElement(self->writer, (xmlChar *) e_type, (xmlChar *) element);
315 }
316 return;
317 }
318
319 /**
320 * font_manager_xml_writer_add_patelt:
321 * @self: a #FontManagerXmlWriter
322 * @p_name: patelt name
323 * @p_type: patelt type
324 * @p_val: patelt value
325 *
326 * Write a valid fontconfig pattern elt.
327 * Valid patelt types are int, double, string, bool and const.
328 */
329 void
font_manager_xml_writer_add_patelt(FontManagerXmlWriter * self,const gchar * p_name,const gchar * p_type,const gchar * p_val)330 font_manager_xml_writer_add_patelt (FontManagerXmlWriter *self,
331 const gchar *p_name,
332 const gchar *p_type,
333 const gchar *p_val)
334 {
335 g_return_if_fail(self != NULL);
336 g_return_if_fail(self->writer != NULL);
337 g_return_if_fail(p_name != NULL && p_type != NULL && p_val != NULL);
338 xmlTextWriterStartElement(self->writer, (xmlChar *) "pattern");
339 xmlTextWriterStartElement(self->writer, (xmlChar *) "patelt");
340 xmlTextWriterWriteAttribute(self->writer, (xmlChar *) "name", (xmlChar *) p_name);
341 xmlTextWriterWriteElement(self->writer, (xmlChar *) p_type, (xmlChar *) p_val);
342 xmlTextWriterEndElement(self->writer);
343 xmlTextWriterEndElement(self->writer);
344 return;
345 }
346
347 /**
348 * font_manager_xml_writer_add_selections:
349 * @self: an #FontManagerXmlWriter
350 * @selection_type: acceptfont or rejectfont
351 * @selections: (element-type utf8) (transfer none): a #GList containing font family names
352 *
353 * Whitelist or blacklist a #GList of font families.
354 */
355 void
font_manager_xml_writer_add_selections(FontManagerXmlWriter * self,const gchar * selection_type,GList * selections)356 font_manager_xml_writer_add_selections (FontManagerXmlWriter *self,
357 const gchar *selection_type,
358 GList *selections)
359 {
360 g_return_if_fail(self != NULL);
361 g_return_if_fail(self->writer != NULL);
362 g_return_if_fail(selection_type != NULL);
363 xmlTextWriterStartElement(self->writer, (xmlChar *) "selectfont");
364 xmlTextWriterStartElement(self->writer, (xmlChar *) selection_type);
365 GList *iter;
366 for (iter = selections; iter != NULL; iter = iter->next) {
367 g_autofree gchar *element = g_markup_escape_text(iter->data, -1);
368 font_manager_xml_writer_add_patelt(self, "family", "string", element);
369 }
370 xmlTextWriterEndElement(self->writer);
371 xmlTextWriterEndElement(self->writer);
372 return;
373 }
374
375 /**
376 * font_manager_xml_writer_add_test_element:
377 * @self: an #FontManagerXmlWriter
378 * @t_name: fontconfig property to test
379 * @t_test: valid comparison operator
380 * @t_type: value type
381 * @t_val: value
382 *
383 * Valid comparison operators can be one of eq, not_eq, less, less_eq,
384 * more, more_eq, contains or not_contains.
385 * Valid value types are int, double, string, bool and const.
386 */
387 void
font_manager_xml_writer_add_test_element(FontManagerXmlWriter * self,const gchar * t_name,const gchar * t_test,const gchar * t_type,const gchar * t_val)388 font_manager_xml_writer_add_test_element (FontManagerXmlWriter *self,
389 const gchar *t_name,
390 const gchar *t_test,
391 const gchar *t_type,
392 const gchar *t_val)
393 {
394 g_return_if_fail(self != NULL);
395 g_return_if_fail(self->writer != NULL);
396 g_return_if_fail(t_name != NULL && t_test != NULL && t_type != NULL && t_val != NULL);
397 xmlTextWriterStartElement(self->writer, (xmlChar *) "test");
398 xmlTextWriterWriteAttribute(self->writer, (xmlChar *) "name", (xmlChar *) t_name);
399 xmlTextWriterWriteAttribute(self->writer, (xmlChar *) "compare", (xmlChar *) t_test);
400 xmlTextWriterWriteElement(self->writer, (xmlChar *) t_type, (xmlChar *) t_val);
401 xmlTextWriterEndElement(self->writer);
402 return;
403 }
404
405 /**
406 * font_manager_xml_writer_new:
407 *
408 * Returns: (transfer full): A newly created #FontManagerXmlWriter.
409 * Free the returned object using #g_object_unref().
410 **/
411 FontManagerXmlWriter *
font_manager_xml_writer_new(void)412 font_manager_xml_writer_new (void)
413 {
414 return g_object_new(FONT_MANAGER_TYPE_XML_WRITER, NULL);
415 }
416
417