mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-31 16:07:30 +03:00 
			
		
		
		
	Added variation font support
This commit is contained in:
		
							parent
							
								
									2334040f56
								
							
						
					
					
						commit
						da16b7ec45
					
				
							
								
								
									
										
											BIN
										
									
								
								Tests/fonts/AdobeVFPrototype.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/fonts/AdobeVFPrototype.ttf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -1,12 +1,12 @@ | ||||||
| 
 | 
 | ||||||
| NotoNastaliqUrdu-Regular.ttf, from https://github.com/googlei18n/noto-fonts | NotoNastaliqUrdu-Regular.ttf, from https://github.com/googlei18n/noto-fonts | ||||||
| NotoSansJP-Thin.otf, from https://www.google.com/get/noto/help/cjk/ | NotoSansJP-Thin.otf, from https://www.google.com/get/noto/help/cjk/ | ||||||
|  | AdobeVFPrototype.ttf, from https://github.com/adobe-fonts/adobe-variable-font-prototype | ||||||
|  | TINY5x3GX.ttf, from http://velvetyne.fr/fonts/tiny | ||||||
| 
 | 
 | ||||||
| All Noto fonts are published under the SIL Open Font License (OFL) v1.1 (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL), which allows you to copy, modify, and redistribute them if you need to. | All of the above fonts are published under the SIL Open Font License (OFL) v1.1 (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL), which allows you to copy, modify, and redistribute them if you need to. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 10x20-ISO8859-1.pcf | 10x20-ISO8859-1.pcf, from https://packages.ubuntu.com/xenial/xfonts-base | ||||||
| 
 |  | ||||||
| (from https://packages.ubuntu.com/xenial/xfonts-base) |  | ||||||
| 
 | 
 | ||||||
| "Public domain font.  Share and enjoy." | "Public domain font.  Share and enjoy." | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								Tests/fonts/TINY5x3GX.ttf
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/fonts/TINY5x3GX.ttf
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								Tests/images/variation_adobe.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/variation_adobe.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/variation_adobe_axes.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/variation_adobe_axes.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/variation_adobe_name.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/variation_adobe_name.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/variation_tiny.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/variation_tiny.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 239 B | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/variation_tiny_axes.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/variation_tiny_axes.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 937 B | 
							
								
								
									
										
											BIN
										
									
								
								Tests/images/variation_tiny_name.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								Tests/images/variation_tiny_name.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.1 KiB | 
|  | @ -570,6 +570,91 @@ class TestImageFont(PillowTestCase): | ||||||
|             self.assertRaises(KeyError, t.getmask, 'абвг', features=['-kern']) |             self.assertRaises(KeyError, t.getmask, 'абвг', features=['-kern']) | ||||||
|             self.assertRaises(KeyError, t.getmask, 'абвг', language='sr') |             self.assertRaises(KeyError, t.getmask, 'абвг', language='sr') | ||||||
| 
 | 
 | ||||||
|  |     def test_variation_get(self): | ||||||
|  |         font = self.get_font() | ||||||
|  | 
 | ||||||
|  |         freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version) | ||||||
|  |         if freetype < '2.9.1': | ||||||
|  |             self.assertRaises(NotImplementedError, font.get_variation_names) | ||||||
|  |             self.assertRaises(NotImplementedError, font.get_variation_axes) | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         self.assertRaises(IOError, font.get_variation_names) | ||||||
|  |         self.assertRaises(IOError, font.get_variation_axes) | ||||||
|  | 
 | ||||||
|  |         font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf") | ||||||
|  |         self.assertEqual( | ||||||
|  |             font.get_variation_names(), | ||||||
|  |             [b'ExtraLight', b'Light', b'Regular', b'Semibold', b'Bold', | ||||||
|  |              b'Black', b'Black Medium Contrast', b'Black High Contrast', b'Default']) | ||||||
|  |         self.assertEqual( | ||||||
|  |             font.get_variation_axes(), | ||||||
|  |             [{'name': b'Weight', 'minimum': 200, 'maximum': 900, 'default': 389}, | ||||||
|  |              {'name': b'Contrast', 'minimum': 0, 'maximum': 100, 'default': 0}]) | ||||||
|  | 
 | ||||||
