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