|  |         font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf") | ||||||
|  |         self.assertEqual( | ||||||
|  |             font.get_variation_names(), | ||||||
|  |             [b'20', b'40', b'60', b'80', b'100', b'120', b'140', b'160', b'180', | ||||||
|  |              b'200', b'220', b'240', b'260', b'280', b'300', b'Regular']) | ||||||
|  |         self.assertEqual( | ||||||
|  |             font.get_variation_axes(), | ||||||
|  |             [{'name': b'Size', 'minimum': 0, 'maximum': 300, 'default': 0}]) | ||||||
|  | 
 | ||||||
|  |     def test_variation_set_by_name(self): | ||||||
|  |         font = self.get_font() | ||||||
|  | 
 | ||||||
|  |         freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version) | ||||||
|  |         if freetype < '2.9.1': | ||||||
|  |             self.assertRaises(NotImplementedError, font.set_variation_by_name, "Bold") | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         self.assertRaises(IOError, font.set_variation_by_name, "Bold") | ||||||
|  | 
 | ||||||
|  |         def _check_text(font, path, epsilon): | ||||||
|  |             im = Image.new("RGB", (100, 75), "white") | ||||||
|  |             d = ImageDraw.Draw(im) | ||||||
|  |             d.text((10, 10), "Text", font=font, fill="black") | ||||||
|  | 
 | ||||||
|  |             expected = Image.open(path) | ||||||
|  |             self.assert_image_similar(im, expected, epsilon) | ||||||
|  |         font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf", 36) | ||||||
|  |         _check_text(font, "Tests/images/variation_adobe.png", 11) | ||||||
|  |         for name in ["Bold", b"Bold"]: | ||||||
|  |             font.set_variation_by_name(name) | ||||||
|  |         _check_text(font, "Tests/images/variation_adobe_name.png", 11) | ||||||
|  | 
 | ||||||
|  |         font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf", 36) | ||||||
|  |         _check_text(font, "Tests/images/variation_tiny.png", 40) | ||||||
|  |         for name in ["200", b"200"]: | ||||||
|  |             font.set_variation_by_name(name) | ||||||
|  |         _check_text(font, "Tests/images/variation_tiny_name.png", 40) | ||||||
|  | 
 | ||||||
|  |     def test_variation_set_by_axes(self): | ||||||
|  |         font = self.get_font() | ||||||
|  | 
 | ||||||
|  |         freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version) | ||||||
|  |         if freetype < '2.9.1': | ||||||
|  |             self.assertRaises(NotImplementedError, font.set_variation_by_axes, [100]) | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         self.assertRaises(IOError, font.set_variation_by_axes, [500, 50]) | ||||||
|  | 
 | ||||||
|  |         def _check_text(font, path, epsilon): | ||||||
|  |             im = Image.new("RGB", (100, 75), "white") | ||||||
|  |             d = ImageDraw.Draw(im) | ||||||
|  |             d.text((10, 10), "Text", font=font, fill="black") | ||||||
|  | 
 | ||||||
|  |             expected = Image.open(path) | ||||||
|  |             self.assert_image_similar(im, expected, epsilon) | ||||||
|  |         font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf", 36) | ||||||
|  |         font.set_variation_by_axes([500, 50]) | ||||||
|  |         _check_text(font, "Tests/images/variation_adobe_axes.png", 5.1) | ||||||
|  | 
 | ||||||
|  |         font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf", 36) | ||||||
|  |         font.set_variation_by_axes([100]) | ||||||
|  |         _check_text(font, "Tests/images/variation_tiny_axes.png", 32.5) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| @unittest.skipUnless(HAS_RAQM, "Raqm not Available") | @unittest.skipUnless(HAS_RAQM, "Raqm not Available") | ||||||
| class TestImageFont_RaqmLayout(TestImageFont): | class TestImageFont_RaqmLayout(TestImageFont): | ||||||
|  |  | ||||||
|  | @ -417,6 +417,59 @@ class FreeTypeFont(object): | ||||||
|             layout_engine=layout_engine or self.layout_engine, |             layout_engine=layout_engine or self.layout_engine, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  |     def get_variation_names(self): | ||||||
|  |         """ | ||||||
|  |         :returns: A list of the named styles in a variation font. | ||||||
|  |         :exception IOError: If the font is not a variation font. | ||||||
|  |         """ | ||||||
|  |         try: | ||||||
|  |             names = self.font.getvarnames() | ||||||
|  |         except AttributeError: | ||||||
|  |             raise NotImplementedError("FreeType 2.9.1 or greater is required") | ||||||
|  |         return [name.replace(b"\x00", b"") for name in names] | ||||||
|  | 
 | ||||||
|  |     def set_variation_by_name(self, name): | ||||||
|  |         """ | ||||||
|  |         :param name: The name of the style. | ||||||
|  |         :exception IOError: If the font is not a variation font. | ||||||
|  |         """ | ||||||
|  |         names = self.get_variation_names() | ||||||
|  |         if not isinstance(name, bytes): | ||||||
|  |             name = name.encode() | ||||||
|  |         index = names.index(name) | ||||||
|  | 
 | ||||||
|  |         if index == getattr(self, "_last_variation_index", None): | ||||||
|  |             # When the same name is set twice in a row, | ||||||
|  |             # there is an 'unknown freetype error' | ||||||
|  |             # https://savannah.nongnu.org/bugs/?56186 | ||||||
|  |             return | ||||||
|  |         self._last_variation_index = index | ||||||
|  | 
 | ||||||
|  |         self.font.setvarname(index) | ||||||
|  | 
 | ||||||
|  |     def get_variation_axes(self): | ||||||
|  |         """ | ||||||
|  |         :returns: A list of the axes in a variation font. | ||||||
|  |         :exception IOError: If the font is not a variation font. | ||||||
|  |         """ | ||||||
|  |         try: | ||||||
|  |             axes = self.font.getvaraxes() | ||||||
|  |         except AttributeError: | ||||||
|  |             raise NotImplementedError("FreeType 2.9.1 or greater is required") | ||||||
|  |         for axis in axes: | ||||||
|  |             axis["name"] = axis["name"].replace(b"\x00", b"") | ||||||
|  |         return axes | ||||||
|  | 
 | ||||||
|  |     def set_variation_by_axes(self, axes): | ||||||
|  |         """ | ||||||
|  |         :param axes: A list of values for each axis. | ||||||
|  |         :exception IOError: If the font is not a variation font. | ||||||
|  |         """ | ||||||
|  |         try: | ||||||
|  |             self.font.setvaraxes(axes) | ||||||
|  |         except AttributeError: | ||||||
|  |             raise NotImplementedError("FreeType 2.9.1 or greater is required") | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class TransposedFont(object): | class TransposedFont(object): | ||||||
|     "Wrapper for writing rotated or mirrored text" |     "Wrapper for writing rotated or mirrored text" | ||||||
|  |  | ||||||
							
								
								
									
										162
									
								
								src/_imagingft.c
									
									
									
									
									
								
							
							
						
						
									
										162
									
								
								src/_imagingft.c
									
									
									
									
									
								
							|  | @ -25,6 +25,8 @@ | ||||||
| #include <ft2build.h> | #include <ft2build.h> | ||||||
| #include FT_FREETYPE_H | #include FT_FREETYPE_H | ||||||
| #include FT_GLYPH_H | #include FT_GLYPH_H | ||||||
|  | #include FT_MULTIPLE_MASTERS_H | ||||||
|  | #include FT_SFNT_NAMES_H | ||||||
| 
 | 
 | ||||||
| #define KEEP_PY_UNICODE | #define KEEP_PY_UNICODE | ||||||
| #include "py3.h" | #include "py3.h" | ||||||
|  | @ -877,6 +879,158 @@ font_render(FontObject* self, PyObject* args) | ||||||
|     Py_RETURN_NONE; |     Py_RETURN_NONE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #if FREETYPE_MAJOR > 2 ||\ | ||||||
|  |     (FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 9) ||\ | ||||||
|  |     (FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 9 && FREETYPE_PATCH == 1) | ||||||
|  |     static PyObject* | ||||||
|  |     font_getvarnames(FontObject* self, PyObject* args) | ||||||
|  |     { | ||||||
|  |         int error; | ||||||
|  |         FT_UInt i, j, num_namedstyles, name_count; | ||||||
|  |         FT_MM_Var *master; | ||||||
|  |         FT_SfntName name; | ||||||
|  |         PyObject *list_names, *list_name; | ||||||
|  | 
 | ||||||
|  |         error = FT_Get_MM_Var(self->face, &master); | ||||||
|  |         if (error) | ||||||
|  |             return geterror(error); | ||||||
|  | 
 | ||||||
|  |         num_namedstyles = master->num_namedstyles; | ||||||
|  |         list_names = PyList_New(num_namedstyles); | ||||||
|  | 
 | ||||||
|  |         name_count = FT_Get_Sfnt_Name_Count(self->face); | ||||||
|  |         for (i = 0; i < name_count; i++) { | ||||||
|  |             error = FT_Get_Sfnt_Name(self->face, i, &name); | ||||||
|  |             if (error) | ||||||
|  |                 return geterror(error); | ||||||
|  | 
 | ||||||
|  |             for (j = 0; j < num_namedstyles; j++) { | ||||||
|  |                 if (PyList_GetItem(list_names, j) != NULL) | ||||||
|  |                     continue; | ||||||
|  | 
 | ||||||
|  |                 if (master->namedstyle[j].strid == name.name_id) { | ||||||
|  |                     list_name = Py_BuildValue(PY_ARG_BYTES_LENGTH, | ||||||
|  |                                               name.string, name.string_len); | ||||||
|  |                     PyList_SetItem(list_names, j, list_name); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         FT_Done_MM_Var(library, master); | ||||||
|  | 
 | ||||||
|  |         return list_names; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static PyObject* | ||||||
|  |     font_getvaraxes(FontObject* self, PyObject* args) | ||||||
|  |     { | ||||||
|  |         int error; | ||||||
|  |         FT_UInt i, j, num_axis, name_count; | ||||||
|  |         FT_MM_Var* master; | ||||||
|  |         FT_Var_Axis axis; | ||||||
|  |         FT_SfntName name; | ||||||
|  |         PyObject *list_axes, *list_axis, *axis_name; | ||||||
|  |         error = FT_Get_MM_Var(self->face, &master); | ||||||
|  |         if (error) | ||||||
|  |             return geterror(error); | ||||||
|  | 
 | ||||||
|  |         num_axis = master->num_axis; | ||||||
|  |         name_count = FT_Get_Sfnt_Name_Count(self->face); | ||||||
|  | 
 | ||||||
|  |         list_axes = PyList_New(num_axis); | ||||||
|  |         for (i = 0; i < num_axis; i++) { | ||||||
|  |             axis = master->axis[i]; | ||||||
|  | 
 | ||||||
|  |             list_axis = PyDict_New(); | ||||||
|  |             PyDict_SetItemString(list_axis, "minimum", | ||||||
|  |                                  PyInt_FromLong(axis.minimum / 65536)); | ||||||
|  |             PyDict_SetItemString(list_axis, "default", | ||||||
|  |                                  PyInt_FromLong(axis.def / 65536)); | ||||||
|  |             PyDict_SetItemString(list_axis, "maximum", | ||||||
|  |                                  PyInt_FromLong(axis.maximum / 65536)); | ||||||
|  | 
 | ||||||
|  |             for (j = 0; j < name_count; j++) { | ||||||
|  |                 error = FT_Get_Sfnt_Name(self->face, j, &name); | ||||||
|  |                 if (error) | ||||||
|  |                     return geterror(error); | ||||||
|  | 
 | ||||||
|  |                 if (name.name_id == axis.strid) { | ||||||
|  |                     axis_name = Py_BuildValue(PY_ARG_BYTES_LENGTH, | ||||||
|  |                                               name.string, name.string_len); | ||||||
|  |                     PyDict_SetItemString(list_axis, "name", axis_name); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             PyList_SetItem(list_axes, i, list_axis); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         FT_Done_MM_Var(library, master); | ||||||
|  | 
 | ||||||
|  |         return list_axes; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static PyObject* | ||||||
|  |     font_setvarname(FontObject* self, PyObject* args) | ||||||
|  |     { | ||||||
|  |         int error; | ||||||
|  | 
 | ||||||
|  |         int instance_index; | ||||||
|  |         if (!PyArg_ParseTuple(args, "i", &instance_index)) | ||||||
|  |             return NULL; | ||||||
|  | 
 | ||||||
|  |         error = FT_Set_Named_Instance(self->face, instance_index); | ||||||
|  |         if (error) | ||||||
|  |             return geterror(error); | ||||||
|  | 
 | ||||||
|  |         Py_INCREF(Py_None); | ||||||
|  |         return Py_None; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static PyObject* | ||||||
|  |     font_setvaraxes(FontObject* self, PyObject* args) | ||||||
|  |     { | ||||||
|  |         int error; | ||||||
|  | 
 | ||||||
|  |         PyObject *axes, *item; | ||||||
|  |         Py_ssize_t i, num_coords; | ||||||
|  |         FT_Fixed *coords; | ||||||
|  |         FT_Fixed coord; | ||||||
|  |         if (!PyArg_ParseTuple(args, "O", &axes)) | ||||||
|  |             return NULL; | ||||||
|  | 
 | ||||||
|  |         if (!PyList_Check(axes)) { | ||||||
|  |             PyErr_SetString(PyExc_TypeError, "argument must be a list"); | ||||||
|  |             return NULL; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         num_coords = PyObject_Length(axes); | ||||||
|  |         coords = malloc(2 * sizeof(coords)); | ||||||
|  |         for (i = 0; i < num_coords; i++) { | ||||||
|  |             item = PyList_GET_ITEM(axes, i); | ||||||
|  |             if (PyFloat_Check(item)) | ||||||
|  |                 coord = PyFloat_AS_DOUBLE(item); | ||||||
|  |             else if (PyInt_Check(item)) | ||||||
|  |                 coord = (float) PyInt_AS_LONG(item); | ||||||
|  |             else if (PyNumber_Check(item)) | ||||||
|  |                 coord = PyFloat_AsDouble(item); | ||||||
|  |             else { | ||||||
|  |                 PyErr_SetString(PyExc_TypeError, "list must contain numbers"); | ||||||
|  |                 return NULL; | ||||||
|  |             } | ||||||
|  |             coords[i] = coord * 65536; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         error = FT_Set_Var_Design_Coordinates(self->face, num_coords, coords); | ||||||
|  |         if (error) | ||||||
|  |             return geterror(error); | ||||||
|  | 
 | ||||||
|  |         Py_INCREF(Py_None); | ||||||
|  |         return Py_None; | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| static void | static void | ||||||
| font_dealloc(FontObject* self) | font_dealloc(FontObject* self) | ||||||
| { | { | ||||||
|  | @ -892,6 +1046,14 @@ font_dealloc(FontObject* self) | ||||||
| static PyMethodDef font_methods[] = { | static PyMethodDef font_methods[] = { | ||||||
|     {"render", (PyCFunction) font_render, METH_VARARGS}, |     {"render", (PyCFunction) font_render, METH_VARARGS}, | ||||||
|     {"getsize", (PyCFunction) font_getsize, METH_VARARGS}, |     {"getsize", (PyCFunction) font_getsize, METH_VARARGS}, | ||||||
|  | #if FREETYPE_MAJOR > 2 ||\ | ||||||
|  |     (FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 9) ||\ | ||||||
|  |     (FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 9 && FREETYPE_PATCH == 1) | ||||||
|  |     {"getvarnames", (PyCFunction) font_getvarnames, METH_VARARGS }, | ||||||
|  |     {"getvaraxes", (PyCFunction) font_getvaraxes, METH_VARARGS }, | ||||||
|  |     {"setvarname", (PyCFunction) font_setvarname, METH_VARARGS}, | ||||||
|  |     {"setvaraxes", (PyCFunction) font_setvaraxes, METH_VARARGS}, | ||||||
|  | #endif | ||||||
|     {NULL, NULL} |     {NULL, NULL} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user