Codebase list appstream-generator / 8117980
Update upstream source from tag 'upstream/0.8.3' Update to upstream version '0.8.3' with Debian dir a89b55ef5a16b5204d2d54b8bdab05d28df9410c Matthias Klumpp 3 years ago
62 changed file(s) with 1266 addition(s) and 5200 deletion(s). Raw diff Collapse all Expand all
0 # See https://editorconfig.org/
1 root = true
2
3 [*]
4 end_of_line = lf
5 trim_trailing_whitespace = true
6 charset = utf-8
7 indent_style = space
8 indent_size = 4
9
10 [*.yml]
11 indent_style = space
12 indent_size = 2
13
14 [*.xml]
15 indent_style = space
16 indent_size = 2
0 name: Build Test
1
2 on: [push, pull_request]
3
4 jobs:
5 build-debian-testing:
6 name: Debian Testing
7 runs-on: ubuntu-latest
8 strategy:
9 matrix:
10 dc: [ldc, gdc]
11
12 steps:
13 - uses: actions/checkout@v2
14
15 - name: Create Build Environment
16 run: cd tests/ci/ && docker build -t asgen -f ./Dockerfile .
17
18 - name: Build & Test
19 continue-on-error: ${{ matrix.dc == 'gdc' }}
20 run: docker run -t -e DC=${{ matrix.dc }} -v `pwd`:/build asgen
21 ./tests/ci/build-and-test.sh
+0
-22
.travis.yml less more
0 # Travis CI config for the AppStream Generator
1 language: d
2 sudo: required
3 dist: trusty
4
5 services:
6 - docker
7
8 d:
9 - gdc
10 - ldc
11
12 matrix:
13 allow_failures:
14 - d: gdc
15
16 before_script:
17 - docker build -t asgen -f tests/ci/Dockerfile .
18
19 script:
20 - docker run -t -e DC=$DC -v `pwd`:/build asgen
21 ./tests/ci/build_and_test.sh
0 Version 0.8.3
1 ~~~~~~~~~~~~~~
2 Released: 2021-02-02
3
4 Notes:
5 * This release requires libappstream-compose from the AppStream project to build.
6 The library is still in progress and currently has an unstable API, but
7 is developed in sync with appstream-generator, so asgen can safely
8 depend on it.
9
10 Features:
11 * Elevate no-metainfo presence to a warning by default (Matthias Klumpp)
12 * Ignore all apps with only desktop-entry files using OnlyShowIn (Matthias Klumpp)
13 * Make use of the helper classes provided by appstream-compose (Matthias Klumpp)
14 * Add editorconfig (Matthias Klumpp)
15 * Use ascompose result type as base for the generator result container (Matthias Klumpp)
16 * Use the validator helper function from appstream-compose (Matthias Klumpp)
17 * Use metainfo parsing helper from appstream-compose (Matthias Klumpp)
18 * alpine: add capabilities to download packages via HTTP (Rasmus Thomsen)
19 * config: allow overriding export dir via --export-dir (Rasmus Thomsen)
20
21 Bugfixes:
22 * Add explicit option to disable network-dependent tests (Matthias Klumpp)
23 * Add missing CSS styling for permalinks to Debian template (Matthias Klumpp)
24 * Captialize "MetaInfo" the same everywhere in hint messages (Matthias Klumpp)
25 * Use binding generator to create the missing AppStream Utils functions (Matthias Klumpp)
26 * Never open contents cache DB env more than once in the same thread (Matthias Klumpp)
27 * Re-enable LMDB TLS support (Matthias Klumpp)
28 * downloader: Check read byte count before appending to buffer (Matthias Klumpp)
29 * Ensure export directory paths are sane, absolute paths all the time (Matthias Klumpp)
30
031 Version 0.8.2
132 ~~~~~~~~~~~~~~
233 Released: 2020-05-12
1010
1111
1212 ## Development
13 [![Build Status](https://travis-ci.org/ximion/appstream-generator.svg?branch=master)](https://travis-ci.org/ximion/appstream-generator)
13 ![Build Test](https://github.com/ximion/appstream-generator/workflows/Build%20Test/badge.svg)
1414
1515 ### Build dependencies
1616
2121 * libarchive (>= 3.2) [5]
2222 * LMDB [6]
2323 * GirToD [7]
24 * LibSoup
2425 * Cairo
2526 * GdkPixbuf 2.0
2627 * RSvg 2.0
11
22 1. Write NEWS entries for AppStream Generator in the same format as usual.
33
4 git shortlog v0.8.1.. | grep -i -v trivial | grep -v Merge > NEWS.new
4 git shortlog v0.8.2.. | grep -i -v trivial | grep -v Merge > NEWS.new
55
66 --------------------------------------------------------------------------------
7 Version 0.8.2
7 Version 0.8.3
88 ~~~~~~~~~~~~~~
99 Released: 2020-xx-xx
1010
1717
1818 2. Commit changes in Git:
1919
20 git commit -a -m "Release version 0.8.2"
21 git tag -s -f -m "Release 0.8.2" v0.8.2 <gpg password>
20 git commit -a -m "Release version 0.8.3"
21 git tag -s -f -m "Release 0.8.3" v0.8.3 <gpg password>
2222 git push --tags
2323 git push
2424
3232 5. Send an email to appstream@lists.freedesktop.org
3333
3434 =================================================
35 AppStream Generator 0.8.2 released!
35 AppStream Generator 0.8.3 released!
3636
3737 Tarballs available here: https://github.com/ximion/appstream-generator/releases
3838
4242
4343 srcDir: .
4444
45 lookup: APILookupGdkPixbuf.txt
4546 lookup: APILookupAppStream.txt
46 #lookup: APILookupSoup.txt
47 lookup: APILookupAppStreamCompose.txt
2525 public import core.stdc.stdio;
2626 public import core.stdc.string;
2727 addAliases: end
28
29 move: version_string Utils appstreamVersion
30
31 move: issue_severity_to_string Utils severityToString
32 move: issue_severity_from_string Utils severityFromString
33
34 move: utils_is_tld Utils isTld
35 move: utils_is_category_name Utils isCategoryName
36
37 move: format_version_to_string Utils
38 move: format_version_from_string Utils
39
40 move: license_is_metadata_license Utils
41 move: spdx_license_tokenize Utils
42
43 move: component_kind_to_string Utils
0 #
1 # Licensed under the GNU Lesser General Public License Version 3
2 #
3 # This library is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Lesser General Public License as published by
5 # the Free Software Foundation, either version 3 of the license, or
6 # (at your option) any later version.
7 #
8 # This software is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU Lesser General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public License
14 # along with this library. If not, see <http://www.gnu.org/licenses/>.
15
16 # must start with wrap
17 wrap: ascompose
18 file: AppStreamCompose-1.0.gir
19
20 addAliases: start
21 public import glib.c.types;
22 public import gobject.c.types;
23 public import gio.c.types;
24 public import appstream.c.types;
25 public import gdkpixbuf.c.types;
26
27 public import core.stdc.stdio;
28 public import core.stdc.string;
29 addAliases: end
30
31 move: image_format_from_filename Utils
32
33 struct: Globals
34 move: globals_get_tmp_dir Globals getTmpDir
35 move: globals_get_tmp_dir_create Globals getTmpDirCreate
36 move: globals_set_tmp_dir Globals setTmpDir
37 move: globals_get_use_optipng Globals useOptipng
38 move: globals_set_use_optipng Globals setUseOptipng
39 move: globals_get_optipng_binary Globals optipngBinary
40 move: globals_set_optipng_binary Globals setOptipngBinary
41 move: globals_add_hint_tag Globals addHintTag
42 move: globals_get_hint_tags Globals getHintTags
43 move: globals_hint_tag_severity Globals hintTagSeverity
44 move: globals_hint_tag_explanation Globals hintTagExplanation
45
46 struct: Result
47 class: Result
48 move: add_hint_v Result addHint
49 move: add_hint_by_cid_v Result addHintByCid
50
51 struct: MetaInfoUtils
52 move: parse_metainfo_data MetaInfoUtils
53 move: parse_metainfo_data_simple MetaInfoUtils
54 move: validate_metainfo_data_for_component MetaInfoUtils
0 # This file is part of gtkD.
1 #
2 # gtkD is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU Lesser General Public License as published by
4 # the Free Software Foundation; either version 2.1 of the License, or
5 # (at your option) any later version.
6 #
7 # gtkD is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU Lesser General Public License for more details.
11 #
12 # You should have received a copy of the GNU Lesser General Public License
13 # along with gtkD; if not, write to the Free Software
14 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15 #
16
17 #############################################
18 ### Definitions for wrapping Gtk+ ###########
19 #############################################
20
21 # must start with wrap
22 wrap: gdkpixbuf
23 file: GdkPixbuf-2.0.gir
24 file: GdkPixdata-2.0.gir
25
26 struct: Pixbuf
27 array: new_from_inline data data_length
28 noCode: save_to_bufferv
29 noCode: ref
30 noCode: unref
31 noCode: new_from_resource
32 noCode: new_from_resource_at_scale
33 noCode: get_pixels
34
35 code: start
36 /**
37 * Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
38 * "tiff", "png", "ico" or "bmp". See gdk_pixbuf_save_to_buffer()
39 * for more details.
40 *
41 * Params:
42 * buffer = location to receive a pointer to the new buffer.
43 * bufferSize = location to receive the size of the new buffer.
44 * type = name of file format.
45 * optionKeys = name of options to set, %NULL-terminated
46 * optionValues = values for named options
47 *
48 * Return: whether an error was set
49 *
50 * Since: 2.4
51 *
52 * Throws: GException on failure.
53 */
54 public bool saveToBuffer(out ubyte[] buffer, string type, string[] optionKeys, string[] optionValues)
55 {
56 char* outbuffer = null;
57 size_t bufferSize;
58 GError* err = null;
59
60 auto p = gdk_pixbuf_save_to_bufferv(gdkPixbuf, &outbuffer, &bufferSize, Str.toStringz(type), Str.toStringzArray(optionKeys), Str.toStringzArray(optionValues), &err) != 0;
61
62 if (err !is null)
63 {
64 throw new GException( new ErrorG(err) );
65 }
66
67 buffer = (cast(ubyte*)outbuffer)[0 .. bufferSize];
68
69 return p;
70 }
71
72 /**
73 * Creates a new pixbuf by loading an image from an resource.
74 *
75 * The file format is detected automatically.
76 *
77 * Params:
78 * resourcePath = the path of the resource file
79 *
80 * Return: A newly-created pixbuf, or null if any of several error
81 * conditions occurred: the file could not be opened, the image format is
82 * not supported, there was not enough memory to allocate the image buffer,
83 * the stream contained invalid data, or the operation was cancelled.
84 *
85 * Since: 2.26
86 *
87 * Throws: GException on failure.
88 */
89 public static Pixbuf newFromResource(string resourcePath)
90 {
91 GError* err = null;
92
93 auto p = gdk_pixbuf_new_from_resource(Str.toStringz(resourcePath), &err);
94
95 if (err !is null)
96 {
97 throw new GException( new ErrorG(err) );
98 }
99
100 return new Pixbuf(cast(GdkPixbuf*) p, true);
101 }
102
103 /**
104 * Creates a new pixbuf by loading an image from an resource.
105 *
106 * The file format is detected automatically.
107 *
108 * The image will be scaled to fit in the requested size, optionally
109 * preserving the image's aspect ratio. When preserving the aspect ratio,
110 * a width of -1 will cause the image to be scaled to the exact given
111 * height, and a height of -1 will cause the image to be scaled to the
112 * exact given width. When not preserving aspect ratio, a width or
113 * height of -1 means to not scale the image at all in that dimension.
114 *
115 * The stream is not closed.
116 *
117 * Params:
118 * resourcePath = the path of the resource file
119 * width = The width the image should have or -1 to not constrain the width
120 * height = The height the image should have or -1 to not constrain the height
121 * preserveAspectRatio = true to preserve the image's aspect ratio
122 *
123 * Return: A newly-created pixbuf, or null if any of several error
124 * conditions occurred: the file could not be opened, the image format is
125 * not supported, there was not enough memory to allocate the image buffer,
126 * the stream contained invalid data, or the operation was cancelled.
127 *
128 * Since: 2.26
129 *
130 * Throws: GException on failure.
131 */
132 public static Pixbuf newFromResource(string resourcePath, int width, int height, bool preserveAspectRatio)
133 {
134 GError* err = null;
135
136 auto p = gdk_pixbuf_new_from_resource_at_scale(Str.toStringz(resourcePath), width, height, preserveAspectRatio, &err);
137
138 if (err !is null)
139 {
140 throw new GException( new ErrorG(err) );
141 }
142
143 return new Pixbuf(cast(GdkPixbuf*) p, true);
144 }
145
146 /**
147 * Queries a pointer to the pixel data of a pixbuf.
148 *
149 * Return: A pointer to the pixbuf's pixel data.
150 * Please see the section on [image data](image-data) for information
151 * about how the pixel data is stored in memory.
152 *
153 * This function will cause an implicit copy of the pixbuf data if the
154 * pixbuf was created from read-only data.
155 */
156 public char* getPixels()
157 {
158 return gdk_pixbuf_get_pixels(gdkPixbuf);
159 }
160 code: end
161
162 struct: PixbufAnimation
163 noCode: new_from_resource
164
165 struct: PixbufLoader
166 import: glib.Str
167 noCode: new_with_type
168 noCode: new_with_mime_type
169 code: start
170 /**
171 * Creates a new pixbuf loader object that always attempts to parse
172 * image data as if it were an image of type @image_type, instead of
173 * identifying the type automatically. Useful if you want an error if
174 * the image isn't the expected type, for loading image formats
175 * that can't be reliably identified by looking at the data, or if
176 * the user manually forces a specific type.
177 *
178 * The list of supported image formats depends on what image loaders
179 * are installed, but typically "png", "jpeg", "gif", "tiff" and
180 * "xpm" are among the supported formats. To obtain the full list of
181 * supported image formats, call gdk_pixbuf_format_get_name() on each
182 * of the #GdkPixbufFormat structs returned by gdk_pixbuf_get_formats().
183 *
184 * Params:
185 * imageType = name of the image format to be loaded with the image
186 * isMimeType = Set to true if type is a mime type
187 *
188 * Return: A newly-created pixbuf loader.
189 *
190 * Throws: GException on failure.
191 */
192 public this(string type, bool isMimeType=false)
193 {
194 GError* err = null;
195 GdkPixbufLoader* p;
196
197 if ( isMimeType )
198 {
199 p = cast(GdkPixbufLoader*)gdk_pixbuf_loader_new_with_mime_type(Str.toStringz(type), &err);
200 }
201 else
202 {
203 p = cast(GdkPixbufLoader*)gdk_pixbuf_loader_new_with_type(Str.toStringz(type), &err);
204 }
205
206 if (err !is null)
207 {
208 throw new GException( new ErrorG(err) );
209 }
210
211 this(cast(GdkPixbufLoader*) p, true);
212 }
213 code: end
214
215 struct: PixbufSimpleAnim
216 class: PixbufSimpleAnimation
217
218 struct: PixbufSimpleAnimIter
219 class: PixbufSimpleAnimationIter
220 noCode: true
221
222 struct: Pixdata
223 noProperty: pixel_data
224
225 move: pixbuf_from_pixdata Pixbuf from_pixdata
+0
-28
contrib/girwrap/APILookupSoup.txt less more
0 #
1 # Licensed under the GNU Lesser General Public License Version 3
2 #
3 # This library is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Lesser General Public License as published by
5 # the Free Software Foundation, either version 3 of the license, or
6 # (at your option) any later version.
7 #
8 # This software is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU Lesser General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public License
14 # along with this library. If not, see <http://www.gnu.org/licenses/>.
15
16 # must start with wrap
17 wrap: soup
18 file: Soup-2.4.gir
19
20 addAliases: start
21 public import glib.c.types;
22 public import gobject.c.types;
23
24 public import core.stdc.stdio;
25 public import core.stdc.string;
26 public import std.socket : sockaddr;
27 addAliases: end
3131 },
3232
3333 "metainfo-parsing-error": {
34 "text": "Unable to parse AppStream metainfo file '{{fname}}', the data is likely malformed.<br/>Error: {{error}}",
34 "text": "Unable to parse AppStream MetaInfo file '{{fname}}', the data is likely malformed.<br/>Error: {{error}}",
3535 "severity": "error"
3636 },
3737
6262 },
6363
6464 "web-app-without-icon": {
65 "text": [ "The component is a GUI web application, but it either has no icon set in its metainfo file,",
65 "text": [ "The component is a GUI web application, but it either has no icon set in its MetaInfo file,",
6666 "or we could not find a matching icon for this application."
6767 ],
6868 "severity": "error"
7070
7171 "font-without-icon": {
7272 "text": [ "The component is a font, but somehow we failed to automatically generate an icon for it, and no custom icon was set explicitly.",
73 "Is there a font file in the analyzed package, and does the metainfo file set the right font name to look for?"
73 "Is there a font file in the analyzed package, and does the MetaInfo file set the right font name to look for?"
7474 ],
7575 "severity": "warning"
7676 },
109109 "severity": "error"
110110 },
111111
112 "metainfo-no-id": {
113 "text": ["Could not determine an ID for the component in '{{fname}}'. The AppStream metainfo file likely lacks an <code>&lt;id/&gt;</code> tag.<br/>",
114 "The identifier tag is essential for AppStream metadata, and must not be missing."],
115 "severity": "error"
116 },
117
118112 "metainfo-validation-error": {
119 "text": "Validation of the metainfo file failed: {{msg}}",
120 "severity": "warning"
121 },
122
123 "ancient-metadata": {
124 "text": ["The AppStream metadata should be updated to follow a more recent version of the specification.<br/>",
125 "Please consult <a href=\"http://freedesktop.org/software/appstream/docs/chap-Quickstart.html\">the XML quickstart guide</a> for more information."],
113 "text": "Validation of the MetaInfo file failed: {{msg}}",
126114 "severity": "warning"
127115 },
128116
129117 "legacy-metainfo-directory": {
130 "text": ["The AppStream metainfo file '{{fname}}' was found in a legacy path.<br/>",
131 "Please install metainfo files into <code>/usr/share/metainfo</code>, as the old path will not be recognized anymore in the future."],
118 "text": ["The AppStream MetaInfo file '{{fname}}' was found in a legacy path.<br/>",
119 "Please install MetaInfo files into <code>/usr/share/metainfo</code>, as the old path will not be recognized anymore in the future."],
132120 "severity": "warning"
133121 },
134122
164152 "severity": "info"
165153 },
166154
167 "metainfo-unknown-type": {
168 "text": ["The component has an unknown type. Please make sure this component type is mentioned in the specification, and that the",
169 "<code>type=</code> property of the component root-node of the metainfo file does not contain a typing error."],
170 "severity": "error"
171 },
172
173155 "metainfo-no-name": {
174 "text": "Component has no name specified. Ensure that the AppStream metainfo file or the .desktop file (if there is any) specify a component name.",
156 "text": "Component has no name specified. Ensure that the AppStream MetaInfo file or the .desktop file (if there is any) specify a component name.",
175157 "severity": "error"
176158 },
177159
178160 "metainfo-no-summary": {
179 "text": ["Component does not contain a short summary. Ensure that the components metainfo file has a <code>summary</code> tag, or that its .desktop file",
161 "text": ["Component does not contain a short summary. Ensure that the components MetaInfo file has a <code>summary</code> tag, or that its .desktop file",
180162 "has a <code>Comment=</code> field set.<br/>",
181163 "More information can be found in the <a href=\"http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s05.html\">Desktop Entry specification</a>",
182164 "and the <a href=\"https://www.freedesktop.org/software/appstream/docs/sect-Metadata-Application.html#tag-dapp-summary\">MetaInfo specification</a>."],
191173 },
192174
193175 "missing-desktop-file": {
194 "text": ["Found an AppStream metainfo XML file, but the associated .desktop file is missing. This often happens when the .desktop file is renamed, but the",
195 "<code>&lt;launchable type=\"desktop-id\"/&gt;</code> tag value of the AppStream metainfo file is not adapted as well, or if the metainfo file is located in a different package than the .desktop file.<br/>",
176 "text": ["Found an AppStream MetaInfo XML file, but the associated .desktop file is missing. This often happens when the .desktop file is renamed, but the",
177 "<code>&lt;launchable type=\"desktop-id\"/&gt;</code> tag value of the AppStream MetaInfo file is not adapted as well, or if the MetaInfo file is located in a different package than the .desktop file.<br/>",
196178 "Please fix the packaging or work with upstream to resolve this issue.<br/>",
197179 "<small>For older metadata, the desktop-id is inferred from the <code>&lt;id/&gt;</code> tag. If the component metadata has no <code>launchable</code> tag and no",
198180 "<code>icon</code> tag of type <i>stock</i>, check if a .desktop file named after the component-ID is located in the same package.</small>"],
200182 },
201183
202184 "missing-launchable-desktop-file": {
203 "text": ["The metainfo file references a .desktop file with ID '{{desktop_id}}' in its <code>launchable</code> tag, but the file",
185 "text": ["The MetaInfo file references a .desktop file with ID '{{desktop_id}}' in its <code>launchable</code> tag, but the file",
204186 "was not found in the same package. In order to be able to launch the software once it was installed, please place the",
205 "metainfo file and its .desktop files in the same package."],
187 "MetaInfo file and its .desktop files in the same package."],
206188 "severity": "warning"
207189 },
208190
209191 "description-from-package": {
210 "text": ["This component gets its description from the package it is located in.<br/>",
192 "text": ["This software component gets its description from the package it is located in.<br/>",
211193 "This has several disadvantages, like poor markup, too technical descriptions for users of software centers, different components having the same description, etc.<br/>",
212 "Please consider writing a metainfo file for this component to take the long description upstream. In future, components without metainfo file might be dropped from the metadata entirely.",
213 "You can consult the <a href=\"http://freedesktop.org/software/appstream/docs/chap-Quickstart.html\">XML quickstart guides</a> for more information on how to write a metainfo file."],
214 "severity": "info"
194 "Please consider to either hide this .desktop file from AppStream by adding a <code>X-AppStream-Ignore=true</code> field to its .desktop file, or to write a MetaInfo file for ",
195 "this component to take the long description upstream. In future, components without MetaInfo file might be dropped from the metadata entirely.",
196 "You can consult the <a href=\"http://freedesktop.org/software/appstream/docs/chap-Quickstart.html\">XML quickstart guides</a> for more information on how to write a MetaInfo file."],
197 "severity": "info"
198 },
199
200 "no-metainfo": {
201 "text": ["This software component is missing a <a href=\"https://freedesktop.org/software/appstream/docs/chap-Metadata.html#sect-Metadata-GenericComponent\">MetaInfo file</a> to provide metadata about it.<br/>",
202 "We currently took some data from its .desktop file and the long description of the package it is located in.<br/>",
203 "This has several disadvantages, like poor markup, too technical descriptions for users of software centers, different components having the same description, etc.<br/>",
204 "Additionally, a lot of software from .desktop files should either not be installable and searchable via the software catalog (like desktop-specific settings applications) or be properly tagged via MetaInfo files.<br/>",
205 "Please consider to either hide this .desktop file from AppStream by adding a <code>X-AppStream-Ignore=true</code> field to its .desktop file, or to write a MetaInfo file for this component and send it upstream.<br/>",
206 "Generating components from non-MetaInfo files is <b>deprecated</b>, if you do not add a MetaInfo file, this software will vanish from the metadata catalog (and if it is a GUI application, no longer be visible in software centers).<br>",
207 "You can consult the <a href=\"http://freedesktop.org/software/appstream/docs/chap-Quickstart.html\">MetaInfo quickstart guides</a> for more information on how to write a MetaInfo file, or file a bug upstream."],
208 "severity": "warning"
215209 },
216210
217211 "description-missing": {
218 "text": ["Software components of type '{{kind}}' require a long description, and we were unable to find one. Please please add one via a metainfo file."],
212 "text": ["Software components of type '{{kind}}' require a long description, and we were unable to find one. Please please add one via a MetaInfo file."],
219213 "severity": "error"
220214 },
221215
235229 "severity": "error"
236230 },
237231
238 "metainfo-license-invalid": {
239 "text": ["The metainfo file does not seem to be licensed under a permissive license. Valid permissive licenses include FSFAP, CC0-1.0 or MIT.",
240 "Permissive licenses are required to allow distributors to include the metadata in mixed data collections without the risk of license violations due to mutually incompatible licenses.",
241 "If you think this message is an error and '{{license}}' is actually valid, please <a href=\"https://github.com/ximion/appstream/issues\">file a bug against AppStream</a>."],
242 "severity": "error"
243 },
244
245232 "font-metainfo-but-no-font": {
246 "text": ["A metainfo file with component-type <code>font</code> was found, but we could not find any matching font file (TrueType or OpenType) in the package.<br/>",
233 "text": ["A MetaInfo file with component-type <code>font</code> was found, but we could not find any matching font file (TrueType or OpenType) in the package.<br/>",
247234 "This can mean that the <code>&lt;provides&gt; - &lt;font&gt;</code> tags contain wrong values that we could not map to the actual fonts, or that the package simply contained no fonts at all.<br/>",
248235 "Fonts in this package: <em>{{font_names}}</em>"],
249236 "severity": "error"
271258
272259 "no-translation-statistics": {
273260 "text": ["We could not extract any translation statitics, althout metadata hints were given that the data exists.",
274 "Please check the &lt;translation/&gt; metainfo tag for spelling mistakes.",
261 "Please check the &lt;translation/&gt; MetaInfo tag for spelling mistakes.",
275262 "It also makes sense to ensure all locale files are placed in the right directories (e.g. gettext .mo files in <code>/usr/share/locale/*/LC_MESSAGES/</code>"],
276263 "severity": "warning"
277264 },
+0
-24
data/pangrams/en.txt less more
0 A quick brown fox jumps over the lazy dog.
1 Whenever the black fox jumped the squirrel gazed suspiciously.
2 The five boxing wizards jump quickly.
3 A large fawn jumped quickly over white zebras in a box.
4 We quietly gave Bert a handsome prize for his six juicy pink plums.
5 Crazy Fredrick bought many very exquisite opal jewels.
6 Five or six big jet planes zoomed quickly past the tower.
7 My grandfather picks up quartz and valuable onyx jewels.
8 Sphinx of black quartz, judge my vow.
9 Two driven jocks help fax my big quiz.
10 Five quacking zephyrs jolt my wax bed.
11 Pack my box with five dozen liquor jugs.
12 Jinxed wizards pluck ivy from the big quilt.
13 We promptly judged antique ivory buckles for the next prize.
14 A mad boxer shot a quick, gloved jab to the jaw of his dizzy opponent.
15 Jaded zombies acted quaintly but kept driving their oxen forward.
16 The job requires extra pluck and zeal from every young wage earner.
17 Jived fox nymph grabs quick waltz.
18 How vexingly quick daft zebras jump!
19 Amazingly few discotheques provide jukeboxes.
20 The quick onyx goblin jumps over the lazy dwarf.
21 Six big devils from Japan quickly forgot how to waltz.
22 Jack amazed a few girls by dropping the antique onyx vase.
23 A quick movement of the enemy will jeopardize six gunboats.
1717 a {
1818 color: #337ab7;
1919 text-decoration: none;
20 }
21
22 a {
2320 background-color: transparent;
2421 }
2522
235232 position: absolute;
236233 width: 1px;
237234 }
235
236 .permalink {
237 font-size: 75%;
238 color: #999;
239 line-height: 100%;
240 font-weight: normal;
241 text-decoration: none;
242 }
1717 a {
1818 color: #337ab7;
1919 text-decoration: none;
20 }
21
22 a {
2320 background-color: transparent;
2421 }
2522
9595 processGStreamer | Synthesise `type=codec` metadata from available GStreamer packages. Requires support in the backend, currently only implemented for Debian. *Default: `ON`*
9696 processLocale | Try to extract the software's localization status from Gettext data. *Default: `ON`*
9797 screenshotVideos | Permit videos in screenshots and cache them if downloads are permitted. *Default: `ON`*
98 propagateMetainfoArtifacts | Release artifact information is filtered out by default if a package is set for the selected metadata. Set this flag to propagate artifact information unconditionally. *Default: `OFF`*
98 propagateMetaInfoArtifacts | Release artifact information is filtered out by default if a package is set for the selected metadata. Set this flag to propagate artifact information unconditionally. *Default: `OFF`*
99 warnNoMetainfo | Make the fact that a metainfo file is missing a deprecation warning. *Default: `ON`*
99100
100101 ### Configuring icon policies
101102
11 meson_version : '>=0.46',
22 subproject_dir : 'contrib/subprojects',
33 license : 'LGPL-3.0+',
4 version : '0.8.2'
4 version : '0.8.3'
55 )
66
77 asgen_version = meson.project_version()
1515 src_dir = include_directories('src/')
1616
1717 glibd_dep = dependency('glibd-2.0')
18 appstream_dep = dependency('appstream', version : '>= 0.12.10')
19 lmdb_dep = dependency('lmdb', version : '>= 0.9')
18 appstream_dep = dependency('appstream', version : '>= 0.14.0')
19 ascompose_dep = dependency('appstream-compose', version : '>= 0.14.0')
20 lmdb_dep = dependency('lmdb', version : '>= 0.9.22')
2021 archive_dep = dependency('libarchive', version : '>= 3.2')
2122 soup_dep = dependency('libsoup-2.4', version: '>= 2.56')
22 cairo_dep = dependency('cairo', version : '>= 1.12')
23 gdkpixbuf_dep = dependency('gdk-pixbuf-2.0')
24 rsvg_dep = dependency('librsvg-2.0')
25 freetype_dep = dependency('freetype2')
26 pango_dep = dependency('pango')
27 fontconfig_dep = dependency('fontconfig')
2823
2924 #
3025 # Build interfaces from GIR
1919
2020 name: appstream-generator
2121 license: LGPL-3.0
22 base: core18
22 base: core20
2323 adopt-info: appstream-generator
2424
2525 confinement: strict
7171
7272 # adjust to an absolute path to help finding the GIR file from the AppStream part
7373 sed -i 's|AppStream-1.0.gir|$SNAPCRAFT_STAGE/usr/share/gir-1.0/AppStream-1.0.gir|g' contrib/girwrap/APILookupAppStream.txt
74 sed -i 's|AppStreamCompose-1.0.gir|$SNAPCRAFT_STAGE/usr/share/gir-1.0/AppStreamCompose-1.0.gir|g' contrib/girwrap/APILookupAppStreamCompose.txt
7475
7576 # actually build asgen - we need to run everything manually here,
7677 # because snapcraft will kill the build if run with maximum amount of ninja jobs,
7879 meson --prefix=/usr --buildtype=debugoptimized -Ddownload-js=true snapbuild
7980 cd snapbuild
8081 ninja -j4
81 ninja test
82 #meson test --verbose
8283 DESTDIR=$SNAPCRAFT_PART_INSTALL ninja install
8384 override-prime: |
8485 set -eux
157158 appstream:
158159 source: https://github.com/ximion/appstream.git
159160 source-type: git
160 source-tag: v0.12.11
161 source-branch: master
161162
162163 plugin: meson
163164 meson-parameters:
164165 - --prefix=/usr
165166 - --buildtype=release
167 - -Dcompose=true
166168 - -Dapidocs=false
167 override-build: |
168 # we can build with a slightly lower GLib version in this instance
169 sed -i 's|>=2.58|>=2.56|g' meson.build
170
171 # build AS
172 snapcraftctl build
173169 build-packages:
174170 - build-essential
175171 - docbook-xsl
187183 - meson
188184 - xsltproc
189185 stage-packages:
190 - libicu60
186 - libicu66
191187 - liblmdb0
192188 - libsoup2.4-1
193189 - libstemmer0d
196192 gir-to-d:
197193 source: https://github.com/gtkd-developers/gir-to-d.git
198194 source-type: git
199 source-tag: v0.21.0
195 source-tag: v0.22.0
200196
201197 plugin: meson
202198 meson-parameters:
227223
228224 ldc:
229225 plugin: dump
230 source: https://github.com/ldc-developers/ldc/releases/download/v1.21.0/ldc2-1.21.0-linux-x86_64.tar.xz
226 source: https://github.com/ldc-developers/ldc/releases/download/v1.24.0/ldc2-1.24.0-linux-x86_64.tar.xz
231227
232228 override-build: |
233229 # link shared by default
00 /*
1 * Copyright (C) 2016-2017 Matthias Klumpp <matthias@tenstral.net>
1 * Copyright (C) 2016-2021 Matthias Klumpp <matthias@tenstral.net>
22 *
33 * Licensed under the GNU Lesser General Public License Version 3
44 *
5252 --verbose Show extra debugging information.
5353 --force Force action.
5454 -w|--workspace Define the workspace location.
55 -c|--config Use the given configuration file.";
55 -c|--config Use the given configuration file.
56 --export-dir Override the workspace root export directory.";
5657
5758 version (unittest) {
5859 void main () {}
9495 bool showVersion;
9596 bool forceAction;
9697 string wdir;
98 string exportDir;
9799 string configFname;
98100
99101 // parse command-line options
104106 "version", &showVersion,
105107 "force", &forceAction,
106108 "workspace|w", &wdir,
107 "config|c", &configFname);
109 "config|c", &configFname,
110 "export-dir", &exportDir);
108111 } catch (Exception e) {
109112 writeln ("Unable to parse parameters: ", e.msg);
110113 exit (1);
139142 }
140143
141144 try {
142 conf.loadFromFile (configFname, wdir);
145 conf.loadFromFile (configFname, wdir, exportDir);
143146 } catch (Exception e) {
144147 writefln ("Unable to load configuration: %s", e.msg);
145148 exit (4);
0 module asgen.backends.alpinelinux.apkindexutils;
1
2 import std.algorithm : remove;
3 import std.algorithm.iteration : map;
4 import std.algorithm.searching : canFind;
5 import std.array : join, split;
6 import std.file : exists;
7 import std.format : format;
8 import std.path : buildPath;
9 import std.range : empty, InputRange, isForwardRange;
10 import std.string : splitLines, startsWith, strip;
11 import std.utf : validate;
12
13 import std.stdio;
14
15 import asgen.backends.alpinelinux.apkpkg;
16 import asgen.downloader : Downloader, DownloadException;
17 import asgen.logging : logDebug;
18 import asgen.utils : isRemote;
19
20 /**
21 * Struct representing a block inside of an APKINDEX. Each block, seperated by
22 * a newline, contains information about exactly one package.
23 */
24 struct ApkIndexBlock {
25 string arch;
26 string maintainer;
27 string pkgname;
28 string pkgversion;
29 string pkgdesc;
30
31 @property string archiveName () {
32 return format ("%s-%s.apk", this.pkgname, this.pkgversion);
33 }
34 }
35
36 /**
37 * Range for looping over the contents of an APKINDEX, block by block.
38 */
39 struct ApkIndexBlockRange {
40 this (string contents)
41 {
42 this.lines = contents.splitLines;
43 this.getNextBlock();
44 }
45
46 @property ApkIndexBlock front () const {
47 return this.currentBlock;
48 }
49
50 @property bool empty () {
51 return this.m_empty;
52 }
53
54 void popFront ()
55 {
56 this.getNextBlock ();
57 }
58
59 @property ApkIndexBlockRange save () { return this; }
60
61 private:
62 void getNextBlock () {
63 string[] completePair;
64 uint iterations = 0;
65
66 currentBlock = ApkIndexBlock();
67 foreach (currentLine; this.lines[this.lineDelta .. $]) {
68 iterations++;
69 if (currentLine == "") {
70 // next block for next package started
71 break;
72 } if (currentLine.canFind (":")) {
73 if (completePair.empty) {
74 completePair = [currentLine];
75 continue;
76 }
77
78 auto pair = completePair.join (" ").split (":");
79 this.setCurrentBlock (pair[0], pair[1]);
80 completePair = [currentLine];
81 } else {
82 completePair ~= currentLine.strip ();
83 }
84 }
85
86 this.lineDelta += iterations;
87 this.m_empty = this.lineDelta == this.lines.length;
88 }
89
90 void setCurrentBlock (string key, string value) {
91 switch (key) {
92 case "A":
93 this.currentBlock.arch = value;
94 break;
95 case "m":
96 this.currentBlock.maintainer = value;
97 break;
98 case "P":
99 this.currentBlock.pkgname = value;
100 break;
101 case "T":
102 this.currentBlock.pkgdesc = value;
103 break;
104 case "V":
105 this.currentBlock.pkgversion = value;
106 break;
107 default:
108 // We dont care about other keys
109 break;
110 }
111 }
112
113 string[] lines;
114 ApkIndexBlock currentBlock;
115 bool m_empty;
116 uint lineDelta;
117 }
118
119 static assert (isForwardRange!ApkIndexBlockRange);
120
121 /**
122 * Download apkindex if required. Returns the path to the local copy of the APKINDEX.
123 */
124 immutable (string) downloadIfNecessary (const string prefix,
125 const string destPrefix,
126 const string srcFileName,
127 const string destFileName)
128 {
129 auto downloader = Downloader.get;
130
131 immutable filePath = buildPath (prefix, srcFileName);
132 immutable destFilePath = buildPath (destPrefix, destFileName);
133
134 if (filePath.isRemote) {
135 try {
136 downloader.downloadFile (filePath, destFilePath);
137
138 return destFilePath;
139 } catch (DownloadException e) {
140 logDebug ("Unable to download: %s", e.msg);
141 }
142 } else {
143 if (filePath.exists)
144 return filePath;
145 }
146
147 /* all extensions failed, so we failed */
148 throw new Exception ("Could not obtain any file matching %s".format (buildPath (prefix, srcFileName)));
149 }
2121
2222 module asgen.backends.alpinelinux.apkpkg;
2323
24 import std.stdio;
25 import std.string;
2624 import std.array : empty;
25 import std.format : format;
26 import std.path : baseName, buildNormalizedPath, buildPath;
2727
28 import asgen.logging;
29 import asgen.zarchive;
3028 import asgen.backends.interfaces;
29 import asgen.config : Config;
30 import asgen.downloader : Downloader;
31 import asgen.utils : isRemote;
32 import asgen.zarchive : ArchiveDecompressor;
3133
3234 final class AlpinePackage : Package
3335 {
3840 string pkgmaintainer;
3941 string[string] desc;
4042 string pkgFname;
43 string localPkgFName;
44 string tmpDir;
4145
42 string[] contentsL;
46 string[] contentsL = null;
4347
4448 ArchiveDecompressor archive;
4549
4650 public:
51 this (string pkgname, string pkgver, string pkgarch)
52 {
53 this.pkgname = pkgname;
54 this.pkgver = pkgver;
55 this.pkgarch = pkgarch;
56
57 auto conf = Config.get ();
58 this.tmpDir = buildPath (conf.getTmpDir (), format ("%s-%s_%s", name, ver, arch));
59 }
60
4761 override @property string name () const
4862 {
4963 return this.pkgname;
8498 this.pkgFname = fname;
8599 }
86100
87 override @property string getFilename () const
101 override @property string getFilename ()
88102 {
89 return pkgFname;
103 if (!this.localPkgFName.empty)
104 return this.localPkgFName;
105
106 if (pkgFname.isRemote) {
107 synchronized (this) {
108 auto dl = Downloader.get;
109 immutable path = buildNormalizedPath (this.tmpDir, this.pkgFname.baseName);
110 dl.downloadFile (this.pkgFname, path);
111 this.localPkgFName = path;
112 return this.localPkgFName;
113 }
114 } else {
115 this.localPkgFName = pkgFname;
116 return this.localPkgFName;
117 }
90118 }
91119
92120 override @property string maintainer () const
114142
115143 @property override string[] contents ()
116144 {
145 if (!this.contentsL.empty)
146 return this.contentsL;
147
148 ArchiveDecompressor ad;
149 ad.open (this.getFilename);
150 this.contentsL = ad.readContents ();
151
117152 return this.contentsL;
118153 }
119154
1818
1919 module asgen.backends.alpinelinux.apkpkgindex;
2020
21 import std.algorithm : canFind, filter, endsWith, remove;
22 import std.array : appender, join, split;
21 import std.array : appender;
2322 import std.conv : to;
2423 import std.exception : enforce;
25 import std.file : dirEntries, exists, SpanMode;
24 import std.file : exists;
2625 import std.format : format;
2726 import std.path : baseName, buildPath;
28 import std.range : empty;
29 import std.string : splitLines, startsWith, strip;
30 import std.utf : UTFException, validate;
27 import std.utf : validate;
3128
32 import asgen.logging;
33 import asgen.zarchive;
34 import asgen.utils : escapeXml;
29 import asgen.config : Config;
30 import asgen.logging : logError;
31 import asgen.zarchive : ArchiveDecompressor;
32 import asgen.utils : escapeXml, isRemote;
3533 import asgen.backends.interfaces;
3634 import asgen.backends.alpinelinux.apkpkg;
35 import asgen.backends.alpinelinux.apkindexutils;
3736
3837 final class AlpinePackageIndex : PackageIndex
3938 {
4039
4140 private:
4241 string rootDir;
42 string tmpDir;
4343 Package[][string] pkgCache;
4444
4545 public:
4646
4747 this (string dir)
4848 {
49 enforce (exists (dir), format ("Directory '%s' does not exist.", dir));
49 if (!dir.isRemote)
50 enforce (exists (dir), format ("Directory '%s' does not exist.", dir));
51
5052 this.rootDir = dir;
53
54 auto conf = Config.get ();
55 tmpDir = buildPath (conf.getTmpDir, dir.baseName);
5156 }
5257
5358 override void release ()
6469 pkg.setDescription (desc, "C");
6570 }
6671
67 private void setPkgValues (ref AlpinePackage pkg, string[] keyValueString)
68 {
69 immutable key = keyValueString[0].strip;
70 immutable value = keyValueString[1].strip;
71
72 switch (key) {
73 case "pkgname":
74 pkg.name = value;
75 break;
76 case "pkgver":
77 pkg.ver = value;
78 break;
79 case "arch":
80 pkg.arch = value;
81 break;
82 case "maintainer":
83 pkg.maintainer = value;
84 break;
85 case "pkgdesc":
86 setPkgDescription(pkg, value);
87 break;
88 default:
89 // We don't care about other entries
90 break;
91 }
92 }
93
9472 private Package[] loadPackages (string suite, string section, string arch)
9573 {
9674 auto apkRootPath = buildPath (rootDir, suite, section, arch);
75 auto indexFPath = downloadIfNecessary(apkRootPath, tmpDir, "APKINDEX.tar.gz", format("APKINDEX-%s-%s-%s.tar.gz", suite, section, arch));
76 AlpinePackage[string] pkgsMap;
9777 ArchiveDecompressor ad;
98 AlpinePackage[string] pkgsMap;
78 ad.open (indexFPath);
79 auto indexString = cast(string) ad.readData ("APKINDEX");
80 validate (indexString);
81 auto range = ApkIndexBlockRange (indexString);
9982
100 foreach (packageArchivePath; dirEntries (apkRootPath, SpanMode.shallow).filter!(
101 f => f.name.endsWith (".apk"))) {
102 auto fileName = packageArchivePath.baseName ();
83 foreach (pkgInfo; range) {
84 auto fileName = pkgInfo.archiveName;
10385 AlpinePackage pkg;
10486 if (fileName in pkgsMap) {
10587 pkg = pkgsMap[fileName];
10688 } else {
107 pkg = new AlpinePackage ();
89 pkg = new AlpinePackage (pkgInfo.pkgname, pkgInfo.pkgversion, pkgInfo.arch);
10890 pkgsMap[fileName] = pkg;
10991 }
11092
111 ad.open (packageArchivePath);
112 auto pkgInfoData = cast(string) ad.readData (".PKGINFO");
113
114 try {
115 validate (pkgInfoData);
116 } catch (UTFException e) {
117 logError ("PKGINFO file in archive %s contained invalid UTF-8, skipping!",
118 packageArchivePath);
119 continue;
120 }
121
122 pkg.filename = packageArchivePath;
123 auto lines = pkgInfoData.splitLines ();
124 // If the current line doesn't contain a = it's meant to extend the previous line
125 string[] completePair;
126 foreach (currentLine; lines) {
127 if (currentLine.canFind ("=")) {
128 if (completePair.empty) {
129 completePair = [currentLine];
130 continue;
131 }
132
133 this.setPkgValues (pkg, completePair.join (" ").split ("="));
134 completePair = [currentLine];
135 } else if (!currentLine.startsWith ("#")) {
136 completePair ~= currentLine.strip.split ("#")[0];
137 }
138 }
139 // We didn't process the last line yet
140 this.setPkgValues (pkg, completePair.join (" ").split ("="));
141 pkg.contents = ad.readContents ().remove!("a == \".PKGINFO\" || a.startsWith (\".SIGN\")");
93 pkg.filename = buildPath(rootDir, suite, section, arch, fileName);
94 pkg.maintainer = pkgInfo.maintainer;
95 setPkgDescription(pkg, pkgInfo.pkgdesc);
14296 }
14397
14498 // perform a sanity check, so we will never emit invalid packages
6262
6363 if (fileName.isRemote) {
6464 try {
65 /* This should use download(), but that doesn't throw errors */
6665 downloader.downloadFile (fileName, destFileName);
67
6866 return destFileName;
6967 } catch (DownloadException e) {
7068 logDebug ("Unable to download: %s", e.msg);
+0
-58
src/asgen/bindings/appstream_utils.d less more
0 /*
1 * Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.bindings.appstream_utils;
20
21 private import appstream.c.types;
22 private import glib.Str;
23 private import std.string : toStringz;
24
25 extern(C) {
26 nothrow:
27 @nogc:
28 @system:
29
30 bool as_utils_is_tld (const(char)* tld) pure;
31 bool as_utils_is_category_name (const(char)* category_name);
32
33 const(char*) as_format_version_to_string (FormatVersion ver);
34 FormatVersion as_format_version_from_string (const(char)* version_str);
35
36 private bool as_license_is_metadata_license (const(char)* license) pure;
37 private char** as_spdx_license_tokenize (const(char)* license) pure;
38
39 const(char) *as_get_appstream_version () pure;
40
41 private const(char) *as_component_kind_to_string (AsComponentKind kind) pure;
42 }
43
44 auto spdxLicenseTokenize (const string license) pure
45 {
46 return Str.toStringArray (as_spdx_license_tokenize (license.toStringz));
47 }
48
49 bool spdxLicenseIsMetadataLicense (const string license) pure
50 {
51 return as_license_is_metadata_license (license.toStringz);
52 }
53
54 auto componentKindToString (AsComponentKind kind) pure
55 {
56 return Str.toString (as_component_kind_to_string (kind));
57 }
+0
-143
src/asgen/bindings/cairo.d less more
0 /*
1 * Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.bindings.cairo;
20
21 extern(C):
22 nothrow:
23 @nogc:
24 static if (!is(typeof(usize))) private alias usize = size_t;
25
26 struct _cairo;
27 alias cairo_p = _cairo*;
28
29 struct _cairo_surface;
30 alias cairo_surface_p = _cairo_surface*;
31
32 struct _cairo_font_face;
33 alias cairo_font_face_p = _cairo_font_face*;
34
35 struct cairo_text_extents_t {
36 double x_bearing;
37 double y_bearing;
38 double width;
39 double height;
40 double x_advance;
41 double y_advance;
42 }
43
44 enum cairo_status_t {
45 STATUS_SUCCESS = 0,
46
47 STATUS_NO_MEMORY,
48 STATUS_INVALID_RESTORE,
49 STATUS_INVALID_POP_GROUP,
50 STATUS_NO_CURRENT_POINT,
51 STATUS_INVALID_MATRIX,
52 STATUS_INVALID_STATUS,
53 STATUS_NULL_POINTER,
54 STATUS_INVALID_STRING,
55 STATUS_INVALID_PATH_DATA,
56 STATUS_READ_ERROR,
57 STATUS_WRITE_ERROR,
58 STATUS_SURFACE_FINISHED,
59 STATUS_SURFACE_TYPE_MISMATCH,
60 STATUS_PATTERN_TYPE_MISMATCH,
61 STATUS_INVALID_CONTENT,
62 STATUS_INVALID_FORMAT,
63 STATUS_INVALID_VISUAL,
64 STATUS_FILE_NOT_FOUND,
65 STATUS_INVALID_DASH,
66 STATUS_INVALID_DSC_COMMENT,
67 STATUS_INVALID_INDEX,
68 STATUS_CLIP_NOT_REPRESENTABLE,
69 STATUS_TEMP_FILE_ERROR,
70 STATUS_INVALID_STRIDE,
71 STATUS_FONT_TYPE_MISMATCH,
72 STATUS_USER_FONT_IMMUTABLE,
73 STATUS_USER_FONT_ERROR,
74 STATUS_NEGATIVE_COUNT,
75 STATUS_INVALID_CLUSTERS,
76 STATUS_INVALID_SLANT,
77 STATUS_INVALID_WEIGHT,
78 STATUS_INVALID_SIZE,
79 STATUS_USER_FONT_NOT_IMPLEMENTED,
80 STATUS_DEVICE_TYPE_MISMATCH,
81 STATUS_DEVICE_ERROR,
82 STATUS_INVALID_MESH_CONSTRUCTION,
83 STATUS_DEVICE_FINISHED,
84 STATUS_JBIG2_GLOBAL_MISSING,
85
86 STATUS_LAST_STATUS
87 }
88
89 enum cairo_format_t {
90 FORMAT_INVALID = -1,
91 FORMAT_ARGB32 = 0,
92 FORMAT_RGB24 = 1,
93 FORMAT_A8 = 2,
94 FORMAT_A1 = 3,
95 FORMAT_RGB16_565 = 4,
96 FORMAT_RGB30 = 5
97 }
98
99 // Context
100 cairo_p cairo_create (cairo_surface_p target);
101 cairo_p cairo_reference (cairo_p cr);
102 void cairo_destroy (cairo_p cr);
103 void cairo_set_source_surface (cairo_p cr, cairo_surface_p surface, double x, double y);
104 void cairo_paint (cairo_p cr);
105
106 void cairo_save (cairo_p cr);
107 void cairo_restore (cairo_p cr);
108
109
110 // Surface
111 cairo_surface_p cairo_image_surface_create (cairo_format_t format, int width, int height);
112 cairo_surface_p cairo_image_surface_create_from_png (const(char) *filename); // Toy API
113 void cairo_surface_destroy (cairo_surface_p surface);
114 cairo_status_t cairo_surface_status (cairo_surface_p surface);
115 int cairo_image_surface_get_width (cairo_surface_p surface);
116 int cairo_image_surface_get_height (cairo_surface_p surface);
117 cairo_status_t cairo_surface_write_to_png (cairo_surface_p surface, const(char) *filename); // Toy API
118
119 void cairo_surface_flush (cairo_surface_p surface);
120 ubyte* cairo_image_surface_get_data (cairo_surface_p surface);
121
122 // Transformations
123 void cairo_scale (cairo_p cr, double sx, double sy);
124 void cairo_translate (cairo_p cr, double tx, double ty);
125
126 // Drawing
127 void cairo_move_to (cairo_p cr, double x, double y);
128 void cairo_set_source_rgb (cairo_p cr, double red, double green, double blue);
129
130 // Fonts
131 import asgen.bindings.freetype;
132 cairo_font_face_p cairo_ft_font_face_create_for_ft_face (const(FT_Face) face, int load_flags);
133 void cairo_font_face_destroy (cairo_font_face_p font_face);
134 cairo_status_t cairo_font_face_status (cairo_font_face_p font_face);
135
136 cairo_font_face_p cairo_get_font_face (cairo_p cr);
137 void cairo_set_font_face (cairo_p cr, cairo_font_face_p font_face);
138
139 void cairo_set_font_size (cairo_p cr, double size);
140 void cairo_show_text (cairo_p cr, const(char) *utf8); // Toy API
141
142 void cairo_text_extents (cairo_p cr, const(char) *utf8, cairo_text_extents_t *extents);
+0
-132
src/asgen/bindings/fontconfig.d less more
0 /*
1 * Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.bindings.fontconfig;
20
21 extern(C):
22 nothrow:
23 @nogc:
24
25 alias FcChar8 = char;
26 alias FcBool = int;
27
28 struct FcPattern;
29 struct FcConfig;
30
31 struct FcMatrix;
32 struct FcCharSet;
33 struct FcLangSet;
34 struct FcRange;
35
36 struct FcStrList;
37 struct FcStrSet;
38
39 struct FcBlanks;
40
41 immutable char *FC_LANG = "lang"; // String RFC 3066 langs
42 immutable char *FC_STYLE = "style"; // String
43 immutable char *FC_FULLNAME = "fullname"; // String
44
45 struct FcFontSet {
46 int nfont;
47 int sfont;
48 FcPattern **fonts;
49 }
50
51 enum FcType {
52 Unknown = -1,
53 Void,
54 Integer,
55 Double,
56 String,
57 Bool,
58 Matrix,
59 CharSet,
60 FTFace,
61 LangSet
62 }
63
64 struct FcValue
65 {
66 FcType type;
67 union {
68 const FcChar8 *s;
69 int i;
70 FcBool b;
71 double d;
72 const FcMatrix *m;
73 const FcCharSet *c;
74 void *f;
75 const FcLangSet *l;
76 const FcRange *r;
77 }
78 }
79
80 enum FcSetName
81 {
82 System = 0,
83 Application = 1
84 }
85
86 enum FcResult {
87 Match,
88 NoMatch,
89 TypeMismatch,
90 NoId,
91 OutOfMemory
92 }
93
94 FcBool FcInit ();
95
96 FcConfig *FcConfigCreate ();
97 void FcConfigDestroy (FcConfig *config);
98
99 void FcConfigAppFontClear (FcConfig *config);
100 bool FcConfigSetCurrent (FcConfig *config);
101 bool FcConfigAppFontAddFile (FcConfig *config,
102 const char *file);
103 FcFontSet *FcConfigGetFonts (FcConfig *config,
104 FcSetName set);
105
106 FcPattern *FcFreeTypeQuery (const FcChar8 *file,
107 int id,
108 FcBlanks *blanks,
109 int *count);
110 void FcPatternDestroy (FcPattern *p);
111
112 FcResult FcPatternGet (const FcPattern *p,
113 const char *object,
114 int id,
115 FcValue *v);
116 FcResult FcPatternGetLangSet (const FcPattern *p,
117 const char *object,
118 int n,
119 FcLangSet **ls);
120 FcResult FcPatternGetString (const FcPattern *p,
121 const char *object,
122 int n,
123 FcChar8 ** s);
124
125 FcStrList *FcStrListCreate (FcStrSet *set);
126 void FcStrListFirst (FcStrList *list);
127 char *FcStrListNext (FcStrList *list);
128 void FcStrListDone (FcStrList *list);
129
130 FcStrSet *FcLangSetGetLangs (const FcLangSet *ls);
131 void FcStrSetDestroy (FcStrSet *set);
+0
-63
src/asgen/bindings/freetype.d less more
0 /*
1 * Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.bindings.freetype;
20
21 public import asgen.bindings.freetypeTypes;
22
23 extern(C):
24 nothrow:
25 @nogc:
26
27 FT_Error FT_Init_FreeType (FT_Library *alibrary);
28 FT_Error FT_Done_Library (FT_Library library);
29
30 FT_Error FT_New_Face (FT_Library library,
31 const(char) *filepathname,
32 FT_Long face_index,
33 FT_Face *aface);
34 FT_Error FT_New_Memory_Face (FT_Library library,
35 const FT_Byte *file_base,
36 FT_Long file_size,
37 FT_Long face_index,
38 FT_Face *aface);
39 FT_Error FT_Done_Face (FT_Face face);
40
41 FT_Error FT_Get_BDF_Charset_ID (FT_Face face,
42 const char* *acharset_encoding,
43 const char* *acharset_registry);
44
45 FT_UInt FT_Get_Char_Index (FT_Face face,
46 FT_ULong charcode);
47
48 FT_ULong FT_Get_First_Char (FT_Face face,
49 FT_UInt *agindex);
50 FT_ULong FT_Get_Next_Char (FT_Face face,
51 FT_ULong char_code,
52 FT_UInt *agindex);
53
54 FT_Error FT_Select_Charmap (FT_Face face,
55 FT_Encoding encoding);
56 FT_Error FT_Set_Charmap (FT_Face face,
57 FT_CharMap charmap);
58
59 FT_UInt FT_Get_Sfnt_Name_Count (FT_Face face);
60 FT_Error FT_Get_Sfnt_Name (FT_Face face,
61 FT_UInt idx,
62 FT_SfntName *aname);
+0
-2231
src/asgen/bindings/freetypeTypes.d less more
0 /*
1 Boost Software License - Version 1.0 - August 17th, 2003
2
3 Permission is hereby granted, free of charge, to any person or organization
4 obtaining a copy of the software and accompanying documentation covered by
5 this license (the "Software") to use, reproduce, display, distribute,
6 execute, and transmit the Software, and to prepare derivative works of the
7 Software, and to permit third-parties to whom the Software is furnished to
8 do so, all subject to the following:
9
10 The copyright notices in the Software and this entire statement, including
11 the above license grant, this restriction and the following disclaimer,
12 must be included in all copies of the Software, in whole or in part, and
13 all derivative works of the Software, unless such copies or derivative
14 works are solely in the form of machine-executable object code generated by
15 a source language processor.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
20 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
21 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
22 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 DEALINGS IN THE SOFTWARE.
24 */
25
26 module asgen.bindings.freetypeTypes;
27
28 import core.stdc.config : c_long, c_ulong;
29
30 extern(C):
31 nothrow:
32 @nogc:
33
34 // config/ftconfig.h
35 alias FT_Int16 = short;
36 alias FT_UInt16 = ushort;
37 alias FT_Int32 = int;
38 alias FT_UInt32 = uint;
39 alias FT_Fast = int;
40 alias FT_UFast = uint;
41 alias FT_Int64 = long;
42 alias FT_Uint64 = ulong;
43
44 // fttypes.h
45 alias FT_Bool = ubyte;
46 alias FT_FWord = short;
47 alias FT_UFWord = ushort;
48 alias FT_Char = char;
49 alias FT_Byte = ubyte;
50 alias FT_Bytes = FT_Byte*;
51 alias FT_Tag = FT_UInt32;
52 alias FT_String = char;
53 alias FT_Short = short;
54 alias FT_UShort = ushort;
55 alias FT_Int = int;
56 alias FT_UInt = uint;
57 alias FT_Long = c_long;
58 alias FT_ULong = c_ulong;
59 alias FT_F2Dot14 = short;
60 alias FT_F26Dot6 = c_long;
61 alias FT_Fixed = c_long;
62 alias FT_Error = int;
63 alias FT_Pointer = void*;
64 alias FT_Offset = size_t;
65 alias FT_PtrDist = ptrdiff_t;
66
67 struct FT_UnitVector {
68 FT_F2Dot14 x;
69 FT_F2Dot14 y;
70 }
71
72 struct FT_Matrix {
73 FT_Fixed xx, xy;
74 FT_Fixed yx, yy;
75 }
76
77 struct FT_Data {
78 const( FT_Byte )* pointer;
79 FT_Int length;
80 }
81
82 extern( C ) nothrow alias FT_Generic_Finalizer = void function( void* );
83
84 struct FT_Generic {
85 void* data;
86 FT_Generic_Finalizer finalizer;
87 }
88
89 FT_Tag FT_MAKE_TAG( char x1, char x2, char x3, char x4 ) {
90 return cast( FT_UInt32)(( x1 << 24 ) | ( x2 << 16 ) | ( x3 << 8 ) | x4 );
91 }
92
93 alias FT_ListNode = FT_ListNodeRec*;
94 alias FT_List = FT_ListRec*;
95
96 struct FT_ListNodeRec {
97 FT_ListNode prev;
98 FT_ListNode next;
99 void* data;
100 }
101
102 struct FT_ListRec {
103 FT_ListNode head;
104 FT_ListNode tail;
105 }
106
107 // freetype.h
108 struct FT_Glyph_Metrics {
109 FT_Pos width;
110 FT_Pos height;
111 FT_Pos horiBearingX;
112 FT_Pos horiBearingY;
113 FT_Pos horiAdvance;
114 FT_Pos vertBearingX;
115 FT_Pos vertBearingY;
116 FT_Pos vertAdvance;
117 }
118
119 struct FT_Bitmap_Size {
120 FT_Short height;
121 FT_Short width;
122 FT_Pos size;
123 FT_Pos x_ppem;
124 FT_Pos y_ppem;
125 }
126
127 struct FT_LibraryRec;
128 struct FT_ModuleRec;
129 struct FT_DriverRec;
130 struct FT_RendererRec;
131
132 alias FT_Library = FT_LibraryRec*;
133 alias FT_Module = FT_ModuleRec*;
134 alias FT_Driver = FT_DriverRec*;
135 alias FT_Renderer = FT_RendererRec*;
136 alias FT_Face = FT_FaceRec*;
137 alias FT_Size = FT_SizeRec*;
138 alias FT_GlyphSlot = FT_GlyphSlotRec*;
139 alias FT_CharMap = FT_CharMapRec*;
140
141 alias FT_ENC_TAG = FT_MAKE_TAG;
142
143 alias FT_Encoding = FT_Tag;
144 enum : FT_Tag {
145 FT_ENCODING_NONE = 0,
146 FT_ENCODING_MS_SYMBOL = FT_MAKE_TAG( 's','y','m','b' ),
147 FT_ENCODING_UNICODE = FT_MAKE_TAG( 'u','n','i','c' ),
148 FT_ENCODING_SJIS = FT_MAKE_TAG( 's','j','i','s' ),
149 FT_ENCODING_GB2312 = FT_MAKE_TAG( 'g','b',' ',' ' ),
150 FT_ENCODING_BIG5 = FT_MAKE_TAG('b','i','g','5' ),
151 FT_ENCODING_WANSUNG = FT_MAKE_TAG( 'w','a','n','s' ),
152 FT_ENCODING_JOHAB = FT_MAKE_TAG( 'j','o','h','a' ),
153 FT_ENCODING_MS_SJIS = FT_ENCODING_SJIS,
154 FT_ENCODING_MS_GB2312 = FT_ENCODING_GB2312,
155 FT_ENCODING_MS_BIG5 = FT_ENCODING_BIG5,
156 FT_ENCODING_MS_WANSUNG = FT_ENCODING_WANSUNG,
157 FT_ENCODING_MS_JOHAB = FT_ENCODING_JOHAB,
158 FT_ENCODING_ADOBE_STANDARD = FT_MAKE_TAG( 'A','D','O','B' ),
159 FT_ENCODING_ADOBE_EXPERT = FT_MAKE_TAG( 'A','D','B','E' ),
160 FT_ENCODING_ADOBE_CUSTOM = FT_MAKE_TAG( 'A','D','B','C' ),
161 FT_ENCODING_ADOBE_LATIN_1 = FT_MAKE_TAG( 'l','a','t','1' ),
162 FT_ENCODING_OLD_LATIN_2 = FT_MAKE_TAG( 'l','a','t','2' ),
163 FT_ENCODING_APPLE_ROMAN = FT_MAKE_TAG( 'a','r','m','n' ),
164 }
165
166 struct FT_CharMapRec {
167 FT_Face face;
168 FT_Encoding encoding;
169 FT_UShort platform_id;
170 FT_UShort encoding_id;
171 }
172
173 struct FT_Face_InternalRec;
174 alias FT_Face_Internal = FT_Face_InternalRec*;
175
176 struct FT_FaceRec {
177 FT_Long num_faces;
178 FT_Long face_index;
179 FT_Long face_flags;
180 FT_Long style_flags;
181 FT_Long num_glyphs;
182 FT_String* family_name;
183 FT_String* style_name;
184 FT_Int num_fixed_sizes;
185 FT_Bitmap_Size* available_sizes;
186 FT_Int num_charmaps;
187 FT_CharMap* charmaps;
188 FT_Generic generic;
189 FT_BBox bbox;
190 FT_UShort units_per_EM;
191 FT_Short ascender;
192 FT_Short descender;
193 FT_Short height;
194 FT_Short max_advance_width;
195 FT_Short max_advance_height;
196 FT_Short underline_position;
197 FT_Short underline_thickness;
198 FT_GlyphSlot glyph;
199 FT_Size size;
200 FT_CharMap charmap;
201 FT_Driver driver;
202 FT_Memory memory;
203 FT_Stream stream;
204 FT_ListRec sizes_list;
205 FT_Generic autohint;
206 void* extensions;
207 FT_Face_Internal internal;
208 }
209
210 enum : uint {
211 FT_FACE_FLAG_SCALABLE = 1 << 0,
212 FT_FACE_FLAG_FIXED_SIZES = 1 << 1,
213 FT_FACE_FLAG_FIXED_WIDTH = 1 << 2,
214 FT_FACE_FLAG_SFNT = 1 << 3,
215 FT_FACE_FLAG_HORIZONTAL = 1 << 4,
216 FT_FACE_FLAG_VERTICAL = 1 << 5,
217 FT_FACE_FLAG_KERNING = 1 << 6,
218 FT_FACE_FLAG_FAST_GLYPHS = 1 << 7,
219 FT_FACE_FLAG_MULTIPLE_MASTERS = 1 << 8,
220 FT_FACE_FLAG_GLYPH_NAMES = 1 << 9,
221 FT_FACE_FLAG_EXTERNAL_STREAM = 1 << 10,
222 FT_FACE_FLAG_HINTER = 1 << 11,
223 FT_FACE_FLAG_CID_KEYED = 1 << 12,
224 FT_FACE_FLAG_TRICKY = 1 << 13,
225 FT_FACE_FLAG_COLOR = 1 << 14,
226 }
227
228 @nogc nothrow {
229 bool FT_HAS_HORIZONTAL( FT_FaceRec* face ) {
230 return ( face.face_flags & FT_FACE_FLAG_HORIZONTAL ) == 0;
231 }
232
233 bool FT_HAS_VERTICAL( FT_FaceRec* face ) {
234 return ( face.face_flags & FT_FACE_FLAG_VERTICAL ) == 0;
235 }
236
237 bool FT_HAS_KERNING( FT_FaceRec* face ) {
238 return ( face.face_flags & FT_FACE_FLAG_KERNING ) == 0;
239 }
240
241 bool FT_IS_SCALABLE( FT_FaceRec* face ) {
242 return ( face.face_flags & FT_FACE_FLAG_SCALABLE ) == 0;
243 }
244
245 bool FT_IS_SFNT( FT_FaceRec* face ) {
246 return ( face.face_flags & FT_FACE_FLAG_SFNT ) == 0;
247 }
248
249 bool FT_IS_FIXED_WIDTH( FT_FaceRec* face ) {
250 return ( face.face_flags & FT_FACE_FLAG_FIXED_WIDTH ) == 0;
251 }
252
253 bool FT_HAS_FIXED_SIZES( FT_FaceRec* face ) {
254 return ( face.face_flags & FT_FACE_FLAG_FIXED_SIZES ) == 0;
255 }
256
257 bool FT_HAS_FAST_GLYPHS( FT_FaceRec* face ) {
258 return false;
259 }
260
261 bool FT_HAS_GLYPH_NAMES( FT_FaceRec* face ) {
262 return ( face.face_flags & FT_FACE_FLAG_GLYPH_NAMES ) == 0;
263 }
264
265 bool FT_HAS_MULTIPLE_MASTERS( FT_FaceRec* face ) {
266 return ( face.face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS ) == 0;
267 }
268
269 bool FT_IS_CID_KEYED( FT_FaceRec* face ) {
270 return ( face.face_flags & FT_FACE_FLAG_CID_KEYED ) == 0;
271 }
272
273 bool FT_IS_TRICKY( FT_FaceRec* face ) {
274 return ( face.face_flags & FT_FACE_FLAG_TRICKY ) == 0;
275 }
276
277 bool FT_HAS_COLOR( FT_FaceRec* face ) {
278 return ( face.face_flags & FT_FACE_FLAG_COLOR ) == 0;
279 }
280 }
281
282 enum {
283 FT_STYLE_FLAG_ITALIC = 1 << 0,
284 FT_STYLE_FLAG_BOLD = 1 << 1,
285 }
286
287 struct FT_Size_InternalRec;
288 alias FT_Size_Internal = FT_Size_InternalRec*;
289
290 struct FT_Size_Metrics {
291 FT_UShort x_ppem;
292 FT_UShort y_ppem;
293
294 FT_Fixed x_scale;
295 FT_Fixed y_scale;
296
297 FT_Pos ascender;
298 FT_Pos descender;
299 FT_Pos height;
300 FT_Pos max_advance;
301 }
302
303 struct FT_SizeRec {
304 FT_Face face;
305 FT_Generic generic;
306 FT_Size_Metrics metrics;
307 FT_Size_Internal internal;
308 }
309
310 struct FT_SubGlyphRec;
311 struct FT_Slot_InternalRec;
312 alias FT_SubGlyph = FT_SubGlyphRec*;
313 alias FT_Slot_Internal = FT_Slot_InternalRec*;
314
315 struct FT_GlyphSlotRec {
316 FT_Library library;
317 FT_Face face;
318 FT_GlyphSlot next;
319 FT_UInt reserved;
320 FT_Generic generic;
321 FT_Glyph_Metrics metrics;
322 FT_Fixed linearHoriAdvance;
323 FT_Fixed linearVertAdvance;
324 FT_Vector advance;
325 FT_Glyph_Format format;
326 FT_Bitmap bitmap;
327 FT_Int bitmap_left;
328 FT_Int bitmap_top;
329 FT_Outline outline;
330 FT_UInt num_subglyphs;
331 FT_SubGlyph subglyphs;
332 void* control_data;
333 c_long control_len;
334 FT_Pos lsb_delta;
335 FT_Pos rsb_delta;
336 void* other;
337 FT_Slot_Internal internal;
338 }
339
340 enum : uint {
341 FT_OPEN_MEMORY = 0x1,
342 FT_OPEN_STREAM = 0x2,
343 FT_OPEN_DRIVER = 0x4,
344 FT_OPEN_PATHNAME = 0x8,
345 FT_OPEN_PARAMS = 0x10,
346 }
347
348 struct FT_Parameter {
349 FT_ULong tag;
350 FT_Pointer data;
351 }
352
353 struct FT_Open_Args {
354 FT_UInt flags;
355 const( FT_Byte )* memory_base;
356 FT_Long memory_size;
357 FT_String* pathname;
358 FT_Stream stream;
359 FT_Module driver;
360 FT_Int num_params;
361 FT_Parameter* params;
362 }
363
364 alias FT_Size_Request_Type = int;
365 enum {
366 FT_SIZE_REQUEST_TYPE_NOMINAL,
367 FT_SIZE_REQUEST_TYPE_REAL_DIM,
368 FT_SIZE_REQUEST_TYPE_BBOX,
369 FT_SIZE_REQUEST_TYPE_CELL,
370 FT_SIZE_REQUEST_TYPE_SCALES,
371 FT_SIZE_REQUEST_TYPE_MAX
372 }
373
374 struct FT_Size_RequestRec {
375 FT_Size_Request_Type type;
376 FT_Long width;
377 FT_Long height;
378 FT_UInt horiResolution;
379 FT_UInt vertResolution;
380 }
381
382 alias FT_Size_Request = FT_Size_RequestRec*;
383
384 enum : uint {
385 FT_LOAD_DEFAULT = 0x0,
386 FT_LOAD_NO_SCALE = 1 << 0,
387 FT_LOAD_NO_HINTING = 1 << 1,
388 FT_LOAD_RENDER = 1 << 2,
389 FT_LOAD_NO_BITMAP = 1 << 3,
390 FT_LOAD_VERTICAL_LAYOUT = 1 << 4,
391 FT_LOAD_FORCE_AUTOHINT = 1 << 5,
392 FT_LOAD_CROP_BITMAP = 1 << 6,
393 FT_LOAD_PEDANTIC = 1 << 7,
394 FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH = 1 << 9,
395 FT_LOAD_NO_RECURSE = 1 << 10,
396 FT_LOAD_IGNORE_TRANSFORM = 1 << 11,
397 FT_LOAD_MONOCHROME = 1 << 12,
398 FT_LOAD_LINEAR_DESIGN = 1 << 13,
399 FT_LOAD_NO_AUTOHINT = 1 << 15,
400 FT_LOAD_COLOR = 1 << 20,
401 FT_LOAD_COMPUTE_METRICS = 1 << 21,
402 }
403
404 enum {
405 FT_LOAD_TARGET_NORMAL = ( FT_RENDER_MODE_NORMAL & 15 ) << 16,
406 FT_LOAD_TARGET_LIGHT = ( FT_RENDER_MODE_LIGHT & 15 ) << 16,
407 FT_LOAD_TARGET_MONO = ( FT_RENDER_MODE_MONO & 15 ) << 16,
408 FT_LOAD_TARGET_LCD = ( FT_RENDER_MODE_LCD & 15 ) << 16,
409 FT_LOAD_TARGET_LCD_V = ( FT_RENDER_MODE_LCD_V & 15 ) << 16,
410 }
411
412 @nogc FT_Render_Mode FT_LOAD_TARGET_MODE( uint x ) nothrow {
413 return cast( FT_Render_Mode )(( x >> 16 ) & 15 );
414 }
415
416 alias FT_Render_Mode = uint;
417 enum {
418 FT_RENDER_MODE_NORMAL = 0,
419 FT_RENDER_MODE_LIGHT,
420 FT_RENDER_MODE_MONO,
421 FT_RENDER_MODE_LCD,
422 FT_RENDER_MODE_LCD_V,
423 FT_RENDER_MODE_MAX
424 }
425
426 enum FT_Kerning_Mode {
427 FT_KERNING_DEFAULT = 0,
428 FT_KERNING_UNFITTED,
429 FT_KERNING_UNSCALED
430 }
431
432 enum
433 {
434 FT_SUBGLYPH_FLAG_ARGS_ARE_WORDS = 1,
435 FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES = 2,
436 FT_SUBGLYPH_FLAG_ROUND_XY_TO_GRID = 4,
437 FT_SUBGLYPH_FLAG_SCALE = 8,
438 FT_SUBGLYPH_FLAG_XY_SCALE = 0x40,
439 FT_SUBGLYPH_FLAG_2X2 = 0x80,
440 FT_SUBGLYPH_FLAG_USE_MY_METRICS = 0x200,
441 }
442
443 enum {
444 FT_FSTYPE_INSTALLABLE_EMBEDDING = 0x0000,
445 FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING = 0x0002,
446 FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING = 0x0004,
447 FT_FSTYPE_EDITABLE_EMBEDDING = 0x0008,
448 FT_FSTYPE_NO_SUBSETTING = 0x0100,
449 FT_FSTYPE_BITMAP_EMBEDDING_ONLY = 0x0200,
450 }
451
452 enum
453 {
454 FREETYPE_MAJOR = 2,
455 FREETYPE_MINOR = 6,
456 FREETYPE_PATCH = 3,
457 }
458
459 // ftadvanc.h
460 enum FT_ADVANCE_FLAG_FAST_ONLY = 0x20000000;
461
462 // ftautoh.h
463 enum {
464 FT_AUTOHINTER_SCRIPT_NONE = 0,
465 FT_AUTOHINTER_SCRIPT_LATIN = 1,
466 FT_AUTOHINTER_SCRIPT_CJK = 2,
467 FT_AUTOHINTER_SCRIPT_INDIC = 3,
468 }
469
470 struct FT_Prop_GlyphToScriptMap {
471 FT_Face face;
472 FT_UShort* map;
473 }
474
475 struct FT_Prop_IncreaseXHeight {
476 FT_Face face;
477 FT_UInt32 limit;
478 }
479
480 // ftbdf.h
481 version( linux ) {
482 alias BDF_PropertyType = int;
483 enum {
484 BDF_PROPERTY_TYPE_NONE = 0,
485 BDF_PROPERTY_TYPE_ATOM = 1,
486 BDF_PROPERTY_TYPE_INTEGER = 2,
487 BDF_PROPERTY_TYPE_CARDINAL = 3
488 }
489
490 alias BDF_Property = BDF_PropertyRec*;
491
492 struct BDF_PropertyRec {
493 BDF_PropertyType type;
494 union u {
495 char* atom;
496 FT_Int32 integer;
497 FT_UInt32 cardinal;
498 }
499 }
500 }
501
502 // ftcache.h
503 alias FTC_FaceID = FT_Pointer;
504 extern( C ) nothrow alias FTC_Face_Requester = FT_Error function( FTC_FaceID,FT_Library,FT_Pointer,FT_Face* );
505
506 struct FTC_ManagerRec;
507 struct FTC_NodeRec;
508
509 alias FTC_Manager = FTC_ManagerRec*;
510 alias FTC_Node = FTC_NodeRec*;
511
512 struct FTC_ScalerRec {
513 FTC_FaceID face_id;
514 FT_UInt width;
515 FT_UInt height;
516 FT_Int pixel;
517 FT_UInt x_res;
518 FT_UInt y_res;
519 }
520
521 alias FTC_Scaler = FTC_ScalerRec*;
522
523 struct FTC_CMapCacheRec;
524 alias FTC_CMapCache = FTC_CMapCacheRec*;
525
526 struct FTC_ImageTypeRec {
527 FTC_FaceID face_id;
528 FT_UInt width;
529 FT_UInt height;
530 FT_Int32 flags;
531 }
532
533
534 alias FTC_ImageType = FTC_ImageTypeRec*;
535
536 struct FTC_ImageCacheRec;
537 alias FTC_ImageCache = FTC_ImageCacheRec*;
538
539 alias FTC_SBit = FTC_SBitRec*;
540
541 struct FTC_SBitRec {
542 FT_Byte width;
543 FT_Byte height;
544 FT_Char left;
545 FT_Char top;
546 FT_Byte format;
547 FT_Byte max_grays;
548 FT_Short pitch;
549 FT_Char xadvance;
550 FT_Char yadvance;
551 FT_Byte* buffer;
552 }
553
554 struct FTC_SBitCacheRec;
555 alias FTC_SBitCache = FTC_SBitCacheRec*;
556
557 // ftcffdrv.h
558 enum {
559 FT_CFF_HINTING_FREETYPE = 0,
560 FT_CFF_HINTING_ADOBE = 1,
561 }
562
563 // fterrdef.h
564 enum {
565 FT_Err_Ok = 0x00,
566 FT_Err_Cannot_Open_Resource = 0x01,
567 FT_Err_Unknown_File_Format = 0x02,
568 FT_Err_Invalid_File_Format = 0x03,
569 FT_Err_Invalid_Version = 0x04,
570 FT_Err_Lower_Module_Version = 0x05,
571 FT_Err_Invalid_Argument = 0x06,
572 FT_Err_Unimplemented_Feature = 0x07,
573 FT_Err_Invalid_Table = 0x08,
574 FT_Err_Invalid_Offset = 0x09,
575 FT_Err_Array_Too_Large = 0x0A,
576 FT_Err_Missing_Module = 0x0B,
577 FT_Err_Missing_Property = 0x0C,
578
579 FT_Err_Invalid_Glyph_Index = 0x10,
580 FT_Err_Invalid_Character_Code = 0x11,
581 FT_Err_Invalid_Glyph_Format = 0x12,
582 FT_Err_Cannot_Render_Glyph = 0x13,
583 FT_Err_Invalid_Outline = 0x14,
584 FT_Err_Invalid_Composite = 0x15,
585 FT_Err_Too_Many_Hints = 0x16,
586 FT_Err_Invalid_Pixel_Size = 0x17,
587
588 FT_Err_Invalid_Handle = 0x20,
589 FT_Err_Invalid_Library_Handle = 0x21,
590 FT_Err_Invalid_Driver_Handle = 0x22,
591 FT_Err_Invalid_Face_Handle = 0x23,
592 FT_Err_Invalid_Size_Handle = 0x24,
593 FT_Err_Invalid_Slot_Handle = 0x25,
594 FT_Err_Invalid_CharMap_Handle = 0x26,
595 FT_Err_Invalid_Cache_Handle = 0x27,
596 FT_Err_Invalid_Stream_Handle = 0x28,
597
598 FT_Err_Too_Many_Drivers = 0x30,
599 FT_Err_Too_Many_Extensions = 0x31,
600
601 FT_Err_Out_Of_Memory = 0x40,
602 FT_Err_Unlisted_Object = 0x41,
603
604 FT_Err_Cannot_Open_Stream = 0x51,
605 FT_Err_Invalid_Stream_Seek = 0x52,
606 FT_Err_Invalid_Stream_Skip = 0x53,
607 FT_Err_Invalid_Stream_Read = 0x54,
608 FT_Err_Invalid_Stream_Operation = 0x55,
609 FT_Err_Invalid_Frame_Operation = 0x56,
610 FT_Err_Nested_Frame_Access = 0x57,
611 FT_Err_Invalid_Frame_Read = 0x58,
612
613 FT_Err_Raster_Uninitialized = 0x60,
614 FT_Err_Raster_Corrupted = 0x61,
615 FT_Err_Raster_Overflow = 0x62,
616 FT_Err_Raster_Negative_Height = 0x63,
617
618 FT_Err_Too_Many_Caches = 0x70,
619
620 FT_Err_Invalid_Opcode = 0x80,
621 FT_Err_Too_Few_Arguments = 0x81,
622 FT_Err_Stack_Overflow = 0x82,
623 FT_Err_Code_Overflow = 0x83,
624 FT_Err_Bad_Argument = 0x84,
625 FT_Err_Divide_By_Zero = 0x85,
626 FT_Err_Invalid_Reference = 0x86,
627 FT_Err_Debug_OpCode = 0x87,
628 FT_Err_ENDF_In_Exec_Stream = 0x88,
629 FT_Err_Nested_DEFS = 0x89,
630 FT_Err_Invalid_CodeRange = 0x8A,
631 FT_Err_Execution_Too_Long = 0x8B,
632 FT_Err_Too_Many_Function_Defs = 0x8C,
633 FT_Err_Too_Many_Instruction_Defs = 0x8D,
634 FT_Err_Table_Missing = 0x8E,
635 FT_Err_Horiz_Header_Missing = 0x8F,
636 FT_Err_Locations_Missing = 0x90,
637 FT_Err_Name_Table_Missing = 0x91,
638 FT_Err_CMap_Table_Missing = 0x92,
639 FT_Err_Hmtx_Table_Missing = 0x93,
640 FT_Err_Post_Table_Missing = 0x94,
641 FT_Err_Invalid_Horiz_Metrics = 0x95,
642 FT_Err_Invalid_CharMap_Format = 0x96,
643 FT_Err_Invalid_PPem = 0x97,
644 FT_Err_Invalid_Vert_Metrics = 0x98,
645 FT_Err_Could_Not_Find_Context = 0x99,
646 FT_Err_Invalid_Post_Table_Format = 0x9A,
647 FT_Err_Invalid_Post_Table = 0x9B,
648
649 FT_Err_Syntax_Error = 0xA0,
650 FT_Err_Stack_Underflow = 0xA1,
651 FT_Err_Ignore = 0xA2,
652 FT_Err_No_Unicode_Glyph_Name = 0xA3,
653 FT_Err_Glyph_Too_Big = 0xA4,
654
655 FT_Err_Missing_Startfont_Field = 0xB0,
656 FT_Err_Missing_Font_Field = 0xB1,
657 FT_Err_Missing_Size_Field = 0xB2,
658 FT_Err_Missing_Fontboundingbox_Field = 0xB3,
659 FT_Err_Missing_Chars_Field = 0xB4,
660 FT_Err_Missing_Startchar_Field = 0xB5,
661 FT_Err_Missing_Encoding_Field = 0xB6,
662 FT_Err_Missing_Bbx_Field = 0xB7,
663 FT_Err_Bbx_Too_Big = 0xB8,
664 FT_Err_Corrupted_Font_Header = 0xB9,
665 FT_Err_Corrupted_Font_Glyphs = 0xBA,
666
667 FT_Err_Max,
668 }
669
670 // ftgasp.h
671 enum {
672 FT_GASP_NO_TABLE = -1,
673 FT_GASP_DO_GRIDFIT = 0x01,
674 FT_GASP_DO_GRAY = 0x02,
675 FT_GASP_SYMMETRIC_SMOOTHING = 0x08,
676 FT_GASP_SYMMETRIC_GRIDFIT = 0x10
677 }
678
679 // ftglyph.h
680 alias FT_Glyph = FT_GlyphRec*;
681
682 struct FT_GlyphRec {
683 FT_Library library;
684 FT_Glyph_Class* clazz;
685 FT_Glyph_Format format;
686 FT_Vector advance;
687 }
688
689 alias FT_BitmapGlyph = FT_BitmapGlyphRec*;
690
691 struct FT_BitmapGlyphRec {
692 FT_GlyphRec root;
693 FT_Int left;
694 FT_Int top;
695 FT_Bitmap bitmap;
696 }
697
698 alias FT_OutlineGlyph = FT_OutlineGlyphRec*;
699
700 struct FT_OutlineGlyphRec {
701 FT_GlyphRec root;
702 FT_Outline outline;
703 }
704
705 alias FT_Glyph_BBox_Mode = int;
706 enum {
707 FT_GLYPH_BBOX_UNSCALED = 0,
708 FT_GLYPH_BBOX_SUBPIXELS = 0,
709 FT_GLYPH_BBOX_GRIDFIT = 1,
710 FT_GLYPH_BBOX_TRUNCATE = 2,
711 FT_GLYPH_BBOX_PIXELS = 3
712 }
713
714 // ftgxval.h
715 enum {
716 FT_VALIDATE_feat_INDEX = 0,
717 FT_VALIDATE_mort_INDEX = 1,
718 FT_VALIDATE_morx_INDEX = 2,
719 FT_VALIDATE_bsln_INDEX = 3,
720 FT_VALIDATE_just_INDEX = 4,
721 FT_VALIDATE_kern_INDEX = 5,
722 FT_VALIDATE_opbd_INDEX = 6,
723 FT_VALIDATE_trak_INDEX = 7,
724 FT_VALIDATE_prop_INDEX = 8,
725 FT_VALIDATE_lcar_INDEX = 9,
726 FT_VALIDATE_GX_LAST_INDEX = FT_VALIDATE_lcar_INDEX,
727 FT_VALIDATE_GX_LENGTH = FT_VALIDATE_GX_LAST_INDEX + 1,
728
729 FT_VALIDATE_GX_START = 0x4000,
730 FT_VALIDATE_feat = FT_VALIDATE_GX_START << FT_VALIDATE_feat_INDEX,
731 FT_VALIDATE_mort = FT_VALIDATE_GX_START << FT_VALIDATE_mort_INDEX,
732 FT_VALIDATE_morx = FT_VALIDATE_GX_START << FT_VALIDATE_morx_INDEX,
733 FT_VALIDATE_bsln = FT_VALIDATE_GX_START << FT_VALIDATE_bsln_INDEX,
734 FT_VALIDATE_just = FT_VALIDATE_GX_START << FT_VALIDATE_just_INDEX,
735 FT_VALIDATE_kern = FT_VALIDATE_GX_START << FT_VALIDATE_kern_INDEX,
736 FT_VALIDATE_opbd = FT_VALIDATE_GX_START << FT_VALIDATE_opbd_INDEX,
737 FT_VALIDATE_trak = FT_VALIDATE_GX_START << FT_VALIDATE_trak_INDEX,
738 FT_VALIDATE_prop = FT_VALIDATE_GX_START << FT_VALIDATE_prop_INDEX,
739 FT_VALIDATE_lcar = FT_VALIDATE_GX_START << FT_VALIDATE_lcar_INDEX,
740
741 FT_VALIDATE_GX = ( FT_VALIDATE_feat |
742 FT_VALIDATE_mort |
743 FT_VALIDATE_morx |
744 FT_VALIDATE_bsln |
745 FT_VALIDATE_just |
746 FT_VALIDATE_kern |
747 FT_VALIDATE_opbd |
748 FT_VALIDATE_trak |
749 FT_VALIDATE_prop |
750 FT_VALIDATE_lcar ),
751
752 FT_VALIDATE_MS = FT_VALIDATE_GX_START << 0,
753 FT_VALIDATE_APPLE = FT_VALIDATE_GX_START << 1,
754 FT_VALIDATE_CKERN = FT_VALIDATE_MS | FT_VALIDATE_APPLE,
755 }
756
757 // ftimage.h
758 alias FT_Pos = c_long;
759
760 struct FT_Vector {
761 FT_Pos x;
762 FT_Pos y;
763 }
764
765 struct FT_BBox {
766 FT_Pos xMin, yMin;
767 FT_Pos xMax, yMax;
768 }
769
770 alias FT_Pixel_Mode = int;
771 enum {
772 FT_PIXEL_MODE_NONE = 0,
773 FT_PIXEL_MODE_MONO,
774 FT_PIXEL_MODE_GRAY,
775 FT_PIXEL_MODE_GRAY2,
776 FT_PIXEL_MODE_GRAY4,
777 FT_PIXEL_MODE_LCD,
778 FT_PIXEL_MODE_LCD_V,
779 FT_PIXEL_MODE_MAX
780 }
781
782 struct FT_Bitmap {
783 uint rows;
784 uint width;
785 int pitch;
786 ubyte* buffer;
787 ushort num_grays;
788 ubyte pixel_mode;
789 ubyte palette_mode;
790 void* palette;
791 }
792
793 struct FT_Outline {
794 short n_contours;
795 short n_points;
796 FT_Vector* points;
797 byte* tags;
798 short* contours;
799 int flags;
800 }
801
802 enum FT_OUTLINE_CONTOURS_MAX = short.max;
803 enum FT_OUTLINE_POINTS_MAX = short.max;
804
805 enum : uint {
806 FT_OUTLINE_NONE = 0x0,
807 FT_OUTLINE_OWNER = 0x1,
808 FT_OUTLINE_EVEN_ODD_FILL = 0x2,
809 FT_OUTLINE_REVERSE_FILL = 0x4,
810 FT_OUTLINE_IGNORE_DROPOUTS = 0x8,
811 FT_OUTLINE_HIGH_PRECISION = 0x100,
812 FT_OUTLINE_SINGLE_PASS = 0x200,
813 }
814
815 enum {
816 FT_CURVE_TAG_ON = 1,
817 FT_CURVE_TAG_CONIC = 0,
818 FT_CURVE_TAG_CUBIC = 2,
819 FT_CURVE_TAG_TOUCH_X = 8,
820 FT_CURVE_TAG_TOUCH_Y = 16,
821 FT_CURVE_TAG_TOUCH_BOTH = FT_CURVE_TAG_TOUCH_X | FT_CURVE_TAG_TOUCH_Y,
822 }
823
824 extern( C ) nothrow {
825 alias FT_Outline_MoveToFunc = int function( const( FT_Vector )*, void* );
826 alias FT_Outline_LineToFunc = int function( const( FT_Vector )*, void* );
827 alias FT_Outline_ConicToFunc = int function( const( FT_Vector )*, const( FT_Vector )*, void* );
828 alias FT_Outline_CubicToFunc = int function( const( FT_Vector )*, const( FT_Vector )*, const( FT_Vector )*, void* );
829 }
830
831 struct FT_Outline_Funcs {
832 FT_Outline_MoveToFunc move_to;
833 FT_Outline_LineToFunc line_to;
834 FT_Outline_ConicToFunc conic_to;
835 FT_Outline_CubicToFunc cubic_to;
836 int shift;
837 FT_Pos delta;
838 }
839
840 alias FT_Glyph_Format = FT_Tag;
841 enum : FT_Tag {
842 FT_GLYPH_FORMAT_NONE = 0,
843 FT_GLYPH_FORMAT_COMPOSITE = FT_MAKE_TAG( 'c','o','m','p' ),
844 FT_GLYPH_FORMAT_BITMAP = FT_MAKE_TAG( 'b','i','t','s' ),
845 FT_GLYPH_FORMAT_OUTLINE = FT_MAKE_TAG( 'o','u','t','l' ),
846 FT_GLYPH_FORMAT_PLOTTER = FT_MAKE_TAG( 'p','l','o','t' ),
847 }
848
849 struct FT_RasterRec;
850 alias FT_Raster = FT_RasterRec*;
851
852 struct FT_Span {
853 short x;
854 ushort len;
855 ubyte coverage;
856 }
857
858 extern( C ) nothrow alias FT_SpanFunc = void function( int, int, FT_Span*, void* );
859
860 enum {
861 FT_RASTER_FLAG_DEFAULT = 0x0,
862 FT_RASTER_FLAG_AA = 0x1,
863 FT_RASTER_FLAG_DIRECT = 0x2,
864 FT_RASTER_FLAG_CLIP = 0x4
865 }
866
867 struct FT_Raster_Params {
868 const( FT_Bitmap )* target;
869 const( void )* source;
870 int flags;
871 FT_SpanFunc gray_spans;
872 void* black_spans;
873 void* bit_test;
874 void* bit_set;
875 void* user;
876 FT_BBox clip_box;
877 }
878
879 extern( C ) nothrow {
880 alias FT_Raster_NewFunc = int function( void*, FT_Raster* );
881 alias FT_Raster_DoneFunc = void function( FT_Raster );
882 alias FT_Raster_ResetFunc = void function( FT_Raster, ubyte*, uint );
883 alias FT_Raster_SetModeFunc = int function( FT_Raster, uint, void* );
884 alias FT_Raster_RenderFunc = int function( FT_Raster, FT_Raster_Params* );
885 }
886
887
888 struct FT_Raster_Funcs {
889 FT_Glyph_Format glyph_format;
890 FT_Raster_NewFunc raster_new;
891 FT_Raster_ResetFunc raster_reset;
892 FT_Raster_SetModeFunc raster_set_mode;
893 FT_Raster_RenderFunc raster_render;
894 FT_Raster_DoneFunc raster_done;
895 }
896
897 // ftincrem.h
898 struct FT_IncrementalRec;
899 alias FT_Incremental = FT_IncrementalRec*;
900
901 struct FT_Incremental_MetricsRec {
902 FT_Long bearing_x;
903 FT_Long bearing_y;
904 FT_Long advance;
905 }
906
907 alias FT_Incremental_Metrics = FT_Incremental_MetricsRec*;
908
909 extern( C ) nothrow {
910 alias FT_Incremental_GetGlyphDataFunc = FT_Error function( FT_Incremental, FT_UInt, FT_Data* );
911 alias FT_Incremental_FreeGlyphDataFunc = void function( FT_Incremental, FT_Data* );
912 alias FT_Incremental_GetGlyphMetricsFunc = FT_Error function( FT_Incremental, FT_UInt, FT_Bool, FT_Incremental_MetricsRec* );
913 }
914
915 struct FT_Incremental_FuncsRec {
916 FT_Incremental_GetGlyphDataFunc get_glyph_data;
917 FT_Incremental_FreeGlyphDataFunc free_glyph_data;
918 FT_Incremental_GetGlyphMetricsFunc get_glyph_metrics;
919 }
920
921 struct FT_Incremental_InterfaceRec {
922 FT_Incremental_FuncsRec* funcs;
923 FT_Incremental object;
924 }
925
926 enum FT_PARAM_TAG_INCREMENTAL = FT_MAKE_TAG( 'i','n','c','r' );
927
928 alias FT_Incremental_Interface = FT_Incremental_InterfaceRec*;
929
930 // ftlcdfil.h
931 alias FT_LcdFilter = int;
932 enum {
933 FT_LCD_FILTER_NONE = 0,
934 FT_LCD_FILTER_DEFAULT = 1,
935 FT_LCD_FILTER_LIGHT = 2,
936 FT_LCD_FILTER_LEGACY1 = 3,
937 FT_LCD_FILTER_LEGACY = 16,
938 FT_LCD_FILTER_MAX
939 }
940
941 // ftlist.h
942 extern( C ) nothrow {
943 alias FT_List_Iterator = FT_Error function( FT_ListNode, void* );
944 alias FT_List_Destructor = void function( FT_Memory, void*, void* );
945 }
946
947 // ftmm.h
948 struct FT_MM_Axis {
949 FT_String* name;
950 FT_Long minimum;
951 FT_Long maximum;
952 }
953
954 struct FT_Multi_Master {
955 FT_UInt num_axis;
956 FT_UInt num_designs;
957 FT_MM_Axis[4] axis;
958 }
959
960 struct FT_Var_Axis {
961 FT_String* name;
962 FT_Fixed minimum;
963 FT_Fixed def;
964 FT_Fixed maximum;
965 FT_ULong tag;
966 FT_UInt strid;
967 }
968
969 struct FT_Var_Named_Style {
970 FT_Fixed* coords;
971 FT_UInt strid;
972 }
973
974 struct FT_MM_Var {
975 FT_UInt num_axis;
976 FT_UInt num_designs;
977 FT_UInt num_namedstyles;
978 FT_Var_Axis* axis;
979 FT_Var_Named_Style* namedstyle;
980 }
981
982 // ftmodapi.h
983 enum
984 {
985 FT_MODULE_FONT_DRIVER = 1,
986 FT_MODULE_RENDERER = 2,
987 FT_MODULE_HINTER = 4,
988 FT_MODULE_STYLER = 8,
989 FT_MODULE_DRIVER_SCALABLE = 0x100,
990 FT_MODULE_DRIVER_NO_OUTLINES= 0x200,
991 FT_MODULE_DRIVER_HAS_HINTER = 0x400,
992 FT_MODULE_DRIVER_HINTS_LIGHTLY = 0x800,
993 }
994
995 alias FT_Module_Interface = FT_Pointer;
996
997 extern( C ) nothrow {
998 alias FT_Module_Constructor = FT_Error function( FT_Module );
999 alias FT_Module_Destructor = void function( FT_Module );
1000 alias FT_Module_Requester = FT_Module_Interface function( FT_Module, const( char )* );
1001 }
1002
1003 struct FT_Module_Class {
1004 FT_ULong module_flags;
1005 FT_Long module_size;
1006 FT_String* module_name;
1007 FT_Fixed module_version;
1008 FT_Fixed module_requires;
1009 void* module_interface;
1010 FT_Module_Constructor module_init;
1011 FT_Module_Destructor module_done;
1012 FT_Module_Requester get_interface;
1013 }
1014
1015 extern( C ) nothrow alias FT_DebugHook_Func = void function( void* );
1016
1017 alias FT_TrueTypeEngineType = int;
1018 enum {
1019 FT_TRUETYPE_ENGINE_TYPE_NONE = 0,
1020 FT_TRUETYPE_ENGINE_TYPE_UNPATENTED,
1021 FT_TRUETYPE_ENGINE_TYPE_PATENTED
1022
1023 }
1024
1025 // ftmoderr.h
1026 enum {
1027 FT_Mod_Err_Base = 0x000,
1028 FT_Mod_Err_Autofit = 0x100,
1029 FT_Mod_Err_BDF = 0x200,
1030 FT_Mod_Err_Bzip2 = 0x300,
1031 FT_Mod_Err_Cache = 0x400,
1032 FT_Mod_Err_CFF = 0x500,
1033 FT_Mod_Err_CID = 0x600,
1034 FT_Mod_Err_Gzip = 0x700,
1035 FT_Mod_Err_LZW = 0x800,
1036 FT_Mod_Err_OTvalid = 0x900,
1037 FT_Mod_Err_PCF = 0xA00,
1038 FT_Mod_Err_PFR = 0xB00,
1039 FT_Mod_Err_PSaux = 0xC00,
1040 FT_Mod_Err_PShinter = 0xD00,
1041 FT_Mod_Err_PSnames = 0xE00,
1042 FT_Mod_Err_Raster = 0xF00,
1043 FT_Mod_Err_SFNT = 0x1000,
1044 FT_Mod_Err_Smooth = 0x1100,
1045 FT_Mod_Err_TrueType = 0x1200,
1046 FT_Mod_Err_Type1 = 0x1300,
1047 FT_Mod_Err_Type42 = 0x1400,
1048 FT_Mod_Err_Winfonts = 0x1500,
1049 FT_Mod_Err_GXvalid = 0x1600,
1050 FT_Mod_Err_Max,
1051 }
1052
1053 // ftotval.h
1054 enum {
1055 FT_VALIDATE_BASE = 0x0100,
1056 FT_VALIDATE_GDEF = 0x0200,
1057 FT_VALIDATE_GPOS = 0x0400,
1058 FT_VALIDATE_GSUB = 0x0800,
1059 FT_VALIDATE_JSTF = 0x1000,
1060 FT_VALIDATE_MATH = 0x2000,
1061 FT_VALIDATE_OT = FT_VALIDATE_BASE |
1062 FT_VALIDATE_GDEF |
1063 FT_VALIDATE_GPOS |
1064 FT_VALIDATE_GSUB |
1065 FT_VALIDATE_JSTF |
1066 FT_VALIDATE_MATH,
1067 }
1068
1069 // ftoutln
1070 alias FT_Orientation = int;
1071 enum {
1072 FT_ORIENTATION_TRUETYPE = 0,
1073 FT_ORIENTATION_POSTSCRIPT = 1,
1074 FT_ORIENTATION_FILL_RIGHT = FT_ORIENTATION_TRUETYPE,
1075 FT_ORIENTATION_FILL_LEFT = FT_ORIENTATION_POSTSCRIPT
1076 }
1077
1078 // ftrender.h
1079 extern( C ) nothrow {
1080 alias FT_Glyph_InitFunc = FT_Error function( FT_Glyph, FT_GlyphSlot );
1081 alias FT_Glyph_DoneFunc = void function( FT_Glyph );
1082 alias FT_Glyph_TransformFunc = void function( FT_Glyph, const( FT_Matrix )* matrix, const( FT_Vector )* );
1083 alias FT_Glyph_GetBBoxFunc = void function( FT_Glyph, FT_BBox* );
1084 alias FT_Glyph_CopyFunc = FT_Error function( FT_Glyph, FT_Glyph );
1085 alias FT_Glyph_PrepareFunc = FT_Error function( FT_Glyph, FT_GlyphSlot );
1086 }
1087
1088 struct FT_Glyph_Class { // typedef'd in ftglyph.h
1089 FT_Long glyph_size;
1090 FT_Glyph_Format glyph_format;
1091 FT_Glyph_InitFunc glyph_init;
1092 FT_Glyph_DoneFunc glyph_done;
1093 FT_Glyph_CopyFunc glyph_copy;
1094 FT_Glyph_TransformFunc glyph_transform;
1095 FT_Glyph_GetBBoxFunc glyph_bbox;
1096 FT_Glyph_PrepareFunc glyph_prepare;
1097 }
1098
1099 extern( C ) nothrow {
1100 alias FT_Renderer_RenderFunc = FT_Error function( FT_Renderer, FT_GlyphSlot, FT_UInt, const( FT_Vector )* );
1101 alias FT_Renderer_TransformFunc = FT_Error function( FT_Renderer, FT_GlyphSlot, const( FT_Matrix )*, const( FT_Vector )* );
1102 alias FT_Renderer_GetCBoxFunc = void function( FT_Renderer, FT_GlyphSlot, FT_BBox* );
1103 alias FT_Renderer_SetModeFunc = FT_Error function( FT_Renderer, FT_ULong, FT_Pointer );
1104 }
1105
1106 struct FT_Renderer_Class {
1107 FT_Module_Class root;
1108 FT_Glyph_Format glyph_format;
1109 FT_Renderer_RenderFunc render_glyph;
1110 FT_Renderer_TransformFunc transform_glyph;
1111 FT_Renderer_GetCBoxFunc get_glyph_cbox;
1112 FT_Renderer_SetModeFunc set_mode;
1113 FT_Raster_Funcs* raster_class;
1114 }
1115
1116 // ftsnames.h
1117 struct FT_SfntName {
1118 FT_UShort platform_id;
1119 FT_UShort encoding_id;
1120 FT_UShort language_id;
1121 FT_UShort name_id;
1122 FT_Byte* string;
1123 FT_UInt string_len;
1124 }
1125
1126 enum FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY = FT_MAKE_TAG( 'i','g','p','f' );
1127 enum FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY = FT_MAKE_TAG( 'i','g','p','s' );
1128
1129 // ftstroke.h
1130 struct FT_StrokerRec;
1131 alias FT_Stroker = FT_StrokerRec*;
1132
1133 alias FT_Stroker_LineJoin = int;
1134 enum {
1135 FT_STROKER_LINEJOIN_ROUND = 0,
1136 FT_STROKER_LINEJOIN_BEVEL,
1137 FT_STROKER_LINEJOIN_MITER
1138 }
1139
1140 alias FT_Stroker_LineCap = int;
1141 enum {
1142 FT_STROKER_LINECAP_BUTT = 0,
1143 FT_STROKER_LINECAP_ROUND,
1144 FT_STROKER_LINECAP_SQUARE
1145 }
1146
1147 alias FT_StrokerBorder = int;
1148 enum {
1149 FT_STROKER_BORDER_LEFT = 0,
1150 FT_STROKER_BORDER_RIGHT
1151 }
1152
1153 // ftsystem.h
1154 alias FT_Memory = FT_MemoryRec*;
1155
1156 extern( C ) nothrow {
1157 alias FT_Alloc_Func = void* function( FT_Memory, c_long );
1158 alias FT_Free_Func = void function( FT_Memory, void* );
1159 alias FT_Realloc_Func = void* function( FT_Memory, c_long, c_long, void* );
1160 }
1161
1162 struct FT_MemoryRec {
1163 void* user;
1164 FT_Alloc_Func alloc;
1165 FT_Free_Func free;
1166 FT_Realloc_Func realloc;
1167 }
1168
1169 alias FT_Stream = FT_StreamRec*;
1170
1171 union FT_StreamDesc {
1172 int value;
1173 void* pointer;
1174 }
1175
1176 extern( C ) nothrow {
1177 alias FT_Stream_IoFunc = c_ulong function( FT_Stream, c_ulong, ubyte*, c_ulong );
1178 alias FT_Stream_CloseFunc = void function( FT_Stream );
1179 }
1180
1181 struct FT_StreamRec {
1182 ubyte* base;
1183 c_ulong size;
1184 c_ulong pos;
1185 FT_StreamDesc descriptor;
1186 FT_StreamDesc pathname;
1187 FT_Stream_IoFunc read;
1188 FT_Stream_CloseFunc close;
1189 FT_Memory memory;
1190 ubyte* cursor;
1191 ubyte* limit;
1192 }
1193
1194 // fttrigon.h
1195 alias FT_Angle = FT_Fixed;
1196
1197 enum {
1198 FT_ANGLE_PI = 180 << 16,
1199 FT_ANGLE_2PI = FT_ANGLE_PI * 2,
1200 FT_ANGLE_PI2 = FT_ANGLE_PI / 2,
1201 FT_ANGLE_PI4 = FT_ANGLE_PI / 4
1202 }
1203
1204 // ftttdrv.h
1205 enum TT_INTERPRETER_VERSION_35 = 35;
1206 enum TT_INTERPRETER_VERSION_38 = 38;
1207
1208 // ftwinfnt.h
1209 enum {
1210 FT_WinFNT_ID_CP1252 = 0,
1211 FT_WinFNT_ID_DEFAULT = 1,
1212 FT_WinFNT_ID_SYMBOL = 2,
1213 FT_WinFNT_ID_MAC = 77,
1214 FT_WinFNT_ID_CP932 = 128,
1215 FT_WinFNT_ID_CP949 = 129,
1216 FT_WinFNT_ID_CP1361 = 130,
1217 FT_WinFNT_ID_CP936 = 134,
1218 FT_WinFNT_ID_CP950 = 136,
1219 FT_WinFNT_ID_CP1253 = 161,
1220 FT_WinFNT_ID_CP1254 = 162,
1221 FT_WinFNT_ID_CP1258 = 163,
1222 FT_WinFNT_ID_CP1255 = 177,
1223 FT_WinFNT_ID_CP1256 = 178,
1224 FT_WinFNT_ID_CP1257 = 186,
1225 FT_WinFNT_ID_CP1251 = 204,
1226 FT_WinFNT_ID_CP874 = 222,
1227 FT_WinFNT_ID_CP1250 = 238,
1228 FT_WinFNT_ID_OEM = 255,
1229 }
1230
1231
1232 struct FT_WinFNT_HeaderRec {
1233 FT_UShort _version;
1234 FT_ULong file_size;
1235 FT_Byte[60] copyright;
1236 FT_UShort file_type;
1237 FT_UShort nominal_point_size;
1238 FT_UShort vertical_resolution;
1239 FT_UShort horizontal_resolution;
1240 FT_UShort ascent;
1241 FT_UShort internal_leading;
1242 FT_UShort external_leading;
1243 FT_Byte italic;
1244 FT_Byte underline;
1245 FT_Byte strike_out;
1246 FT_UShort weight;
1247 FT_Byte charset;
1248 FT_UShort pixel_width;
1249 FT_UShort pixel_height;
1250 FT_Byte pitch_and_family;
1251 FT_UShort avg_width;
1252 FT_UShort max_width;
1253 FT_Byte first_char;
1254 FT_Byte last_char;
1255 FT_Byte default_char;
1256 FT_Byte break_char;
1257 FT_UShort bytes_per_row;
1258 FT_ULong device_offset;
1259 FT_ULong face_name_offset;
1260 FT_ULong bits_pointer;
1261 FT_ULong bits_offset;
1262 FT_Byte reserved;
1263 FT_ULong flags;
1264 FT_UShort A_space;
1265 FT_UShort B_space;
1266 FT_UShort C_space;
1267 FT_UShort color_table_offset;
1268 FT_ULong[4] reserved1;
1269 }
1270
1271 alias FT_WinFNT_Header = FT_WinFNT_HeaderRec*;
1272
1273 // t1tables.h
1274 struct PS_FontInfoRec {
1275 FT_String* _version;
1276 FT_String* notice;
1277 FT_String* full_name;
1278 FT_String* family_name;
1279 FT_String* weight;
1280 FT_Long italic_angle;
1281 FT_Bool is_fixed_pitch;
1282 FT_Short underline_position;
1283 FT_UShort underline_thickness;
1284 }
1285
1286 alias PS_FontInfo = PS_FontInfoRec*;
1287
1288 struct PS_PrivateRec {
1289 FT_Int unique_id;
1290 FT_Int lenIV;
1291 FT_Byte num_blue_values;
1292 FT_Byte num_other_blues;
1293 FT_Byte num_family_blues;
1294 FT_Byte num_family_other_blues;
1295 FT_Short[14] blue_values;
1296 FT_Short[10] other_blues;
1297 FT_Short[14] family_blues;
1298 FT_Short[10] family_other_blues;
1299 FT_Fixed blue_scale;
1300 FT_Int blue_shift;
1301 FT_Int blue_fuzz;
1302 FT_UShort[1] standard_width;
1303 FT_UShort[1] standard_height;
1304 FT_Byte num_snap_widths;
1305 FT_Byte num_snap_heights;
1306 FT_Bool force_bold;
1307 FT_Bool round_stem_up;
1308 FT_Short[13] snap_widths;
1309 FT_Short[13] snap_heights;
1310 FT_Fixed expansion_factor;
1311 FT_Long language_group;
1312 FT_Long password;
1313 FT_Short[2] min_feature;
1314 }
1315
1316 alias PS_Private = PS_PrivateRec*;
1317
1318 alias T1_Blend_Flags = int;
1319 enum {
1320 T1_BLEND_UNDERLINE_POSITION = 0,
1321 T1_BLEND_UNDERLINE_THICKNESS,
1322 T1_BLEND_ITALIC_ANGLE,
1323 T1_BLEND_BLUE_VALUES,
1324 T1_BLEND_OTHER_BLUES,
1325 T1_BLEND_STANDARD_WIDTH,
1326 T1_BLEND_STANDARD_HEIGHT,
1327 T1_BLEND_STEM_SNAP_WIDTHS,
1328 T1_BLEND_STEM_SNAP_HEIGHTS,
1329 T1_BLEND_BLUE_SCALE,
1330 T1_BLEND_BLUE_SHIFT,
1331 T1_BLEND_FAMILY_BLUES,
1332 T1_BLEND_FAMILY_OTHER_BLUES,
1333 T1_BLEND_FORCE_BOLD,
1334 T1_BLEND_MAX
1335 }
1336
1337 enum T1_MAX_MM_DESIGNS = 16;
1338 enum T1_MAX_MM_AXIS = 4;
1339 enum T1_MAX_MM_MAP_POINTS = 20;
1340
1341 struct PS_DesignMapRec {
1342 FT_Byte num_points;
1343 FT_Long* design_points;
1344 FT_Fixed* blend_points;
1345 }
1346
1347 alias PS_DesignMap = PS_DesignMapRec*;
1348
1349 struct PS_BlendRec {
1350 FT_UInt num_designs;
1351 FT_UInt num_axis;
1352 FT_String*[T1_MAX_MM_AXIS] axis_names;
1353 FT_Fixed*[T1_MAX_MM_DESIGNS] design_pos;
1354 PS_DesignMapRec[T1_MAX_MM_AXIS] design_map;
1355 FT_Fixed* weight_vector;
1356 FT_Fixed* default_weight_vector;
1357 PS_FontInfo[T1_MAX_MM_DESIGNS+1] font_infos;
1358 PS_Private[T1_MAX_MM_DESIGNS+1] privates;
1359 FT_ULong blend_bitflags;
1360 FT_BBox*[T1_MAX_MM_DESIGNS+1] bboxes;
1361 FT_UInt[T1_MAX_MM_DESIGNS] default_design_vector;
1362 FT_UInt num_default_design_vector;
1363 }
1364
1365 alias PS_Blend = PS_BlendRec*;
1366
1367 struct CID_FaceDictRec {
1368 PS_PrivateRec private_dict;
1369 FT_UInt len_buildchar;
1370 FT_Fixed forcebold_threshold;
1371 FT_Pos stroke_width;
1372 FT_Fixed expansion_factor;
1373 FT_Byte paint_type;
1374 FT_Byte font_type;
1375 FT_Matrix font_matrix;
1376 FT_Vector font_offset;
1377 FT_UInt num_subrs;
1378 FT_ULong subrmap_offset;
1379 FT_Int sd_bytes;
1380 }
1381
1382 alias CID_FaceDict = CID_FaceDictRec*;
1383
1384 struct CID_FaceInfoRec {
1385 FT_String* cid_font_name;
1386 FT_Fixed cid_version;
1387 FT_Int cid_font_type;
1388 FT_String* registry;
1389 FT_String* ordering;
1390 FT_Int supplement;
1391 PS_FontInfoRec font_info;
1392 FT_BBox font_bbox;
1393 FT_ULong uid_base;
1394 FT_Int num_xuid;
1395 FT_ULong[16] xuid;
1396 FT_ULong cidmap_offset;
1397 FT_Int fd_bytes;
1398 FT_Int gd_bytes;
1399 FT_ULong cid_count;
1400 FT_Int num_dicts;
1401 CID_FaceDict font_dicts;
1402 FT_ULong data_offset;
1403 }
1404
1405 alias CID_FaceInfo = CID_FaceInfoRec*;
1406
1407 alias T1_EncodingType = int;
1408 enum {
1409 T1_ENCODING_TYPE_NONE = 0,
1410 T1_ENCODING_TYPE_ARRAY,
1411 T1_ENCODING_TYPE_STANDARD,
1412 T1_ENCODING_TYPE_ISOLATIN1,
1413 T1_ENCODING_TYPE_EXPERT,
1414 }
1415
1416 alias PS_Dict_Keys = int;
1417 enum {
1418 PS_DICT_FONT_TYPE,
1419 PS_DICT_FONT_MATRIX,
1420 PS_DICT_FONT_BBOX,
1421 PS_DICT_PAINT_TYPE,
1422 PS_DICT_FONT_NAME,
1423 PS_DICT_UNIQUE_ID,
1424 PS_DICT_NUM_CHAR_STRINGS,
1425 PS_DICT_CHAR_STRING_KEY,
1426 PS_DICT_CHAR_STRING,
1427 PS_DICT_ENCODING_TYPE,
1428 PS_DICT_ENCODING_ENTRY,
1429
1430 PS_DICT_NUM_SUBRS,
1431 PS_DICT_SUBR,
1432 PS_DICT_STD_HW,
1433 PS_DICT_STD_VW,
1434 PS_DICT_NUM_BLUE_VALUES,
1435 PS_DICT_BLUE_VALUE,
1436 PS_DICT_BLUE_FUZZ,
1437 PS_DICT_NUM_OTHER_BLUES,
1438 PS_DICT_OTHER_BLUE,
1439 PS_DICT_NUM_FAMILY_BLUES,
1440 PS_DICT_FAMILY_BLUE,
1441 PS_DICT_NUM_FAMILY_OTHER_BLUES,
1442 PS_DICT_FAMILY_OTHER_BLUE,
1443 PS_DICT_BLUE_SCALE,
1444 PS_DICT_BLUE_SHIFT,
1445 PS_DICT_NUM_STEM_SNAP_H,
1446 PS_DICT_STEM_SNAP_H,
1447 PS_DICT_NUM_STEM_SNAP_V,
1448 PS_DICT_STEM_SNAP_V,
1449 PS_DICT_FORCE_BOLD,
1450 PS_DICT_RND_STEM_UP,
1451 PS_DICT_MIN_FEATURE,
1452 PS_DICT_LEN_IV,
1453 PS_DICT_PASSWORD,
1454 PS_DICT_LANGUAGE_GROUP,
1455
1456 PS_DICT_VERSION,
1457 PS_DICT_NOTICE,
1458 PS_DICT_FULL_NAME,
1459 PS_DICT_FAMILY_NAME,
1460 PS_DICT_WEIGHT,
1461 PS_DICT_IS_FIXED_PITCH,
1462 PS_DICT_UNDERLINE_POSITION,
1463 PS_DICT_UNDERLINE_THICKNESS,
1464 PS_DICT_FS_TYPE,
1465 PS_DICT_ITALIC_ANGLE,
1466
1467 PS_DICT_MAX = PS_DICT_ITALIC_ANGLE
1468 }
1469
1470 // ttnameid.h
1471 enum {
1472 TT_PLATFORM_APPLE_UNICODE = 0,
1473 TT_PLATFORM_MACINTOSH = 1,
1474 TT_PLATFORM_MICROSOFT = 3,
1475 TT_PLATFORM_CUSTOM = 4,
1476 TT_PLATFORM_ADOBE = 7,
1477 }
1478
1479 enum {
1480 TT_APPLE_ID_DEFAULT = 0,
1481 TT_APPLE_ID_UNICODE_1_1 = 1,
1482 TT_APPLE_ID_UNICODE_2_0 = 3,
1483 TT_APPLE_ID_UNICODE_32 = 4,
1484 TT_APPLE_ID_VARIANT_SELECTOR = 5,
1485 }
1486
1487 enum {
1488 TT_MAC_ID_ROMAN = 0,
1489 TT_MAC_ID_JAPANESE = 1,
1490 TT_MAC_ID_TRADITIONAL_CHINESE = 2,
1491 TT_MAC_ID_KOREAN = 3,
1492 TT_MAC_ID_ARABIC = 4,
1493 TT_MAC_ID_HEBREW = 5,
1494 TT_MAC_ID_GREEK = 6,
1495 TT_MAC_ID_RUSSIAN = 7,
1496 TT_MAC_ID_RSYMBOL = 8,
1497 TT_MAC_ID_DEVANAGARI = 9,
1498 TT_MAC_ID_GURMUKHI = 10,
1499 TT_MAC_ID_GUJARATI = 11,
1500 TT_MAC_ID_ORIYA = 12,
1501 TT_MAC_ID_BENGALI = 13,
1502 TT_MAC_ID_TAMIL = 14,
1503 TT_MAC_ID_TELUGU = 15,
1504 TT_MAC_ID_KANNADA = 16,
1505 TT_MAC_ID_MALAYALAM = 17,
1506 TT_MAC_ID_SINHALESE = 18,
1507 TT_MAC_ID_BURMESE = 19,
1508 TT_MAC_ID_KHMER = 20,
1509 TT_MAC_ID_THAI = 21,
1510 TT_MAC_ID_LAOTIAN = 22,
1511 TT_MAC_ID_GEORGIAN = 23,
1512 TT_MAC_ID_ARMENIAN = 24,
1513 TT_MAC_ID_MALDIVIAN = 25,
1514 TT_MAC_ID_SIMPLIFIED_CHINESE = 25,
1515 TT_MAC_ID_TIBETAN = 26,
1516 TT_MAC_ID_MONGOLIAN = 27,
1517 TT_MAC_ID_GEEZ = 28,
1518 TT_MAC_ID_SLAVIC = 29,
1519 TT_MAC_ID_VIETNAMESE = 30,
1520 TT_MAC_ID_SINDHI = 31,
1521 TT_MAC_ID_UNINTERP = 32,
1522 }
1523
1524 enum {
1525 TT_ISO_ID_7BIT_ASCII = 0,
1526 TT_ISO_ID_10646 = 1,
1527 TT_ISO_ID_8859_1 = 2,
1528 }
1529
1530 enum {
1531 TT_MS_ID_SYMBOL_CS = 0,
1532 TT_MS_ID_UNICODE_CS = 1,
1533 TT_MS_ID_SJIS = 2,
1534 TT_MS_ID_GB2312 = 3,
1535 TT_MS_ID_BIG_5 = 4,
1536 TT_MS_ID_WANSUNG = 5,
1537 TT_MS_ID_JOHAB = 6,
1538 TT_MS_ID_UCS_4 = 10,
1539 }
1540
1541 enum {
1542 TT_ADOBE_ID_STANDARD = 0,
1543 TT_ADOBE_ID_EXPERT = 1,
1544 TT_ADOBE_ID_CUSTOM = 2,
1545 TT_ADOBE_ID_LATIN_1 = 3,
1546 }
1547
1548 enum {
1549 TT_MAC_LANGID_ENGLISH = 0,
1550 TT_MAC_LANGID_FRENCH = 1,
1551 TT_MAC_LANGID_GERMAN = 2,
1552 TT_MAC_LANGID_ITALIAN = 3,
1553 TT_MAC_LANGID_DUTCH = 4,
1554 TT_MAC_LANGID_SWEDISH = 5,
1555 TT_MAC_LANGID_SPANISH = 6,
1556 TT_MAC_LANGID_DANISH = 7,
1557 TT_MAC_LANGID_PORTUGUESE = 8,
1558 TT_MAC_LANGID_NORWEGIAN = 9,
1559 TT_MAC_LANGID_HEBREW = 10,
1560 TT_MAC_LANGID_JAPANESE = 11,
1561 TT_MAC_LANGID_ARABIC = 12,
1562 TT_MAC_LANGID_FINNISH = 13,
1563 TT_MAC_LANGID_GREEK = 14,
1564 TT_MAC_LANGID_ICELANDIC = 15,
1565 TT_MAC_LANGID_MALTESE = 16,
1566 TT_MAC_LANGID_TURKISH = 17,
1567 TT_MAC_LANGID_CROATIAN = 18,
1568 TT_MAC_LANGID_CHINESE_TRADITIONAL = 19,
1569 TT_MAC_LANGID_URDU = 20,
1570 TT_MAC_LANGID_HINDI = 21,
1571 TT_MAC_LANGID_THAI = 22,
1572 TT_MAC_LANGID_KOREAN = 23,
1573 TT_MAC_LANGID_LITHUANIAN = 24,
1574 TT_MAC_LANGID_POLISH = 25,
1575 TT_MAC_LANGID_HUNGARIAN = 26,
1576 TT_MAC_LANGID_ESTONIAN = 27,
1577 TT_MAC_LANGID_LETTISH = 28,
1578 TT_MAC_LANGID_SAAMISK = 29,
1579 TT_MAC_LANGID_FAEROESE = 30,
1580 TT_MAC_LANGID_FARSI = 31,
1581 TT_MAC_LANGID_RUSSIAN = 32,
1582 TT_MAC_LANGID_CHINESE_SIMPLIFIED = 33,
1583 TT_MAC_LANGID_FLEMISH = 34,
1584 TT_MAC_LANGID_IRISH = 35,
1585 TT_MAC_LANGID_ALBANIAN = 36,
1586 TT_MAC_LANGID_ROMANIAN = 37,
1587 TT_MAC_LANGID_CZECH = 38,
1588 TT_MAC_LANGID_SLOVAK = 39,
1589 TT_MAC_LANGID_SLOVENIAN = 40,
1590 TT_MAC_LANGID_YIDDISH = 41,
1591 TT_MAC_LANGID_SERBIAN = 42,
1592 TT_MAC_LANGID_MACEDONIAN = 43,
1593 TT_MAC_LANGID_BULGARIAN = 44,
1594 TT_MAC_LANGID_UKRAINIAN = 45,
1595 TT_MAC_LANGID_BYELORUSSIAN = 46,
1596 TT_MAC_LANGID_UZBEK = 47,
1597 TT_MAC_LANGID_KAZAKH = 48,
1598 TT_MAC_LANGID_AZERBAIJANI = 49,
1599 TT_MAC_LANGID_AZERBAIJANI_CYRILLIC_SCRIPT = 49,
1600 TT_MAC_LANGID_AZERBAIJANI_ARABIC_SCRIPT = 50,
1601 TT_MAC_LANGID_ARMENIAN = 51,
1602 TT_MAC_LANGID_GEORGIAN = 52,
1603 TT_MAC_LANGID_MOLDAVIAN = 53,
1604 TT_MAC_LANGID_KIRGHIZ = 54,
1605 TT_MAC_LANGID_TAJIKI = 55,
1606 TT_MAC_LANGID_TURKMEN = 56,
1607 TT_MAC_LANGID_MONGOLIAN = 57,
1608 TT_MAC_LANGID_MONGOLIAN_MONGOLIAN_SCRIPT = 57,
1609 TT_MAC_LANGID_MONGOLIAN_CYRILLIC_SCRIPT = 58,
1610 TT_MAC_LANGID_PASHTO = 59,
1611 TT_MAC_LANGID_KURDISH = 60,
1612 TT_MAC_LANGID_KASHMIRI = 61,
1613 TT_MAC_LANGID_SINDHI = 62,
1614 TT_MAC_LANGID_TIBETAN = 63,
1615 TT_MAC_LANGID_NEPALI = 64,
1616 TT_MAC_LANGID_SANSKRIT = 65,
1617 TT_MAC_LANGID_MARATHI = 66,
1618 TT_MAC_LANGID_BENGALI = 67,
1619 TT_MAC_LANGID_ASSAMESE = 68,
1620 TT_MAC_LANGID_GUJARATI = 69,
1621 TT_MAC_LANGID_PUNJABI = 70,
1622 TT_MAC_LANGID_ORIYA = 71,
1623 TT_MAC_LANGID_MALAYALAM = 72,
1624 TT_MAC_LANGID_KANNADA = 73,
1625 TT_MAC_LANGID_TAMIL = 74,
1626 TT_MAC_LANGID_TELUGU = 75,
1627 TT_MAC_LANGID_SINHALESE = 76,
1628 TT_MAC_LANGID_BURMESE = 77,
1629 TT_MAC_LANGID_KHMER = 78,
1630 TT_MAC_LANGID_LAO = 79,
1631 TT_MAC_LANGID_VIETNAMESE = 80,
1632 TT_MAC_LANGID_INDONESIAN = 81,
1633 TT_MAC_LANGID_TAGALOG = 82,
1634 TT_MAC_LANGID_MALAY_ROMAN_SCRIPT = 83,
1635 TT_MAC_LANGID_MALAY_ARABIC_SCRIPT = 84,
1636 TT_MAC_LANGID_AMHARIC = 85,
1637 TT_MAC_LANGID_TIGRINYA = 86,
1638 TT_MAC_LANGID_GALLA = 87,
1639 TT_MAC_LANGID_SOMALI = 88,
1640 TT_MAC_LANGID_SWAHILI = 89,
1641 TT_MAC_LANGID_RUANDA = 90,
1642 TT_MAC_LANGID_RUNDI = 91,
1643 TT_MAC_LANGID_CHEWA = 92,
1644 TT_MAC_LANGID_MALAGASY = 93,
1645 TT_MAC_LANGID_ESPERANTO = 94,
1646 TT_MAC_LANGID_WELSH = 128,
1647 TT_MAC_LANGID_BASQUE = 129,
1648 TT_MAC_LANGID_CATALAN = 130,
1649 TT_MAC_LANGID_LATIN = 131,
1650 TT_MAC_LANGID_QUECHUA = 132,
1651 TT_MAC_LANGID_GUARANI = 133,
1652 TT_MAC_LANGID_AYMARA = 134,
1653 TT_MAC_LANGID_TATAR = 135,
1654 TT_MAC_LANGID_UIGHUR = 136,
1655 TT_MAC_LANGID_DZONGKHA = 137,
1656 TT_MAC_LANGID_JAVANESE = 138,
1657 TT_MAC_LANGID_SUNDANESE = 139,
1658 TT_MAC_LANGID_GALICIAN = 140,
1659 TT_MAC_LANGID_AFRIKAANS = 141,
1660 TT_MAC_LANGID_BRETON = 142,
1661 TT_MAC_LANGID_INUKTITUT = 143,
1662 TT_MAC_LANGID_SCOTTISH_GAELIC = 144,
1663 TT_MAC_LANGID_MANX_GAELIC = 145,
1664 TT_MAC_LANGID_IRISH_GAELIC = 146,
1665 TT_MAC_LANGID_TONGAN = 147,
1666 TT_MAC_LANGID_GREEK_POLYTONIC = 148,
1667 TT_MAC_LANGID_GREELANDIC = 149,
1668 TT_MAC_LANGID_AZERBAIJANI_ROMAN_SCRIPT = 150,
1669 }
1670
1671 enum {
1672 TT_MS_LANGID_ARABIC_GENERAL = 0x0001,
1673 TT_MS_LANGID_ARABIC_SAUDI_ARABIA = 0x0401,
1674 TT_MS_LANGID_ARABIC_IRAQ = 0x0801,
1675 TT_MS_LANGID_ARABIC_EGYPT = 0x0c01,
1676 TT_MS_LANGID_ARABIC_LIBYA = 0x1001,
1677 TT_MS_LANGID_ARABIC_ALGERIA = 0x1401,
1678 TT_MS_LANGID_ARABIC_MOROCCO = 0x1801,
1679 TT_MS_LANGID_ARABIC_TUNISIA = 0x1c01,
1680 TT_MS_LANGID_ARABIC_OMAN = 0x2001,
1681 TT_MS_LANGID_ARABIC_YEMEN = 0x2401,
1682 TT_MS_LANGID_ARABIC_SYRIA = 0x2801,
1683 TT_MS_LANGID_ARABIC_JORDAN = 0x2c01,
1684 TT_MS_LANGID_ARABIC_LEBANON = 0x3001,
1685 TT_MS_LANGID_ARABIC_KUWAIT = 0x3401,
1686 TT_MS_LANGID_ARABIC_UAE = 0x3801,
1687 TT_MS_LANGID_ARABIC_BAHRAIN = 0x3c01,
1688 TT_MS_LANGID_ARABIC_QATAR = 0x4001,
1689 TT_MS_LANGID_BULGARIAN_BULGARIA = 0x0402,
1690 TT_MS_LANGID_CATALAN_SPAIN = 0x0403,
1691 TT_MS_LANGID_CHINESE_GENERAL = 0x0004,
1692 TT_MS_LANGID_CHINESE_TAIWAN = 0x0404,
1693 TT_MS_LANGID_CHINESE_PRC = 0x0804,
1694 TT_MS_LANGID_CHINESE_HONG_KONG = 0x0c04,
1695 TT_MS_LANGID_CHINESE_SINGAPORE = 0x1004,
1696 TT_MS_LANGID_CHINESE_MACAU = 0x1404,
1697 TT_MS_LANGID_CZECH_CZECH_REPUBLIC = 0x0405,
1698 TT_MS_LANGID_DANISH_DENMARK = 0x0406,
1699 TT_MS_LANGID_GERMAN_GERMANY = 0x0407,
1700 TT_MS_LANGID_GERMAN_SWITZERLAND = 0x0807,
1701 TT_MS_LANGID_GERMAN_AUSTRIA = 0x0c07,
1702 TT_MS_LANGID_GERMAN_LUXEMBOURG = 0x1007,
1703 TT_MS_LANGID_GERMAN_LIECHTENSTEI = 0x1407,
1704 TT_MS_LANGID_GREEK_GREECE = 0x0408,
1705 TT_MS_LANGID_ENGLISH_GENERAL = 0x0009,
1706 TT_MS_LANGID_ENGLISH_UNITED_STATES = 0x0409,
1707 TT_MS_LANGID_ENGLISH_UNITED_KINGDOM = 0x0809,
1708 TT_MS_LANGID_ENGLISH_AUSTRALIA = 0x0c09,
1709 TT_MS_LANGID_ENGLISH_CANADA = 0x1009,
1710 TT_MS_LANGID_ENGLISH_NEW_ZEALAND = 0x1409,
1711 TT_MS_LANGID_ENGLISH_IRELAND = 0x1809,
1712 TT_MS_LANGID_ENGLISH_SOUTH_AFRICA = 0x1c09,
1713 TT_MS_LANGID_ENGLISH_JAMAICA = 0x2009,
1714 TT_MS_LANGID_ENGLISH_CARIBBEAN = 0x2409,
1715 TT_MS_LANGID_ENGLISH_BELIZE = 0x2809,
1716 TT_MS_LANGID_ENGLISH_TRINIDAD = 0x2c09,
1717 TT_MS_LANGID_ENGLISH_ZIMBABWE = 0x3009,
1718 TT_MS_LANGID_ENGLISH_PHILIPPINES = 0x3409,
1719 TT_MS_LANGID_ENGLISH_INDONESIA = 0x3809,
1720 TT_MS_LANGID_ENGLISH_HONG_KONG = 0x3c09,
1721 TT_MS_LANGID_ENGLISH_INDIA = 0x4009,
1722 TT_MS_LANGID_ENGLISH_MALAYSIA = 0x4409,
1723 TT_MS_LANGID_ENGLISH_SINGAPORE = 0x4809,
1724 TT_MS_LANGID_SPANISH_SPAIN_TRADITIONAL_SORT = 0x040a,
1725 TT_MS_LANGID_SPANISH_MEXICO = 0x080a,
1726 TT_MS_LANGID_SPANISH_SPAIN_INTERNATIONAL_SORT = 0x0c0a,
1727 TT_MS_LANGID_SPANISH_GUATEMALA = 0x100a,
1728 TT_MS_LANGID_SPANISH_COSTA_RICA = 0x140a,
1729 TT_MS_LANGID_SPANISH_PANAMA = 0x180a,
1730 TT_MS_LANGID_SPANISH_DOMINICAN_REPUBLIC = 0x1c0a,
1731 TT_MS_LANGID_SPANISH_VENEZUELA = 0x200a,
1732 TT_MS_LANGID_SPANISH_COLOMBIA = 0x240a,
1733 TT_MS_LANGID_SPANISH_PERU = 0x280a,
1734 TT_MS_LANGID_SPANISH_ARGENTINA = 0x2c0a,
1735 TT_MS_LANGID_SPANISH_ECUADOR = 0x300a,
1736 TT_MS_LANGID_SPANISH_CHILE = 0x340a,
1737 TT_MS_LANGID_SPANISH_URUGUAY = 0x380a,
1738 TT_MS_LANGID_SPANISH_PARAGUAY = 0x3c0a,
1739 TT_MS_LANGID_SPANISH_BOLIVIA = 0x400a,
1740 TT_MS_LANGID_SPANISH_EL_SALVADOR = 0x440a,
1741 TT_MS_LANGID_SPANISH_HONDURAS = 0x480a,
1742 TT_MS_LANGID_SPANISH_NICARAGUA = 0x4c0a,
1743 TT_MS_LANGID_SPANISH_PUERTO_RICO = 0x500a,
1744 TT_MS_LANGID_SPANISH_UNITED_STATES = 0x540a,
1745 TT_MS_LANGID_SPANISH_LATIN_AMERICA = 0xE40aU,
1746 TT_MS_LANGID_FINNISH_FINLAND = 0x040b,
1747 TT_MS_LANGID_FRENCH_FRANCE = 0x040c,
1748 TT_MS_LANGID_FRENCH_BELGIUM = 0x080c,
1749 TT_MS_LANGID_FRENCH_CANADA = 0x0c0c,
1750 TT_MS_LANGID_FRENCH_SWITZERLAND = 0x100c,
1751 TT_MS_LANGID_FRENCH_LUXEMBOURG = 0x140c,
1752 TT_MS_LANGID_FRENCH_MONACO = 0x180c,
1753 TT_MS_LANGID_FRENCH_WEST_INDIES = 0x1c0c,
1754 TT_MS_LANGID_FRENCH_REUNION = 0x200c,
1755 TT_MS_LANGID_FRENCH_CONGO = 0x240c,
1756 TT_MS_LANGID_FRENCH_ZAIRE = TT_MS_LANGID_FRENCH_CONGO,
1757 TT_MS_LANGID_FRENCH_SENEGAL = 0x280c,
1758 TT_MS_LANGID_FRENCH_CAMEROON = 0x2c0c,
1759 TT_MS_LANGID_FRENCH_COTE_D_IVOIRE = 0x300c,
1760 TT_MS_LANGID_FRENCH_MALI = 0x340c,
1761 TT_MS_LANGID_FRENCH_MOROCCO = 0x380c,
1762 TT_MS_LANGID_FRENCH_HAITI = 0x3c0c,
1763 TT_MS_LANGID_FRENCH_NORTH_AFRICA = 0xE40cU,
1764 TT_MS_LANGID_HEBREW_ISRAEL = 0x040d,
1765 TT_MS_LANGID_HUNGARIAN_HUNGARY = 0x040e,
1766 TT_MS_LANGID_ICELANDIC_ICELAND = 0x040f,
1767 TT_MS_LANGID_ITALIAN_ITALY = 0x0410,
1768 TT_MS_LANGID_ITALIAN_SWITZERLAND = 0x0810,
1769 TT_MS_LANGID_JAPANESE_JAPAN = 0x0411,
1770 TT_MS_LANGID_KOREAN_EXTENDED_WANSUNG_KOREA = 0x0412,
1771 TT_MS_LANGID_KOREAN_JOHAB_KOREA = 0x0812,
1772 TT_MS_LANGID_DUTCH_NETHERLANDS = 0x0413,
1773 TT_MS_LANGID_DUTCH_BELGIUM = 0x0813,
1774 TT_MS_LANGID_NORWEGIAN_NORWAY_BOKMAL = 0x0414,
1775 TT_MS_LANGID_NORWEGIAN_NORWAY_NYNORSK = 0x0814,
1776 TT_MS_LANGID_POLISH_POLAND = 0x0415,
1777 TT_MS_LANGID_PORTUGUESE_BRAZIL = 0x0416,
1778 TT_MS_LANGID_PORTUGUESE_PORTUGAL = 0x0816,
1779 TT_MS_LANGID_RHAETO_ROMANIC_SWITZERLAND = 0x0417,
1780 TT_MS_LANGID_ROMANIAN_ROMANIA = 0x0418,
1781 TT_MS_LANGID_MOLDAVIAN_MOLDAVIA = 0x0818,
1782 TT_MS_LANGID_RUSSIAN_RUSSIA = 0x0419,
1783 TT_MS_LANGID_RUSSIAN_MOLDAVIA = 0x0819,
1784 TT_MS_LANGID_CROATIAN_CROATIA = 0x041a,
1785 TT_MS_LANGID_SERBIAN_SERBIA_LATIN = 0x081a,
1786 TT_MS_LANGID_SERBIAN_SERBIA_CYRILLIC = 0x0c1a,
1787 TT_MS_LANGID_CROATIAN_BOSNIA_HERZEGOVINA = 0x101a,
1788 TT_MS_LANGID_BOSNIAN_BOSNIA_HERZEGOVINA = 0x141a,
1789 TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_LATIN = 0x181a,
1790 TT_MS_LANGID_SERBIAN_BOSNIA_HERZ_CYRILLIC = 0x181a,
1791 TT_MS_LANGID_SLOVAK_SLOVAKIA = 0x041b,
1792 TT_MS_LANGID_ALBANIAN_ALBANIA = 0x041c,
1793 TT_MS_LANGID_SWEDISH_SWEDEN = 0x041d,
1794 TT_MS_LANGID_SWEDISH_FINLAND = 0x081d,
1795 TT_MS_LANGID_THAI_THAILAND = 0x041e,
1796 TT_MS_LANGID_TURKISH_TURKEY = 0x041f,
1797 TT_MS_LANGID_URDU_PAKISTAN = 0x0420,
1798 TT_MS_LANGID_URDU_INDIA = 0x0820,
1799 TT_MS_LANGID_INDONESIAN_INDONESIA = 0x0421,
1800 TT_MS_LANGID_UKRAINIAN_UKRAINE = 0x0422,
1801 TT_MS_LANGID_BELARUSIAN_BELARUS = 0x0423,
1802 TT_MS_LANGID_SLOVENE_SLOVENIA = 0x0424,
1803 TT_MS_LANGID_ESTONIAN_ESTONIA = 0x0425,
1804 TT_MS_LANGID_LATVIAN_LATVIA = 0x0426,
1805 TT_MS_LANGID_LITHUANIAN_LITHUANIA = 0x0427,
1806 TT_MS_LANGID_CLASSIC_LITHUANIAN_LITHUANIA = 0x0827,
1807 TT_MS_LANGID_TAJIK_TAJIKISTAN = 0x0428,
1808 TT_MS_LANGID_FARSI_IRAN = 0x0429,
1809 TT_MS_LANGID_VIETNAMESE_VIET_NAM = 0x042a,
1810 TT_MS_LANGID_ARMENIAN_ARMENIA = 0x042b,
1811 TT_MS_LANGID_AZERI_AZERBAIJAN_LATIN = 0x042c,
1812 TT_MS_LANGID_AZERI_AZERBAIJAN_CYRILLIC = 0x082c,
1813 TT_MS_LANGID_BASQUE_SPAIN = 0x042d,
1814 TT_MS_LANGID_SORBIAN_GERMANY = 0x042e,
1815 TT_MS_LANGID_MACEDONIAN_MACEDONIA = 0x042f,
1816 TT_MS_LANGID_SUTU_SOUTH_AFRICA = 0x0430,
1817 TT_MS_LANGID_TSONGA_SOUTH_AFRICA = 0x0431,
1818 TT_MS_LANGID_TSWANA_SOUTH_AFRICA = 0x0432,
1819 TT_MS_LANGID_VENDA_SOUTH_AFRICA = 0x0433,
1820 TT_MS_LANGID_XHOSA_SOUTH_AFRICA = 0x0434,
1821 TT_MS_LANGID_ZULU_SOUTH_AFRICA = 0x0435,
1822 TT_MS_LANGID_AFRIKAANS_SOUTH_AFRICA = 0x0436,
1823 TT_MS_LANGID_GEORGIAN_GEORGIA = 0x0437,
1824 TT_MS_LANGID_FAEROESE_FAEROE_ISLANDS = 0x0438,
1825 TT_MS_LANGID_HINDI_INDIA = 0x0439,
1826 TT_MS_LANGID_MALTESE_MALTA = 0x043a,
1827 TT_MS_LANGID_SAMI_NORTHERN_NORWAY = 0x043b,
1828 TT_MS_LANGID_SAMI_NORTHERN_SWEDEN = 0x083b,
1829 TT_MS_LANGID_SAMI_NORTHERN_FINLAND = 0x0C3b,
1830 TT_MS_LANGID_SAMI_LULE_NORWAY = 0x103b,
1831 TT_MS_LANGID_SAMI_LULE_SWEDEN = 0x143b,
1832 TT_MS_LANGID_SAMI_SOUTHERN_NORWAY = 0x183b,
1833 TT_MS_LANGID_SAMI_SOUTHERN_SWEDEN = 0x1C3b,
1834 TT_MS_LANGID_SAMI_SKOLT_FINLAND = 0x203b,
1835 TT_MS_LANGID_SAMI_INARI_FINLAND = 0x243b,
1836 TT_MS_LANGID_SAAMI_LAPONIA = 0x043b,
1837 TT_MS_LANGID_SCOTTISH_GAELIC_UNITED_KINGDOM = 0x083c,
1838 TT_MS_LANGID_IRISH_GAELIC_IRELAND = 0x043c,
1839 TT_MS_LANGID_YIDDISH_GERMANY = 0x043d,
1840 TT_MS_LANGID_MALAY_MALAYSIA = 0x043e,
1841 TT_MS_LANGID_MALAY_BRUNEI_DARUSSALAM = 0x083e,
1842 TT_MS_LANGID_KAZAK_KAZAKSTAN = 0x043f,
1843 TT_MS_LANGID_KIRGHIZ_KIRGHIZSTAN = 0x0440,
1844 TT_MS_LANGID_KIRGHIZ_KIRGHIZ_REPUBLIC = TT_MS_LANGID_KIRGHIZ_KIRGHIZSTAN,
1845 TT_MS_LANGID_SWAHILI_KENYA = 0x0441,
1846 TT_MS_LANGID_TURKMEN_TURKMENISTAN = 0x0442,
1847 TT_MS_LANGID_UZBEK_UZBEKISTAN_LATIN = 0x0443,
1848 TT_MS_LANGID_UZBEK_UZBEKISTAN_CYRILLIC = 0x0843,
1849 TT_MS_LANGID_TATAR_TATARSTAN = 0x0444,
1850 TT_MS_LANGID_BENGALI_INDIA = 0x0445,
1851 TT_MS_LANGID_BENGALI_BANGLADESH = 0x0845,
1852 TT_MS_LANGID_PUNJABI_INDIA = 0x0446,
1853 TT_MS_LANGID_PUNJABI_ARABIC_PAKISTAN = 0x0846,
1854 TT_MS_LANGID_GUJARATI_INDIA = 0x0447,
1855 TT_MS_LANGID_ORIYA_INDIA = 0x0448,
1856 TT_MS_LANGID_TAMIL_INDIA = 0x0449,
1857 TT_MS_LANGID_TELUGU_INDIA = 0x044a,
1858 TT_MS_LANGID_KANNADA_INDIA = 0x044b,
1859 TT_MS_LANGID_MALAYALAM_INDIA = 0x044c,
1860 TT_MS_LANGID_ASSAMESE_INDIA = 0x044d,
1861 TT_MS_LANGID_MARATHI_INDIA = 0x044e,
1862 TT_MS_LANGID_SANSKRIT_INDIA = 0x044f,
1863 TT_MS_LANGID_MONGOLIAN_MONGOLIA = 0x0450,
1864 TT_MS_LANGID_MONGOLIAN_MONGOLIA_MONGOLIAN = 0x0850,
1865 TT_MS_LANGID_TIBETAN_CHINA = 0x0451,
1866 TT_MS_LANGID_DZONGHKA_BHUTAN = 0x0851,
1867 TT_MS_LANGID_TIBETAN_BHUTAN = TT_MS_LANGID_DZONGHKA_BHUTAN,
1868 TT_MS_LANGID_WELSH_WALES = 0x0452,
1869 TT_MS_LANGID_KHMER_CAMBODIA = 0x0453,
1870 TT_MS_LANGID_LAO_LAOS = 0x0454,
1871 TT_MS_LANGID_BURMESE_MYANMAR = 0x0455,
1872 TT_MS_LANGID_GALICIAN_SPAIN = 0x0456,
1873 TT_MS_LANGID_KONKANI_INDIA = 0x0457,
1874 TT_MS_LANGID_MANIPURI_INDIA = 0x0458,
1875 TT_MS_LANGID_SINDHI_INDIA = 0x0459,
1876 TT_MS_LANGID_SINDHI_PAKISTAN = 0x0859,
1877 TT_MS_LANGID_SYRIAC_SYRIA = 0x045a,
1878 TT_MS_LANGID_SINHALESE_SRI_LANKA = 0x045b,
1879 TT_MS_LANGID_CHEROKEE_UNITED_STATES = 0x045c,
1880 TT_MS_LANGID_INUKTITUT_CANADA = 0x045d,
1881 TT_MS_LANGID_AMHARIC_ETHIOPIA = 0x045e,
1882 TT_MS_LANGID_TAMAZIGHT_MOROCCO = 0x045f,
1883 TT_MS_LANGID_TAMAZIGHT_MOROCCO_LATIN = 0x085f,
1884 TT_MS_LANGID_KASHMIRI_PAKISTAN = 0x0460,
1885 TT_MS_LANGID_KASHMIRI_SASIA = 0x0860,
1886 TT_MS_LANGID_KASHMIRI_INDIA = TT_MS_LANGID_KASHMIRI_SASIA,
1887 TT_MS_LANGID_NEPALI_NEPAL = 0x0461,
1888 TT_MS_LANGID_NEPALI_INDIA = 0x0861,
1889 TT_MS_LANGID_FRISIAN_NETHERLANDS = 0x0462,
1890 TT_MS_LANGID_PASHTO_AFGHANISTAN = 0x0463,
1891 TT_MS_LANGID_FILIPINO_PHILIPPINES = 0x0464,
1892 TT_MS_LANGID_DHIVEHI_MALDIVES = 0x0465,
1893 TT_MS_LANGID_DIVEHI_MALDIVES = TT_MS_LANGID_DHIVEHI_MALDIVES,
1894 TT_MS_LANGID_EDO_NIGERIA = 0x0466,
1895 TT_MS_LANGID_FULFULDE_NIGERIA = 0x0467,
1896 TT_MS_LANGID_HAUSA_NIGERIA = 0x0468,
1897 TT_MS_LANGID_IBIBIO_NIGERIA = 0x0469,
1898 TT_MS_LANGID_YORUBA_NIGERIA = 0x046a,
1899 TT_MS_LANGID_QUECHUA_BOLIVIA = 0x046b,
1900 TT_MS_LANGID_QUECHUA_ECUADOR = 0x086b,
1901 TT_MS_LANGID_QUECHUA_PERU = 0x0c6b,
1902 TT_MS_LANGID_SEPEDI_SOUTH_AFRICA = 0x046c,
1903 TT_MS_LANGID_SOTHO_SOUTHERN_SOUTH_AFRICA = TT_MS_LANGID_SEPEDI_SOUTH_AFRICA,
1904 TT_MS_LANGID_IGBO_NIGERIA = 0x0470,
1905 TT_MS_LANGID_KANURI_NIGERIA = 0x0471,
1906 TT_MS_LANGID_OROMO_ETHIOPIA = 0x0472,
1907 TT_MS_LANGID_TIGRIGNA_ETHIOPIA = 0x0473,
1908 TT_MS_LANGID_TIGRIGNA_ERYTHREA = 0x0873,
1909 TT_MS_LANGID_TIGRIGNA_ERYTREA = TT_MS_LANGID_TIGRIGNA_ERYTHREA,
1910 TT_MS_LANGID_GUARANI_PARAGUAY = 0x0474,
1911 TT_MS_LANGID_HAWAIIAN_UNITED_STATES = 0x0475,
1912 TT_MS_LANGID_LATIN = 0x0476,
1913 TT_MS_LANGID_SOMALI_SOMALIA = 0x0477,
1914 TT_MS_LANGID_YI_CHINA = 0x0478,
1915 TT_MS_LANGID_PAPIAMENTU_NETHERLANDS_ANTILLES = 0x0479,
1916 TT_MS_LANGID_UIGHUR_CHINA = 0x0480,
1917 TT_MS_LANGID_MAORI_NEW_ZEALAND = 0x0481,
1918 }
1919
1920 enum {
1921 TT_NAME_ID_COPYRIGHT = 0,
1922 TT_NAME_ID_FONT_FAMILY = 1,
1923 TT_NAME_ID_FONT_SUBFAMILY = 2,
1924 TT_NAME_ID_UNIQUE_ID = 3,
1925 TT_NAME_ID_FULL_NAME = 4,
1926 TT_NAME_ID_VERSION_STRING = 5,
1927 TT_NAME_ID_PS_NAME = 6,
1928 TT_NAME_ID_TRADEMARK = 7,
1929 TT_NAME_ID_MANUFACTURER = 8,
1930 TT_NAME_ID_DESIGNER = 9,
1931 TT_NAME_ID_DESCRIPTION = 10,
1932 TT_NAME_ID_VENDOR_URL = 11,
1933 TT_NAME_ID_DESIGNER_URL = 12,
1934 TT_NAME_ID_LICENSE = 13,
1935 TT_NAME_ID_LICENSE_URL = 14,
1936 TT_NAME_ID_PREFERRED_FAMILY = 16,
1937 TT_NAME_ID_PREFERRED_SUBFAMILY = 17,
1938 TT_NAME_ID_MAC_FULL_NAME = 18,
1939 TT_NAME_ID_SAMPLE_TEXT = 19,
1940 TT_NAME_ID_CID_FINDFONT_NAME = 20,
1941 TT_NAME_ID_WWS_FAMILY = 21,
1942 TT_NAME_ID_WWS_SUBFAMILY = 22,
1943 }
1944
1945 enum {
1946 TT_UCR_BASIC_LATIN = 1 << 0,
1947 TT_UCR_LATIN1_SUPPLEMENT = 1 << 1,
1948 TT_UCR_LATIN_EXTENDED_A = 1 << 2,
1949 TT_UCR_LATIN_EXTENDED_B = 1 << 3,
1950 TT_UCR_IPA_EXTENSIONS = 1 << 4,
1951 TT_UCR_SPACING_MODIFIER = 1 << 5,
1952 TT_UCR_COMBINING_DIACRITICS = 1 << 6,
1953 TT_UCR_GREEK = 1 << 7,
1954 TT_UCR_COPTIC = 1 << 8,
1955 TT_UCR_CYRILLIC = 1 << 9,
1956 TT_UCR_ARMENIAN = 1 << 10,
1957 TT_UCR_HEBREW = 1 << 11,
1958 TT_UCR_VAI = 1 << 12,
1959 TT_UCR_ARABIC = 1 << 13,
1960 TT_UCR_NKO = 1 << 14,
1961 TT_UCR_DEVANAGARI = 1 << 15,
1962 TT_UCR_BENGALI = 1 << 16,
1963 TT_UCR_GURMUKHI = 1 << 17,
1964 TT_UCR_GUJARATI = 1 << 18,
1965 TT_UCR_ORIYA = 1 << 19,
1966 TT_UCR_TAMIL = 1 << 20,
1967 TT_UCR_TELUGU = 1 << 21,
1968 TT_UCR_KANNADA = 1 << 22,
1969 TT_UCR_MALAYALAM = 1 << 23,
1970 TT_UCR_THAI = 1 << 24,
1971 TT_UCR_LAO = 1 << 25,
1972 TT_UCR_GEORGIAN = 1 << 26,
1973 TT_UCR_BALINESE = 1 << 27,
1974 TT_UCR_HANGUL_JAMO = 1 << 28,
1975 TT_UCR_LATIN_EXTENDED_ADDITIONAL = 1 << 29,
1976 TT_UCR_GREEK_EXTENDED = 1 << 30,
1977 TT_UCR_SUPERSCRIPTS_SUBSCRIPTS = 1 << 0,
1978 TT_UCR_CURRENCY_SYMBOLS = 1 << 1,
1979 TT_UCR_COMBINING_DIACRITICS_SYMB = 1 << 2,
1980 TT_UCR_LETTERLIKE_SYMBOLS = 1 << 3,
1981 TT_UCR_NUMBER_FORMS = 1 << 4,
1982 TT_UCR_ARROWS = 1 << 5,
1983 TT_UCR_MATHEMATICAL_OPERATORS = 1 << 6,
1984 TT_UCR_MISCELLANEOUS_TECHNICAL = 1 << 7,
1985 TT_UCR_CONTROL_PICTURES = 1 << 8,
1986 TT_UCR_OCR = 1 << 9,
1987 TT_UCR_ENCLOSED_ALPHANUMERICS = 1 << 10,
1988 TT_UCR_BOX_DRAWING = 1 << 11,
1989 TT_UCR_BLOCK_ELEMENTS = 1 << 12,
1990 TT_UCR_GEOMETRIC_SHAPES = 1 << 13,
1991 TT_UCR_MISCELLANEOUS_SYMBOLS = 1 << 14,
1992 TT_UCR_DINGBATS = 1 << 15,
1993 TT_UCR_CJK_SYMBOLS = 1 << 16,
1994 TT_UCR_HIRAGANA = 1 << 17,
1995 TT_UCR_KATAKANA = 1 << 18,
1996 TT_UCR_BOPOMOFO = 1 << 19,
1997 TT_UCR_HANGUL_COMPATIBILITY_JAMO = 1 << 20,
1998 TT_UCR_CJK_MISC = 1 << 21,
1999 TT_UCR_ENCLOSED_CJK_LETTERS_MONTHS = 1 << 22,
2000 TT_UCR_CJK_COMPATIBILITY = 1 << 23,
2001 TT_UCR_HANGUL = 1 << 24,
2002 TT_UCR_SURROGATES = 1 << 25,
2003 TT_UCR_NON_PLANE_0 = TT_UCR_SURROGATES,
2004 TT_UCR_PHOENICIAN = 1 << 26,
2005 TT_UCR_CJK_UNIFIED_IDEOGRAPHS = 1 << 27,
2006 TT_UCR_PRIVATE_USE = 1 << 28,
2007 TT_UCR_CJK_COMPATIBILITY_IDEOGRAPHS = 1 << 29,
2008 TT_UCR_ALPHABETIC_PRESENTATION_FORMS = 1 << 30,
2009 TT_UCR_ARABIC_PRESENTATIONS_A = 1 << 31,
2010 TT_UCR_COMBINING_HALF_MARKS = 1 << 0,
2011 TT_UCR_CJK_COMPATIBILITY_FORMS = 1 << 1,
2012 TT_UCR_SMALL_FORM_VARIANTS = 1 << 2,
2013 TT_UCR_ARABIC_PRESENTATIONS_B = 1 << 3,
2014 TT_UCR_HALFWIDTH_FULLWIDTH_FORMS = 1 << 4,
2015 TT_UCR_SPECIALS = 1 << 5,
2016 TT_UCR_TIBETAN = 1 << 6,
2017 TT_UCR_SYRIAC = 1 << 7,
2018 TT_UCR_THAANA = 1 << 8,
2019 TT_UCR_SINHALA = 1 << 9,
2020 TT_UCR_MYANMAR = 1 << 10,
2021 TT_UCR_ETHIOPIC = 1 << 11,
2022 TT_UCR_CHEROKEE = 1 << 12,
2023 TT_UCR_CANADIAN_ABORIGINAL_SYLLABICS = 1 << 13,
2024 TT_UCR_OGHAM = 1 << 14,
2025 TT_UCR_RUNIC = 1 << 15,
2026 TT_UCR_KHMER = 1 << 16,
2027 TT_UCR_MONGOLIAN = 1 << 17,
2028 TT_UCR_BRAILLE = 1 << 18,
2029 TT_UCR_YI = 1 << 19,
2030 TT_UCR_PHILIPPINE = 1 << 20,
2031 TT_UCR_OLD_ITALIC = 1 << 21,
2032 TT_UCR_GOTHIC = 1 << 22,
2033 TT_UCR_DESERET = 1 << 23,
2034 TT_UCR_MUSICAL_SYMBOLS = 1 << 24,
2035 TT_UCR_MATH_ALPHANUMERIC_SYMBOLS = 1 << 25,
2036 TT_UCR_PRIVATE_USE_SUPPLEMENTARY = 1 << 26,
2037 TT_UCR_VARIATION_SELECTORS = 1 << 27,
2038 TT_UCR_TAGS = 1 << 28,
2039 TT_UCR_LIMBU = 1 << 29,
2040 TT_UCR_TAI_LE = 1 << 30,
2041 TT_UCR_NEW_TAI_LUE = 1 << 31,
2042 TT_UCR_BUGINESE = 1 << 0,
2043 TT_UCR_GLAGOLITIC = 1 << 1,
2044 TT_UCR_TIFINAGH = 1 << 2,
2045 TT_UCR_YIJING = 1 << 3,
2046 TT_UCR_SYLOTI_NAGRI = 1 << 4,
2047 TT_UCR_LINEAR_B = 1 << 5,
2048 TT_UCR_ANCIENT_GREEK_NUMBERS = 1 << 6,
2049 TT_UCR_UGARITIC = 1 << 7,
2050 TT_UCR_OLD_PERSIAN = 1 << 8,
2051 TT_UCR_SHAVIAN = 1 << 9,
2052 TT_UCR_OSMANYA = 1 << 10,
2053 TT_UCR_CYPRIOT_SYLLABARY = 1 << 11,
2054 TT_UCR_KHAROSHTHI = 1 << 12,
2055 TT_UCR_TAI_XUAN_JING = 1 << 13,
2056 TT_UCR_CUNEIFORM = 1 << 14,
2057 TT_UCR_COUNTING_ROD_NUMERALS = 1 << 15,
2058 TT_UCR_SUNDANESE = 1 << 16,
2059 TT_UCR_LEPCHA = 1 << 17,
2060 TT_UCR_OL_CHIKI = 1 << 18,
2061 TT_UCR_SAURASHTRA = 1 << 19,
2062 TT_UCR_KAYAH_LI = 1 << 20,
2063 TT_UCR_REJANG = 1 << 21,
2064 TT_UCR_CHAM = 1 << 22,
2065 TT_UCR_ANCIENT_SYMBOLS = 1 << 23,
2066 TT_UCR_PHAISTOS_DISC = 1 << 24,
2067 TT_UCR_OLD_ANATOLIAN = 1 << 25,
2068 TT_UCR_GAME_TILES = 1 << 26,
2069 }
2070
2071 // tttables.h
2072 struct TT_Header {
2073 FT_Fixed Table_Version;
2074 FT_Fixed Font_Revision;
2075 FT_Long CheckSum_Adjust;
2076 FT_Long Magic_Number;
2077 FT_UShort Flags;
2078 FT_UShort Units_Per_EM;
2079 FT_Long[2] Created;
2080 FT_Long[2] Modified;
2081 FT_Short xMin;
2082 FT_Short yMin;
2083 FT_Short xMax;
2084 FT_Short yMax;
2085 FT_UShort Mac_Style;
2086 FT_UShort Lowest_Rec_PPEM;
2087 FT_Short Font_Direction;
2088 FT_Short Index_To_Loc_Format;
2089 FT_Short Glyph_Data_Format;
2090 }
2091
2092 struct TT_HoriHeader {
2093 FT_Fixed Version;
2094 FT_Short Ascender;
2095 FT_Short Descender;
2096 FT_Short Line_Gap;
2097 FT_UShort advance_Width_Max;
2098 FT_Short min_Left_Side_Bearing;
2099 FT_Short min_Right_Side_Bearing;
2100 FT_Short xMax_Extent;
2101 FT_Short caret_Slope_Rise;
2102 FT_Short caret_Slope_Run;
2103 FT_Short caret_Offset;
2104 FT_Short[4] Reserved;
2105 FT_Short metric_Data_Format;
2106 FT_UShort number_Of_HMetrics;
2107 void* long_metrics;
2108 void* short_metrics;
2109 }
2110
2111 struct TT_VertHeader {
2112 FT_Fixed Version;
2113 FT_Short Ascender;
2114 FT_Short Descender;
2115 FT_Short Line_Gap;
2116 FT_UShort advance_Height_Max;
2117 FT_Short min_Top_Side_Bearing;
2118 FT_Short min_Bottom_Side_Bearing;
2119 FT_Short yMax_Extent;
2120 FT_Short caret_Slope_Rise;
2121 FT_Short caret_Slope_Run;
2122 FT_Short caret_Offset;
2123 FT_Short[4] Reserved;
2124 FT_Short metric_Data_Format;
2125 FT_UShort number_Of_VMetrics;
2126 void* long_metrics;
2127 void* short_metrics;
2128 }
2129
2130 struct TT_OS2 {
2131 FT_UShort _version;
2132 FT_Short xAvgCharWidth;
2133 FT_UShort usWeightClass;
2134 FT_UShort usWidthClass;
2135 FT_UShort fsType;
2136 FT_Short ySubscriptXSize;
2137 FT_Short ySubscriptYSize;
2138 FT_Short ySubscriptXOffset;
2139 FT_Short ySubscriptYOffset;
2140 FT_Short ySuperscriptXSize;
2141 FT_Short ySuperscriptYSize;
2142 FT_Short ySuperscriptXOffset;
2143 FT_Short ySuperscriptYOffset;
2144 FT_Short yStrikeoutSize;
2145 FT_Short yStrikeoutPosition;
2146 FT_Short sFamilyClass;
2147 FT_Byte[10] panose;
2148 FT_ULong ulUnicodeRange1;
2149 FT_ULong ulUnicodeRange2;
2150 FT_ULong ulUnicodeRange3;
2151 FT_ULong ulUnicodeRange4;
2152 FT_Char[4] achVendID;
2153 FT_UShort fsSelection;
2154 FT_UShort usFirstCharIndex;
2155 FT_UShort usLastCharIndex;
2156 FT_Short sTypoAscender;
2157 FT_Short sTypoDescender;
2158 FT_Short sTypoLineGap;
2159 FT_UShort usWinAscent;
2160 FT_UShort usWinDescent;
2161 FT_ULong ulCodePageRange1;
2162 FT_ULong ulCodePageRange2;
2163 FT_Short sxHeight;
2164 FT_Short sCapHeight;
2165 FT_UShort usDefaultChar;
2166 FT_UShort usBreakChar;
2167 FT_UShort usMaxContext;
2168 FT_UShort usLowerOpticalPointSize;
2169 FT_UShort usUpperOpticalPointSize;
2170 }
2171
2172 struct TT_Postscript {
2173 FT_Fixed FormatType;
2174 FT_Fixed italicAngle;
2175 FT_Short underlinePosition;
2176 FT_Short underlineThickness;
2177 FT_ULong isFixedPitch;
2178 FT_ULong minMemType42;
2179 FT_ULong maxMemType42;
2180 FT_ULong minMemType1;
2181 FT_ULong maxMemType1;
2182 }
2183
2184 struct TT_PCLT {
2185 FT_Fixed Version;
2186 FT_ULong FontNumber;
2187 FT_UShort Pitch;
2188 FT_UShort xHeight;
2189 FT_UShort Style;
2190 FT_UShort TypeFamily;
2191 FT_UShort CapHeight;
2192 FT_UShort SymbolSet;
2193 FT_Char[16] TypeFace;
2194 FT_Char[8] CharacterComplement;
2195 FT_Char[6] FileName;
2196 FT_Char StrokeWeight;
2197 FT_Char WidthType;
2198 FT_Byte SerifStyle;
2199 FT_Byte Reserved;
2200 }
2201
2202 struct TT_MaxProfile {
2203 FT_Fixed _version;
2204 FT_UShort numGlyphs;
2205 FT_UShort maxPoints;
2206 FT_UShort maxContours;
2207 FT_UShort maxCompositePoints;
2208 FT_UShort maxCompositeContours;
2209 FT_UShort maxZones;
2210 FT_UShort maxTwilightPoints;
2211 FT_UShort maxStorage;
2212 FT_UShort maxFunctionDefs;
2213 FT_UShort maxInstructionDefs;
2214 FT_UShort maxStackElements;
2215 FT_UShort maxSizeOfInstructions;
2216 FT_UShort maxComponentElements;
2217 FT_UShort maxComponentDepth;
2218 }
2219
2220 alias FT_Sfnt_Tag = int;
2221 enum {
2222 FT_SFNT_HEAD,
2223 FT_SFNT_MAXP,
2224 FT_SFNT_OS2,
2225 FT_SFNT_HHEA,
2226 FT_SFNT_VHEA,
2227 FT_SFNT_POST,
2228 FT_SFNT_PCLT,
2229 FT_SFNT_MAX
2230 }
+0
-78
src/asgen/bindings/gdkpixbuf.d less more
0 /*
1 * Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.bindings.gdkpixbuf;
20
21 import glib.c.types;
22 import gio.c.types;
23 import asgen.bindings.cairo;
24
25 @nogc nothrow
26 extern(C) {
27
28 enum GdkInterpType {
29 NEAREST,
30 TILES,
31 BILINEAR,
32 HYPER
33 }
34
35 struct _GdkPixbuf {}
36 alias GdkPixbuf = _GdkPixbuf*;
37
38 GdkPixbuf gdk_pixbuf_new_from_file (const(char) *filename, GError **error);
39 GdkPixbuf gdk_pixbuf_new_from_stream (GInputStream *stream, GCancellable *cancellable, GError **error);
40
41 int gdk_pixbuf_get_width (GdkPixbuf pixbuf);
42 int gdk_pixbuf_get_height (GdkPixbuf pixbuf);
43
44 GdkPixbuf gdk_pixbuf_scale_simple (const(GdkPixbuf) src, int dest_width, int dest_height, GdkInterpType interp_type);
45
46 bool gdk_pixbuf_save_to_buffer (GdkPixbuf pixbuf, char **buffer, size_t *buffer_size, const(char) *type, GError **error, ...);
47 bool gdk_pixbuf_save (GdkPixbuf pixbuf, const(char) *filename, const(char) *type, GError **error, ...);
48
49 private GSList* gdk_pixbuf_get_formats ();
50 private const(char) *gdk_pixbuf_format_get_name (void *);
51
52 } // end of extern:C
53
54 /**
55 * Get a set of image format names GdkPixbuf
56 * currently supports.
57 */
58 public auto gdkPixbufGetFormatNames () @trusted
59 {
60 import glib.Str : Str;
61 import glib.c.functions : g_slist_free;
62
63 bool[string] res;
64 auto fmList = gdk_pixbuf_get_formats ();
65 if(fmList is null)
66 return res;
67
68 auto list = fmList;
69 while (list !is null) {
70 immutable formatName = Str.toString(cast(char*) gdk_pixbuf_format_get_name (list.data));
71 res[formatName] = true;
72 list = list.next;
73 }
74
75 g_slist_free (fmList);
76 return res;
77 }
4444 enum {
4545 MDB_VERSION_MAJOR = 0,
4646 MDB_VERSION_MINOR = 9,
47 MDB_VERSION_PATCH = 18,
48 MDB_VERSION_DATE = "December 19, 2015",
47 MDB_VERSION_PATCH = 27,
48 MDB_VERSION_DATE = "October 26, 2020",
4949 }
5050
5151 struct MDB_env_s {}
+0
-29
src/asgen/bindings/pango.d less more
0 /*
1 * Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.bindings.pango;
20
21 extern(C):
22 nothrow:
23 @nogc:
24
25 struct PangoLanguage;
26
27 PangoLanguage *pango_language_from_string (const(char) *language);
28 const(char) *pango_language_get_sample_string (PangoLanguage *language);
+0
-46
src/asgen/bindings/rsvg.d less more
0 /*
1 * Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.bindings.rsvg;
20
21 import glib.c.types;
22 import asgen.bindings.cairo;
23
24 extern(C):
25 nothrow:
26 @nogc:
27
28 struct _RsvgHandle;
29 alias RsvgHandle = _RsvgHandle*;
30
31 struct RsvgDimensionData {
32 int width;
33 int height;
34 double em;
35 double ex;
36 }
37
38 RsvgHandle rsvg_handle_new ();
39 void g_object_unref (void* object);
40
41 bool rsvg_handle_write (RsvgHandle handle, const(ubyte) *buf, long count, GError **error);
42 bool rsvg_handle_close (RsvgHandle handle, GError **error);
43 void rsvg_handle_get_dimensions (RsvgHandle handle, RsvgDimensionData *dimension_data);
44
45 bool rsvg_handle_render_cairo (RsvgHandle handle, cairo_p cr);
00 /*
1 * Copyright (C) 2016-2020 Matthias Klumpp <matthias@tenstral.net>
1 * Copyright (C) 2016-2021 Matthias Klumpp <matthias@tenstral.net>
22 *
33 * Licensed under the GNU Lesser General Public License Version 3
44 *
2121 import std.stdio;
2222 import std.array;
2323 import std.string : format, toLower;
24 import std.path : dirName, buildPath, buildNormalizedPath;
24 import std.path : dirName, buildPath, buildNormalizedPath, isAbsolute, absolutePath;
2525 import std.conv : to;
2626 import std.json;
2727 import std.typecons;
2828 import std.file : getcwd, thisExePath, exists;
2929
30 import ascompose.Globals : Globals;
3031 public import appstream.c.types : FormatVersion;
3132
3233 import asgen.utils : existsAndIsDir, randomString, ImageSize;
8990 bool processGStreamer;
9091 bool processLocale;
9192 bool screenshotVideos;
92 bool propagateMetainfoArtifacts;
93 bool propagateMetaInfoArtifacts;
94 bool warnNoMetaInfo;
9395 }
9496
9597 /// Fake package name AppStream Generator uses internally to inject additional metainfo on users' request
149151 // find all the external binaries we (may) need
150152 // we search for them unconditionally, because the unittests may rely on their absolute
151153 // paths being set even if a particular feature flag that requires them isn't.
152 optipngBinary = Util.findProgramInPath ("optipng");
154 optipngBinary = Globals.optipngBinary;
153155 ffprobeBinary = Util.findProgramInPath ("ffprobe");
154156 }
155157
200202 @property
201203 string formatVersionStr ()
202204 {
203 import asgen.bindings.appstream_utils : as_format_version_to_string;
204 import std.string : fromStringz;
205
206 auto ver = fromStringz (as_format_version_to_string (formatVersion));
207 return ver.to!string;
205 static import appstream.Utils;
206 alias AsUtils = appstream.Utils.Utils;
207 return AsUtils.formatVersionToString (formatVersion);
208208 }
209209
210210 @property
226226 auto tdir = buildPath (workspaceDir, "templates");
227227 tdir = getVendorTemplateDir (tdir, true);
228228
229 if (tdir is null) {
229 if (tdir.empty) {
230230 immutable exeDir = dirName (thisExePath ());
231231 tdir = buildNormalizedPath (exeDir, "..", "..", "..", "data", "templates");
232232 tdir = getVendorTemplateDir (tdir);
233233
234 if (tdir is null) {
234 if (tdir.empty) {
235235 tdir = getVendorTemplateDir (buildPath (DATADIR, "templates"));
236236
237 if (tdir is null) {
237 if (tdir.empty) {
238238 tdir = buildNormalizedPath (exeDir, "..", "data", "templates");
239239 tdir = getVendorTemplateDir (tdir);
240240 }
250250 private string getVendorTemplateDir (const string dir, bool allowRoot = false) @safe
251251 {
252252 string tdir;
253 if (projectName !is null) {
254 tdir = buildPath (dir, projectName.toLower ());
253 if (!projectName.empty) {
254 tdir = buildPath (dir, projectName.toLower);
255255 if (existsAndIsDir (tdir))
256256 return tdir;
257257 }
266266 return null;
267267 }
268268
269 void loadFromFile (string fname, string enforcedWorkspaceDir = null)
270 {
271 import bindings.gdkpixbuf : gdkPixbufGetFormatNames;
272
269 void loadFromFile (string fname, string enforcedWorkspaceDir = null, string enforcedExportDir = null)
270 {
273271 // read the configuration JSON file
274272 auto f = File (fname, "r");
275273 string jsonData;
290288 // allow overriding the workspace location
291289 if (!enforcedWorkspaceDir.empty)
292290 workspaceDir = enforcedWorkspaceDir;
291 if (!workspaceDir.isAbsolute)
292 workspaceDir = workspaceDir.absolutePath;
293293
294294 this.projectName = "Unknown";
295295 if ("ProjectName" in root)
305305 if ("HtmlBaseUrl" in root)
306306 this.htmlBaseUrl = root["HtmlBaseUrl"].str;
307307
308 // set root export directory
309 if (enforcedExportDir.empty) {
310 exportDir = buildPath (workspaceDir, "export");
311 } else {
312 exportDir = enforcedExportDir;
313 logInfo ("Using data export directory root from the command-line: %s", exportDir);
314 }
315 if (!exportDir.isAbsolute)
316 exportDir = exportDir.absolutePath;
317
308318 // set the default export directory locations, allow people to override them in the config
309 exportDir = buildPath (workspaceDir, "export");
310 mediaExportDir = buildPath (exportDir, "media");
311 dataExportDir = buildPath (exportDir, "data");
312 hintsExportDir = buildPath (exportDir, "hints");
313 htmlExportDir = buildPath (exportDir, "html");
314 auto extraMetainfoDir = buildPath (workspaceDir, "extra-metainfo");
319 // (we convert the relative to absolute paths later)
320 mediaExportDir = "media";
321 dataExportDir = "data";
322 hintsExportDir = "hints";
323 htmlExportDir = "html";
315324
316325 if ("ExportDirs" in root) {
317326 auto edirs = root["ExportDirs"].object;
335344 }
336345 }
337346
347 // convert export directory paths to absolute paths if necessary
348 mediaExportDir = mediaExportDir.isAbsolute? mediaExportDir : buildNormalizedPath (exportDir, mediaExportDir);
349 dataExportDir = dataExportDir.isAbsolute? dataExportDir : buildNormalizedPath (exportDir, dataExportDir);
350 hintsExportDir = hintsExportDir.isAbsolute? hintsExportDir : buildNormalizedPath (exportDir, hintsExportDir);
351 htmlExportDir = htmlExportDir.isAbsolute? htmlExportDir : buildNormalizedPath (exportDir, htmlExportDir);
352
338353 // a place where external metainfo data can be injected
354 auto extraMetainfoDir = buildPath (workspaceDir, "extra-metainfo");
339355 if ("ExtraMetainfoDir" in root)
340356 extraMetainfoDir = root["ExtraMetainfoDir"].str;
341357
538554 feature.processGStreamer = true;
539555 feature.processLocale = true;
540556 feature.screenshotVideos = true;
557 feature.warnNoMetaInfo = true;
541558
542559 // apply vendor feature settings
543560 if ("Features" in root.object) {
580597 case "screenshotVideos":
581598 feature.screenshotVideos = featuresObj[featureId].type == JSONType.true_;
582599 break;
583 case "propagateMetainfoArtifacts":
584 feature.propagateMetainfoArtifacts = featuresObj[featureId].type == JSONType.true_;
600 case "propagateMetaInfoArtifacts":
601 feature.propagateMetaInfoArtifacts = featuresObj[featureId].type == JSONType.true_;
602 break;
603 case "warnNoMetaInfo":
604 feature.warnNoMetaInfo = featuresObj[featureId].type == JSONType.true_;
585605 break;
586606 default:
587607 break;
590610 }
591611
592612 // check if we need to disable features because some prerequisites are not met
613 Globals.setUseOptipng (feature.optipng);
593614 if (feature.optipng) {
594615 if (optipngBinary.empty) {
595616 feature.optipng = false;
621642 }
622643
623644 if (!feature.validate)
624 logWarning ("Metainfo validation has been disabled in configuration.");
645 logWarning ("MetaInfo validation has been disabled in configuration.");
625646
626647 // sanity check to warn if our GdkPixbuf does not support the minimum amount
627648 // of image formats we need
628 const pixbufFormatNames = gdkPixbufGetFormatNames ();
629 if (("png" !in pixbufFormatNames) || ("svg" !in pixbufFormatNames) || ("jpeg" !in pixbufFormatNames)) {
649 import ascompose.Image : Image;
650 import std.string : toStringz;
651 auto pbFormatNames = Image.supportedFormatNames ();
652 if (!pbFormatNames.contains(cast(char*) "png".toStringz) ||
653 !pbFormatNames.contains(cast(char*) "svg".toStringz) ||
654 !pbFormatNames.contains(cast(char*) "jpeg".toStringz)) {
630655 logError ("The currently used GdkPixbuf does not seem to support all image formats we require to run normally (png/svg/jpeg). " ~
631656 "This may be a problem with your installation of appstream-generator or gdk-pixbuf.");
632657 }
642667 */
643668 string getTmpDir ()
644669 {
645 if (tmpDir.empty) {
646 synchronized (this) {
670 synchronized (this) {
671 if (tmpDir.empty) {
647672 string root;
648673 if (cacheRootDir.empty)
649674 root = "/tmp/";
651676 root = cacheRootDir;
652677
653678 tmpDir = buildPath (root, "tmp", format ("asgen-%s", randomString (8)));
654 }
655 }
656
679
680 // make appstream-compose internal functions aware of the new temp dir
681 Globals.setTmpDir (tmpDir);
682 }
683 }
657684 return tmpDir;
658685 }
659686 }
5555
5656 ~this ()
5757 {
58 if (opened)
59 dbEnv.mdb_env_close ();
58 close ();
6059 }
6160
6261 private void checkError (int rc, string msg)
9897
9998 // open database
10099 rc = dbEnv.mdb_env_open (dir.toStringz (),
101 MDB_NOMETASYNC | MDB_NOTLS,
100 MDB_NOMETASYNC,
102101 octal!755);
103102 checkError (rc, "mdb_env_open");
104103
132131 this.open (buildPath (conf.databaseDir, "contents"));
133132 }
134133
135 private MDB_val makeDbValue (string data)
134 void close ()
135 {
136 synchronized (this) {
137 if (opened)
138 dbEnv.mdb_env_close ();
139 opened = false;
140 dbEnv = null;
141 }
142 }
143
144 private MDB_val makeDbValue (const string data)
136145 {
137146 import core.stdc.string : strlen;
138147 MDB_val mval;
143152 }
144153
145154 private MDB_txnp newTransaction (uint flags = 0)
155 in { assert (opened); }
156 do
146157 {
147158 int rc;
148159 MDB_txnp txn;
191202 checkError (res, "mdb_del (locale)");
192203 }
193204
194 bool packageExists (string pkid)
205 bool packageExists (const string pkid)
195206 {
196207 MDB_val dkey;
197208 MDB_cursorp cur;
301312 auto data = fromStringz (cast(char*) cval.mv_data);
302313 auto contents = to!string (data);
303314
304 foreach (ref c; contents.split ("\n")) {
315 foreach (const ref c; contents.split ("\n")) {
305316 if (useBaseName)
306317 pkgCMap[c.baseName] = pkid;
307318 else
413424 }
414425
415426 void sync ()
427 in { assert (opened); }
428 do
416429 {
417430 dbEnv.mdb_env_sync (1);
418431 }
8080
8181 ~this ()
8282 {
83 if (opened)
84 dbEnv.mdb_env_close ();
83 close ();
8584 }
8685
8786 @property
111110 static import std.math;
112111
113112 int rc;
114 assert (opened == false);
113 assert (!opened);
115114
116115 // add LMDB version we are using to the debug output
117116 printVersionDbg ();
138137
139138 // open database
140139 rc = dbEnv.mdb_env_open (dir.toStringz (),
141 MDB_NOMETASYNC | MDB_NOTLS,
140 MDB_NOMETASYNC,
142141 octal!755);
143142 checkError (rc, "mdb_env_open");
144143
171170
172171 this.mediaDir = buildPath (mediaBaseDir, "pool");
173172 mkdirRecurse (this.mediaDir);
173 }
174
175 void close ()
176 {
177 synchronized (this) {
178 if (opened)
179 dbEnv.mdb_env_close ();
180 opened = false;
181 dbEnv = null;
182 }
174183 }
175184
176185 void open (Config conf)
189198 }
190199
191200 private MDB_txnp newTransaction (uint flags = 0)
201 in { assert (opened); }
202 do
192203 {
193204 int rc;
194205 MDB_txnp txn;
321332 {
322333 // if the package has no components or hints,
323334 // mark it as always-ignore
324 if (gres.packageIsIgnored ()) {
335 if (gres.unitIgnored) {
325336 setPackageIgnore (gres.pkid);
326337 return;
327338 }
328339
329 foreach (ref cpt; gres.getComponents ()) {
340 auto cptsPtrArray = gres.fetchComponents ();
341 for (uint i = 0; i < cptsPtrArray.len; i++) {
342 auto cpt = new Component (cast (AsComponent*) cptsPtrArray.index (i));
330343 auto gcid = gres.gcidForComponent (cpt);
331344 if (metadataExists (dtype, gcid) && !alwaysRegenerate) {
332345 // we already have seen this exact metadata - only adjust the reference,
346359 data = mdata.componentsToCollection (FormatKind.YAML);
347360 }
348361 } catch (Exception e) {
349 gres.addHint (cpt.getId (), "metadata-serialization-failed", e.msg);
362 gres.addHint (cpt, "metadata-serialization-failed", e.msg);
350363 continue;
351364 }
352365 // remove trailing whitespaces and linebreaks
363376 setHints (gres.pkid, hintsJson);
364377 }
365378
366 auto gcids = gres.getGCIDs ();
379 auto gcids = gres.getComponentGcids ();
367380 if (gcids.empty) {
368381 // no global components, and we're not ignoring this component.
369382 // this means we likely have hints stored for this one. Mark it
373386 import std.array : join;
374387 // store global component IDs for this package as newline-separated list
375388 auto gcidVal = join (gcids, "\n");
376
377389 putKeyValue (dbPackages, gres.pkid, gcidVal);
378390 }
379391 }
186186 scope(exit) g_object_unref (stream);
187187
188188 ptrdiff_t len;
189 ubyte[GENERIC_BUFFER_SIZE] buffer;
189190 do {
190 ubyte[GENERIC_BUFFER_SIZE] buffer;
191
192191 len = g_input_stream_read (stream, cast(void*)buffer.ptr, cast(size_t)buffer.length, null, null);
193192 if (len > 0)
194193 dFile.rawWrite (buffer[0..len]);
205204
206205 auto result = appender!(ubyte[]);
207206 ptrdiff_t len;
207 ubyte[GENERIC_BUFFER_SIZE] buffer;
208208 do {
209 ubyte[GENERIC_BUFFER_SIZE] buffer;
210
211209 len = g_input_stream_read (stream, cast(void*)buffer.ptr, cast(size_t)buffer.length, null, null);
212 result ~= buffer[0..len];
210 if (len > 0)
211 result ~= buffer[0..len];
213212 } while (len > 0);
214213
215214 return result.data;
281280 import std.stdio : writeln;
282281 import std.exception : assertThrown;
283282 import std.file : remove, readText;
283 import std.process : environment;
284284 asgen.logging.setVerbose (true);
285285
286286 writeln ("TEST: ", "Downloader");
287287
288 if (environment.get("ASGEN_TESTS_NO_NET", "no") != "no") {
289 writeln ("I: NETWORK DEPENDENT TESTS SKIPPED. (explicitly disabled via `ASGEN_TESTS_NO_NET`)");
290 return;
291 }
292
288293 immutable urlFirefoxDetectportal = "https://detectportal.firefox.com/";
289
290294 auto dl = new Downloader;
291295 string detectPortalRes;
292296 try {
293297 detectPortalRes = dl.downloadText (urlFirefoxDetectportal);
294298 } catch (Exception e) {
295 writeln ("I: NETWORK DEPENDENT TESTS SKIPPED. (", e.msg, ")");
299 writeln ("W: NETWORK DEPENDENT TESTS SKIPPED. (automatically, no network detected: ", e.msg, ")");
296300 return;
297301 }
298302 writeln ("I: Running network-dependent tests.");
9696 throw new Exception ("No backend specified, can not continue!");
9797 }
9898
99 // load global registry of issue hint templates
100 loadHintsRegistry ();
101
99102 // create cache in cache directory on workspace
100103 dstore = new DataStore ();
101104 dstore.open (conf);
103106 // open package contents cache
104107 cstore = new ContentsStore ();
105108 cstore.open (conf);
106
107 // for Cairo/Fontconfig issues with multithreading
108 import asgen.image : setupFontconfigMutex;
109 if (conf.feature.processFonts)
110 setupFontconfigMutex ();
111109 }
112110
113111 @property
132130 private void logVersionInfo ()
133131 {
134132 import asgen.defines : ASGEN_VERSION;
135 import asgen.bindings.appstream_utils : as_get_appstream_version;
136 import std.string : fromStringz;
133 static import appstream.Utils;
134 alias AsUtils = appstream.Utils.Utils;
137135
138136 string backendInfo = "";
139137 if (!conf.backendName.empty)
140138 backendInfo = " [%s]".format (conf.backendName);
141 logInfo ("AppStream Generator %s, AS: %s%s", ASGEN_VERSION, as_get_appstream_version.fromStringz, backendInfo);
139 logInfo ("AppStream Generator %s, AS: %s%s", ASGEN_VERSION, AsUtils.appstreamVersion, backendInfo);
142140 }
143141
144142 /**
147145 */
148146 private void processPackages (ref Package[] pkgs, IconHandler iconh)
149147 {
150 auto localeh = new LocaleHandler (pkgs);
148 auto localeh = new LocaleHandler (cstore, pkgs);
151149 auto mde = new DataExtractor (dstore, iconh, localeh);
152150 foreach (ref pkg; parallel (pkgs)) {
153151 immutable pkid = pkg.id;
489487 // save a copy of the hints registry to be used by other tools
490488 // (this allows other apps to just resolve the hint tags to severities and explanations
491489 // without loading either AppStream or AppStream-Generator code)
492 auto hintTagRegistry = HintTagRegistry.get ();
493 hintTagRegistry.saveToFile (buildPath (conf.hintsExportDir, suite.name, "hint-definitions.json"));
490 saveHintsRegistryToJsonFile (buildPath (conf.hintsExportDir, suite.name, "hint-definitions.json"));
494491 }
495492
496493 private HashMap!(string, Package) getIconCandidatePackages (Suite suite, string section, string arch)
548545 cpt.setMergeKind (MergeKind.REMOVE_COMPONENT);
549546 cpt.setId (cid);
550547
551 gres.addComponent (cpt, metainfoDir ~ "/-" ~ cid);
548 gres.addComponentWithString (cpt, metainfoDir ~ "/-" ~ cid);
552549 }
553550 }
554551
561558 import std.stdio : File;
562559 import std.path : baseName;
563560 import std.array : replace;
564 import asgen.handlers.metainfoparser : parseMetaInfoData;
565 import asgen.handlers.metainfovalidator : validateMetaInfoFile;
561 import ascompose.MetaInfoUtils : MetaInfoUtils;
562 import asgen.utils : toStaticGBytes;
566563 import asgen.handlers.screenshothandler : processScreenshots;
567564
568565 foreach (miFname; metainfoDir.dirEntries ("*.xml", SpanMode.shallow, false)) {
578575 // we want some replacement logic for arch-specific injected metainfo files,
579576 // so arch-specific xml files can replace generic ones. To archieve that we assume
580577 // the metainfo file is named after the component-ID it contains and do some cheap replacement here.
581 gres.dropComponent (miBasename.replace (".metainfo.xml", ""));
582
583 auto cpt = parseMetaInfoData (gres, data.data, miBasename);
578 gres.removeComponentById (miBasename.replace (".metainfo.xml", ""));
579
580 auto dataBytes = data.data.toStaticGBytes;
581
582 auto cpt = MetaInfoUtils.parseMetainfoDataSimple (gres, dataBytes, miBasename);
584583 if (cpt is null)
585584 continue;
586585
587586 // validate
588 if (conf.feature.validate)
589 validateMetaInfoFile (gres, cpt, data.data, miFname.baseName);
587 if (conf.feature.validate) {
588 DataExtractor.validateMetaInfoData (gres, cpt, dataBytes, miBasename);
589 }
590590
591591 // get icon
592592 iconh.process (gres, cpt);
666666
667667 // process new packages
668668 auto pkgs = pkgIndex.packagesFor (suite.name, section, arch);
669 auto iconh = new IconHandler (dstore.mediaExportPoolDir,
669 auto iconh = new IconHandler (cstore,
670 dstore.mediaExportPoolDir,
670671 conf.iconSettings,
671672 getIconCandidatePackages (suite, section, arch),
672673 suite.iconTheme);
798799 }
799800
800801 // process new packages
801 auto iconh = new IconHandler (dstore.mediaExportPoolDir,
802 auto iconh = new IconHandler (cstore,
803 dstore.mediaExportPoolDir,
802804 conf.iconSettings,
803805 getIconCandidatePackages (suite, sectionName, arch),
804806 suite.iconTheme);
2626 import std.typecons : scoped;
2727 import appstream.Component;
2828 import appstream.Metadata;
29 import ascompose.Hint : Hint;
30 import ascompose.MetaInfoUtils : MetaInfoUtils;
31 import glib.Bytes : Bytes;
2932
3033 import asgen.containers : HashMap, SList;
3134 import asgen.config;
32 import asgen.hint;
35 import asgen.hintregistry;
3336 import asgen.result;
3437 import asgen.backends.interfaces;
3538 import asgen.datastore;
3639 import asgen.handlers;
37 import asgen.utils : componentGetStockIcon;
40 import asgen.utils : componentGetStockIcon, toStaticGBytes;
3841
3942
4043 final class DataExtractor
4245
4346 private:
4447 Component[] cpts;
45 GeneratorHint[] hints;
48 Hint[] hints;
4649
4750 DataStore dstore;
4851 IconHandler iconh;
6164 dtype = conf.metadataType;
6265 }
6366
67 static void validateMetaInfoData (GeneratorResult gres, Component cpt,
68 Bytes bytes, const string miBasename)
69 {
70 import appstream.Validator : Validator;
71 import glib.c.types : GDestroyNotify;
72
73 // create thread-local validator for efficiency
74 static Validator validator = null;
75 if (validator is null)
76 validator = new Validator;
77
78 MetaInfoUtils.validateMetainfoDataForComponent(gres, validator, cpt,
79 bytes,
80 miBasename);
81 }
82
6483 GeneratorResult processPackage (Package pkg)
6584 {
6685 // create a new result container
91110
92111 // now process metainfo XML files
93112 foreach (ref const mfname; metadataFiles) {
94 auto dataBytes = pkg.getFileData (mfname);
95 if (dataBytes.empty)
96 continue;
97 auto data = cast(string) dataBytes;
113 auto dataBytesRaw = pkg.getFileData (mfname);
114 if (dataBytesRaw.empty)
115 continue;
116 auto dataBytes = dataBytesRaw.toStaticGBytes;
98117
99118 mdata.clearComponents ();
100 auto cpt = parseMetaInfoData (mdata, gres, data, mfname);
119 auto cpt = MetaInfoUtils.parseMetainfoData (gres, mdata, dataBytes, mfname);
101120 if (cpt is null)
102121 continue;
103122
111130
112131 // we need to add the version to re-download screenshot on every new upload.
113132 // otherwise, screenshots would only get updated if the actual metadata file was touched.
114 gres.updateComponentGCID (cpt, pkg.ver);
133 gres.updateComponentGcidWithString (cpt, pkg.ver);
115134
116135 // validate the desktop-id launchables and merge the desktop file data
117136 // in case we find it.
138157 parseDesktopFile (gres, cpt, dfname, ddata, true);
139158
140159 // update GCID checksum
141 gres.updateComponentGCID (cpt, ddata);
160 gres.updateComponentGcid (cpt, ddataBytes.toStaticGBytes);
142161
143162 // drop the .desktop file from the list, it has been handled
144163 desktopFiles.remove (desktopId);
164183 // Otherwise we take the data and see how far we get.
165184
166185 // finalize GCID checksum and continue
167 gres.updateComponentGCID (cpt, data);
186 gres.updateComponentGcid (cpt, dataBytes);
168187
169188 gres.addHint (cpt, "missing-desktop-file");
170189 // we have a desktop-application component, but no .desktop file.
172191 continue;
173192 } else {
174193 // update component with .desktop file data, ignoring NoDisplay field
175 const ddataBytes = pkg.getFileData (dfname);
176 auto ddata = cast(string) ddataBytes;
177 parseDesktopFile (gres, cpt, dfname, ddata, true);
194 const ddataBytesRaw = pkg.getFileData (dfname);
195 auto ddataBytes = ddataBytesRaw.toStaticGBytes;
196 parseDesktopFile (gres, cpt, dfname, cast(string) ddataBytesRaw, true);
178197
179198 // update GCID checksum
180 gres.updateComponentGCID (cpt, ddata);
199 gres.updateComponentGcid (cpt, ddataBytes);
181200
182201 // drop the .desktop file from the list, it has been handled
183202 desktopFiles.remove (cid);
188207 // the user to disable this feature.
189208 if (conf.feature.validate) {
190209 if (!dstore.metadataExists (dtype, gres.gcidForComponent (cpt)))
191 validateMetaInfoFile (gres, cpt, data, mfname.baseName);
210 validateMetaInfoData (gres, cpt, dataBytes, mfname.baseName);
192211 }
193212 }
194213
195214 // process the remaining .desktop files
196215 foreach (ref dfname; desktopFiles.byValue) {
197 auto ddataBytes = pkg.getFileData (dfname);
198 auto ddata = cast(string) ddataBytes;
199 auto cpt = parseDesktopFile (gres, null, dfname, ddata, false);
216 auto ddataBytesRaw = pkg.getFileData (dfname);
217 auto ddataBytes = ddataBytesRaw.toStaticGBytes;
218 auto cpt = parseDesktopFile (gres, null, dfname, cast(string) ddataBytesRaw, false);
200219 if (cpt !is null)
201 gres.updateComponentGCID (cpt, ddata);
220 gres.updateComponentGcid (cpt, ddataBytes);
202221 }
203222
204223 if (conf.feature.processGStreamer && !pkg.gst.isNull && pkg.gst.get.isNotEmpty) {
214233 data ~= desc;
215234 }
216235
217 gres.addComponent (cpt);
218 gres.updateComponentGCID (cpt, data.data);
236 gres.addComponent (cpt, data.data.toStaticGBytes);
219237 }
220238
221239 auto hasFontComponent = false;
222 foreach (ref cpt; gres.getComponents ()) {
240 auto cptsPtrArray = gres.fetchComponents ();
241 for (uint i = 0; i < cptsPtrArray.len; i++) {
242 auto cpt = new Component (cast (AsComponent*) cptsPtrArray.index (i));
223243 auto gcid = gres.gcidForComponent (cpt);
224244
225245 // don't run expensive operations if the metadata already exists
+0
-586
src/asgen/font.d less more
0 /*
1 * Copyright (C) 2016-2018 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this software. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.font;
20
21 import std.string : format, fromStringz, toStringz, toLower, strip, splitLines;
22 import std.conv : to;
23 import std.path : buildPath, baseName;
24 import std.array : empty, appender, replace;
25 import std.algorithm : countUntil, remove;
26 static import std.file;
27
28 import asgen.containers : HashMap;
29 import asgen.bindings.freetype;
30 import asgen.bindings.fontconfig;
31 import asgen.bindings.pango;
32
33 import asgen.logging;
34 import asgen.config : Config;
35
36
37 // NOTE: The font's full-name (and the family-style combo we use if the full name is unavailable), can be
38 // determined on the command-line via:
39 // fc-query --format='FN: %{fullname}\nFS: %{family[0]} %{style[0]}\n' <fontfile>
40
41 // global font icon text lookup table, initialized by the constructor or Font and valid (and in memory)
42 // as long as the generator runs.
43 private static string[string] iconTexts;
44
45 private static string[] englishPangrams = import("pangrams/en.txt").splitLines ();
46
47 /**
48 * Representation of a single font file.
49 */
50 final class Font
51 {
52
53 private:
54
55 FT_Library library;
56 FT_Face fface;
57
58 HashMap!(string, bool) _languages;
59 string _preferredLanguage;
60 string _sampleText;
61 string _sampleIconText;
62
63 string _style;
64 string _fullname;
65
66 string _description;
67 string _designerName;
68 string _homepage;
69
70 immutable string fileBaseName;
71
72 public:
73
74 this (string fname)
75 {
76 _languages.clear ();
77
78 // NOTE: Freetype is completely non-threadsafe, but we only use it in the constructor.
79 // So mark this section of code as synchronized to never run it in parallel (even having
80 // two Font objects constructed in parallel may lead to errors)
81 synchronized {
82 // initialize the global font icon lookup table
83 if (iconTexts.length == 0) {
84 iconTexts = ["en": "Aa",
85 "ar": "أب",
86 "as": "অআই",
87 "bn": "অআই",
88 "be": "Аа",
89 "bg": "Аа",
90 "cs": "Aa",
91 "da": "Aa",
92 "de": "Aa",
93 "es": "Aa",
94 "fr": "Aa",
95 "gu": "અબક",
96 "hi": "अआइ",
97 "he": "אב",
98 "it": "Aa",
99 "kn": "ಅಆಇ",
100 "ml": "ആഇ",
101 "ne": "अआइ",
102 "nl": "Aa",
103 "or": "ଅଆଇ",
104 "pa": "ਅਆਇ",
105 "pl": "ĄĘ",
106 "pt": "Aa",
107 "ru": "Аа",
108 "sv": "Åäö",
109 "ta": "அஆஇ",
110 "te": "అఆఇ",
111 "ua": "Аа",
112 "zh-tw": "漢"];
113 }
114
115 initFreeType ();
116
117 FT_Error err;
118 err = FT_New_Face (library, fname.toStringz (), 0, &fface);
119 if (err != 0)
120 throw new Exception ("Unable to load font face from file. Error code: %s".format (err));
121
122 loadFontConfigData (fname);
123 fileBaseName = fname.baseName;
124 }
125 }
126
127 this (const(ubyte)[] data, string fileBaseName)
128 {
129 import std.stdio : File;
130
131 // we unfortunately need to create a stupid temporary file here, otherwise Fontconfig
132 // does not work and we can not determine the right demo strings for this font.
133 // (FreeType itself could load from memory)
134 immutable tmpRoot = Config.get ().getTmpDir;
135 std.file.mkdirRecurse (tmpRoot);
136 immutable fname = buildPath (tmpRoot, fileBaseName);
137 auto f = File (fname, "w");
138 f.rawWrite (data);
139 f.close ();
140
141 this (fname);
142 }
143
144 ~this ()
145 {
146 release ();
147 }
148
149 void release ()
150 {
151 if (fface !is null)
152 FT_Done_Face (fface);
153 if (library !is null)
154 FT_Done_Library (library);
155
156 fface = null;
157 library = null;
158 }
159
160 private bool ready () const
161 {
162 return fface !is null && library !is null;
163 }
164
165 private void initFreeType ()
166 {
167 library = null;
168 fface = null;
169 FT_Error err;
170
171 err = FT_Init_FreeType (&library);
172 if (err != 0)
173 throw new Exception ("Unable to load FreeType. Error code: %s".format (err));
174 }
175
176 private void loadFontConfigData (string fname)
177 {
178 // open FC font patter
179 // the count pointer has to be valid, otherwise FcFreeTypeQuery() crashes.
180 int c;
181 auto fpattern = FcFreeTypeQuery (fname.toStringz, 0, null, &c);
182 scope (exit) FcPatternDestroy (fpattern);
183
184 // load information about the font
185 _languages.clear ();
186
187 auto anyLangAdded = false;
188 auto match = true;
189 for (uint i = 0; match == true; i++) {
190 FcLangSet *ls;
191
192 match = false;
193 if (FcPatternGetLangSet (fpattern, FC_LANG, i, &ls) == FcResult.Match) {
194 match = true;
195 auto langs = FcLangSetGetLangs (ls);
196 auto list = FcStrListCreate (langs);
197 scope (exit) {
198 FcStrListDone (list);
199 FcStrSetDestroy (langs);
200 }
201
202 char *tmp;
203 FcStrListFirst (list);
204 while ((tmp = FcStrListNext (list)) !is null) {
205 _languages.put (to!string (tmp.fromStringz), true);
206 anyLangAdded = true;
207 }
208 }
209 }
210
211 char *fullNameVal;
212 if (FcPatternGetString (fpattern, FC_FULLNAME, 0, &fullNameVal) == FcResult.Match) {
213 _fullname = fullNameVal.fromStringz.dup;
214 }
215
216 char *styleVal;
217 if (FcPatternGetString (fpattern, FC_STYLE, 0, &styleVal) == FcResult.Match) {
218 _style = styleVal.fromStringz.dup;
219 }
220
221 // assume 'en' is available
222 if (!anyLangAdded)
223 _languages.put ("en", true);
224
225 // prefer the English language if possible
226 // this is a hack since some people don't set their
227 // <languages> tag properly.
228 if (anyLangAdded && _languages.contains ("en"))
229 preferredLanguage = "en";
230
231 // read font metadata, if any is there
232 readSFNTData ();
233 }
234
235 private void readSFNTData ()
236 {
237 import glib.c.functions : g_convert, g_free;
238
239 immutable namecount = FT_Get_Sfnt_Name_Count (fface);
240 for (int index = 0; index < namecount; index++) {
241 FT_SfntName sname;
242 if (FT_Get_Sfnt_Name (fface, index, &sname) != 0)
243 continue;
244
245 // only handle unicode names for en_US
246 if (!(sname.platform_id == TT_PLATFORM_MICROSOFT
247 && sname.encoding_id == TT_MS_ID_UNICODE_CS
248 && sname.language_id == TT_MS_LANGID_ENGLISH_UNITED_STATES))
249 continue;
250
251 char* val = g_convert(cast(char*) sname.string,
252 sname.string_len,
253 "UTF-8",
254 "UTF-16BE",
255 null,
256 null,
257 null);
258 scope (exit) g_free (val);
259 switch (sname.name_id) {
260 case TT_NAME_ID_SAMPLE_TEXT:
261 this._sampleIconText = val.fromStringz.dup;
262 break;
263 case TT_NAME_ID_DESCRIPTION:
264 this._description = val.fromStringz.dup;
265 break;
266 case TT_NAME_ID_DESIGNER_URL:
267 this._homepage = val.fromStringz.dup;
268 break;
269 case TT_NAME_ID_VENDOR_URL:
270 if (this._homepage.empty)
271 this._homepage = val.fromStringz.dup;
272 break;
273 default:
274 break;
275 }
276 }
277 }
278
279 @property
280 string family ()
281 {
282 assert (ready ());
283 return to!string (fface.family_name.fromStringz);
284 }
285
286 @property
287 string style ()
288 {
289 return _style;
290 }
291
292 @property
293 string fullName ()
294 {
295 if (_fullname.empty)
296 return "%s %s".format (family, style);
297 else
298 return _fullname;
299 }
300
301 @property
302 string id ()
303 {
304 import std.string;
305
306 if (this.family is null)
307 return fileBaseName;
308 if (this.style is null)
309 return fileBaseName;
310 return "%s-%s".format (this.family.strip.toLower.replace (" ", ""),
311 this.style.strip.toLower.replace (" ", ""));
312 }
313
314 @property
315 FT_Encoding charset ()
316 {
317 assert (ready ());
318 if (fface.num_charmaps == 0)
319 return FT_ENCODING_NONE;
320
321 return fface.charmaps[0].encoding;
322 }
323
324 @property
325 const(FT_Face) fontFace () const
326 {
327 assert (ready ());
328 return fface;
329 }
330
331 auto getLanguageList ()
332 {
333 import std.algorithm : sort;
334 import std.array : array;
335
336 return array (_languages.byKey).sort;
337 }
338
339 @property
340 void preferredLanguage (string lang)
341 {
342 _preferredLanguage = lang;
343 }
344
345 @property
346 string preferredLanguage ()
347 {
348 return _preferredLanguage;
349 }
350
351 void addLanguage (string lang)
352 {
353 _languages.put (lang, true);
354 }
355
356 @property
357 string description ()
358 {
359 return _description;
360 }
361
362 @property
363 string homepage ()
364 {
365 return _homepage;
366 }
367
368 private string randomEnglishPangram (const string tmpId)
369 {
370 import std.digest.crc : crc32Of;
371 import std.conv : to;
372 import std.bitmanip : peek;
373 import std.range : take;
374
375 import std.stdio : writeln;
376
377 // we do want deterministic results here, so base the "random"
378 // pangram on the font family / font base name
379 immutable ubyte[4] hash = crc32Of (tmpId);
380 immutable pangramIdx = hash.to!(ubyte[]).peek!uint % englishPangrams.length;
381
382 return englishPangrams[pangramIdx];
383 }
384
385 private string randomEnglishPangram ()
386 {
387 auto tmpFontId = this.family;
388 if (tmpFontId.empty)
389 tmpFontId = this.fileBaseName;
390
391 return randomEnglishPangram (tmpFontId);
392 }
393
394 private void findSampleTexts ()
395 {
396 assert (ready ());
397 import std.uni : byGrapheme, isGraphical, byCodePoint, Grapheme;
398 import std.range;
399
400 void setFallbackSampleTextIfRequired ()
401 {
402 if (_sampleText.empty)
403 _sampleText = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr.";
404
405 if (_sampleIconText.empty) {
406 import std.conv : text;
407
408 auto graphemes = _sampleText.byGrapheme;
409 if (graphemes.walkLength > 3)
410 _sampleIconText = graphemes.array[0..3].byCodePoint.text;
411 else
412 _sampleIconText = "Aa";
413 }
414 }
415
416 dchar getFirstUnichar (string str)
417 {
418 auto g = Grapheme (str);
419 return g[0];
420 }
421
422 // if we only have to set the icon text, try to do it!
423 if (!_sampleText.empty)
424 setFallbackSampleTextIfRequired ();
425 if (!_sampleIconText.empty)
426 return;
427
428 // always prefer English (even if not alphabetically first)
429 if (_languages.contains ("en"))
430 preferredLanguage = "en";
431
432 // ensure we try the preferred language first
433 auto tmpLangList = array(getLanguageList ());
434 if (!preferredLanguage.empty)
435 tmpLangList = [this.preferredLanguage] ~ tmpLangList;
436
437 // determine our sample texts
438 foreach (ref lang; tmpLangList) {
439 auto plang = pango_language_from_string (lang.toStringz);
440 string text;
441 if (lang == "en")
442 text = randomEnglishPangram ();
443 else
444 text = pango_language_get_sample_string (plang).fromStringz.to!string;
445
446 if (text.empty)
447 continue;
448
449 _sampleText = text;
450 const itP = lang in iconTexts;
451 if (itP !is null) {
452 _sampleIconText = *itP;
453 break;
454 }
455 }
456
457 // set some default values if we have been unable to find any texts
458 setFallbackSampleTextIfRequired ();
459
460 // check if we have a font that can actually display the characters we picked - in case
461 // it doesn't, we just select random chars.
462 if (FT_Get_Char_Index (fface, getFirstUnichar (_sampleIconText)) == 0) {
463 _sampleText = "☃❤✓☀★☂♞☯☢∞❄♫↺";
464 _sampleIconText = "☃❤";
465 }
466 if (FT_Get_Char_Index (fface, getFirstUnichar (_sampleIconText)) == 0) {
467 import std.uni;
468
469 _sampleText = "";
470 _sampleIconText = "";
471
472 auto count = 0;
473 for (uint map = 0; map < fface.num_charmaps; map++) {
474 auto charmap = fface.charmaps[map];
475
476 FT_Set_Charmap (fface, charmap);
477
478 FT_UInt gindex;
479 auto charcode = FT_Get_First_Char (fface, &gindex);
480 while (gindex != 0) {
481 immutable chc = to!dchar (charcode);
482 if (chc.isGraphical && !chc.isSpace && !chc.isPunctuation) {
483 count++;
484 _sampleText ~= chc;
485 }
486
487 if (count >= 24)
488 break;
489 charcode = FT_Get_Next_Char (fface, charcode, &gindex);
490 }
491
492 if (count >= 24)
493 break;
494 }
495
496 _sampleText = _sampleText.strip;
497
498 // if we were unsuccessful at adding chars, set fallback again
499 // (and in this case, also set the icon text to something useful again)
500 setFallbackSampleTextIfRequired ();
501 }
502 }
503
504 @property
505 string sampleText ()
506 {
507 if (_sampleText.empty)
508 findSampleTexts ();
509 return _sampleText;
510 }
511
512 @property
513 void sampleText (string val)
514 {
515 if (val.length > 2)
516 _sampleText = val;
517 }
518
519 @property
520 string sampleIconText ()
521 {
522 if (_sampleIconText.empty)
523 findSampleTexts ();
524 return _sampleIconText;
525 }
526
527 @property
528 void sampleIconText (string val)
529 {
530 if (val.length <= 3)
531 _sampleIconText = val;
532 }
533 }
534
535 unittest
536 {
537 import std.stdio : writeln, File;
538 import std.path : buildPath;
539 import std.array : array;
540 import asgen.utils : getTestSamplesDir;
541 writeln ("TEST: ", "Font");
542
543 immutable fontFile = buildPath (getTestSamplesDir (), "NotoSans-Regular.ttf");
544
545 // test reading from file
546 auto font = new Font (fontFile);
547 assert (font.family == "Noto Sans");
548 assert (font.style == "Regular");
549
550 ubyte[] data;
551 auto f = File (fontFile, "r");
552 while (!f.eof) {
553 char[512] buf;
554 data ~= f.rawRead (buf);
555 }
556
557 // test reading from memory
558 font = new Font (data, "test.ttf");
559 assert (font.family == "Noto Sans");
560 assert (font.style == "Regular");
561 assert (font.charset == FT_ENCODING_UNICODE);
562 assert (font.homepage == "http://www.monotype.com/studio");
563 assert (font.description == "Data hinted. Designed by Monotype design team.");
564
565 const langList = array (font.getLanguageList ());
566 writeln (langList);
567 assert (langList == ["aa", "ab", "af", "ak", "an", "ast", "av", "ay", "az-az", "ba", "be", "ber-dz", "bg", "bi", "bin",
568 "bm", "br", "bs", "bua", "ca", "ce", "ch", "chm", "co", "crh", "cs", "csb", "cu", "cv", "cy", "da",
569 "de", "ee", "el", "en", "eo", "es", "et", "eu", "fat", "ff", "fi", "fil", "fj", "fo", "fr", "fur",
570 "fy", "ga", "gd", "gl", "gn", "gv", "ha", "haw", "ho", "hr", "hsb", "ht", "hu", "hz", "ia", "id",
571 "ie", "ig", "ik", "io", "is", "it", "jv", "kaa", "kab", "ki", "kj", "kk", "kl", "kr", "ku-am",
572 "ku-tr", "kum", "kv", "kw", "kwm", "ky", "la", "lb", "lez", "lg", "li", "ln", "lt","lv", "mg", "mh",
573 "mi", "mk", "mn-mn", "mo", "ms", "mt", "na", "nb", "nds", "ng", "nl", "nn", "no", "nr", "nso", "nv",
574 "ny", "oc", "om", "os", "pap-an", "pap-aw", "pl", "pt", "qu", "quz", "rm", "rn", "ro", "ru", "rw",
575 "sah", "sc", "sco", "se", "sel", "sg", "sh", "shs", "sk", "sl", "sm","sma", "smj", "smn", "sms", "sn",
576 "so", "sq", "sr", "ss", "st", "su", "sv", "sw", "tg", "tk", "tl", "tn", "to", "tr", "ts", "tt", "tw",
577 "ty", "tyv", "uk", "uz", "ve", "vi", "vo", "vot", "wa", "wen", "wo", "xh", "yap", "yo", "za", "zu"]);
578
579
580 // uses "Noto Sans"
581 assert (font.randomEnglishPangram () == "A large fawn jumped quickly over white zebras in a box.");
582
583 assert (font.randomEnglishPangram ("aaaaa") == "Jack amazed a few girls by dropping the antique onyx vase.");
584 assert (font.randomEnglishPangram ("abcdefg") == "Two driven jocks help fax my big quiz.");
585 }
3232 import appstream.Provided;
3333 import appstream.Icon;
3434 import appstream.Launchable : Launchable, LaunchableKind;
35 static import appstream.Utils;
36 alias AsUtils = appstream.Utils.Utils;
3537 static import std.regex;
3638
3739 import asgen.result;
9193 */
9294 private auto filterCategories (Component cpt, GeneratorResult gres, ref string[] cats)
9395 {
94 import asgen.bindings.appstream_utils : as_utils_is_category_name;
95
9696 auto res = appender!(string[]);
9797 res.reserve (cats.length / 2);
9898 foreach (const cat; cats) {
106106 break;
107107 default:
108108 if (!cat.empty && !cat.toLower.startsWith ("x-")) {
109 if (as_utils_is_category_name (cat.toStringz))
109 if (AsUtils.isCategoryName(cat))
110110 res ~= cat;
111111 else
112112 gres.addHint (cpt, "category-name-invalid", ["category": cat]);
178178
179179 try {
180180 immutable onlyShowIn = df.getString (DESKTOP_GROUP, "OnlyShowIn");
181 if (onlyShowIn.empty) {
181 if (onlyShowIn.empty)
182182 gres.addHint (fnameBase, "desktop-file-empty-onlyshowin");
183 if (!ignoreNoDisplay)
184 return null; // we ignore this .desktop file
185 }
183
184 // We want to ignore all desktop-entry files which were made desktop-exclusive
185 // via OnlyShowIn (those are usually configuration apps and control center modules)
186 // Only exception is if a metainfo file was present.
187 if (!ignoreNoDisplay)
188 return null; // we ignore this .desktop file
186189 } catch (GException) {}
187190
188191 /* check this is a valid desktop file */
214217 cpt.setId (fnameBase);
215218
216219 cpt.setKind (ComponentKind.DESKTOP_APP);
217 gres.addComponent (cpt);
220 gres.addComponent (cpt, null);
218221 }
219222 }
220223
367370 ecpt.setId ("org.example.foobar");
368371 ecpt.setName ("TestX", "C");
369372 ecpt.setSummary ("Summary of TestX", "C");
370 res.addComponent (ecpt);
373 res.addComponent (ecpt, null);
371374
372375 cpt = parseDesktopFile (res, null, "org.example.foobar.desktop", data, false);
373376 assert (cpt !is null);
2626 import appstream.Component;
2727 import appstream.Icon;
2828 import appstream.Screenshot;
29 static import appstream.Image;
29 static import asImage = appstream.Image;
3030 static import std.file;
31 import ascompose.Font : Font;
32 import ascompose.Canvas : Canvas;
33 import ascompose.c.types : ImageFormat;
3134
3235 import asgen.utils;
3336 import asgen.logging;
3437 import asgen.result;
35 import asgen.image : Canvas;
36 import asgen.font : Font;
3738 import asgen.config : Config, IconPolicy;
3839
3940
6566 // the font class locks the global mutex internally when reading data with Fontconfig
6667 Font font;
6768 try {
68 font = new Font (fdata, fontBaseName);
69 font = new Font ((cast(ubyte[]) fdata).ptr, cast(ptrdiff_t)fdata.length, fontBaseName);
6970 } catch (Exception e) {
7071 gres.addHint (null, "font-load-error", ["fname": fontBaseName,
7172 "pkg_fname": gres.pkg.getFilename.baseName,
7374 return;
7475 }
7576
76 logDebug ("Found font %s/%s", fontBaseName, font.fullName);
77 allFonts[font.fullName.toLower] = font;
78 }
79
80 foreach (ref cpt; gres.getComponents ()) {
77 logDebug ("Found font %s/%s", fontBaseName, font.getFullname);
78 allFonts[font.getFullname.toLower] = font;
79 }
80
81 auto cptsPtrArray = gres.fetchComponents ();
82 for (uint i = 0; i < cptsPtrArray.len; i++) {
83 auto cpt = new Component (cast (AsComponent*) cptsPtrArray.index (i));
8184 if (cpt.getKind () != ComponentKind.FONT)
8285 continue;
8386
125128 // also ensure that the font style list is sorted for more
126129 // deterministic results
127130 auto regularFound = false;
128 foreach (ref font; allFonts.byValue.array.sort!"a.fullName < b.fullName") {
129 immutable fontStyleId = font.style.toLower;
131 foreach (ref font; allFonts.byValue.array.sort!"a.getFullname < b.getFullname") {
132 immutable fontStyleId = font.getStyle.toLower;
130133 if (!regularFound && fontStyleId.canFind ("regular")) {
131134 auto tmp = selectedFonts.data.dup;
132135 selectedFonts.clear ();
159162 if (selectedFonts.data.length == 0) {
160163 auto fontNamesStr = appender!string;
161164 foreach (ref font; allFonts.byValue)
162 fontNamesStr ~= fontNamesStr.data.empty? font.fullName : ("; " ~ font.fullName);
165 fontNamesStr ~= fontNamesStr.data.empty? font.getFullname : ("; " ~ font.getFullname);
163166 if (fontNamesStr.data.empty)
164167 fontNamesStr ~= "None";
165168 gres.addHint (cpt, "font-metainfo-but-no-font", ["font_names": fontNamesStr.data]);
173176 auto firstLang = (cast(char*) cptLanguages.first.data).fromStringz;
174177
175178 foreach (ref font; selectedFonts.data)
176 font.preferredLanguage = firstLang.to!string;
179 font.setPreferredLanguage (firstLang.to!string);
177180
178181 // add languages mentioned in the metainfo file to list of supported languages
179182 // of the respective font
192195 // process font files
193196 auto hasIcon = false;
194197 foreach (ref font; selectedFonts.data) {
195 logDebug ("Processing font '%s'", font.id);
198 import glib.Str;
199 logDebug ("Processing font '%s'", font.getId);
196200
197201 // add language information
198 foreach (ref lang; font.getLanguageList ()) {
202 for (auto l = font.getLanguageList; l !is null; l = l.next) {
203 immutable lang = Str.toString (cast(char*)l.data);
199204 // we have no idea how well the font supports the language's script,
200205 // but since it adverties support in its metadata, we just assume 100% here
201206 cpt.addLanguage (lang, 100);
211216
212217 // set additional metadata. The font metadata might be terrible, but if the data is bad
213218 // it hopefully motivates people to write proper metainfo files.
214 if (cpt.getDescription.empty && !font.description.empty) {
215 cpt.setDescription (font.description, "C");
216 }
217 if (cpt.getUrl (UrlKind.HOMEPAGE).empty && !font.homepage.empty) {
218 cpt.addUrl (UrlKind.HOMEPAGE, font.homepage);
219 if (cpt.getDescription.empty && !font.getDescription.empty) {
220 cpt.setDescription (font.getDescription, "C");
221 }
222 if (cpt.getUrl (UrlKind.HOMEPAGE).empty && !font.getHomepage.empty) {
223 cpt.addUrl (UrlKind.HOMEPAGE, font.getHomepage);
219224 }
220225 }
221226
243248 // check if we have a custom icon text value (useful for symbolic fonts)
244249 immutable customIconText = cpt.getCustomValue ("FontIconText");
245250 if (!customIconText.empty)
246 font.sampleIconText = customIconText; // Font will ensure that the value does not exceed 3 chars
247
248 immutable fid = font.id;
249 immutable iconName = format ("%s_%s.png", gres.pkgname, fid);
251 font.setSampleIconText (customIconText); // Font will ensure that the value does not exceed 3 chars
252
253 immutable fid = font.getId;
254 immutable iconName = format ("%s_%s.png", gres.pkg.name, fid);
250255 immutable iconStoreLocation = buildPath (path, iconName);
251256
252257 if (!std.file.exists (iconStoreLocation)) {
253258 // we didn't create an icon yet - render it
254259 auto cv = new Canvas (size.width, size.height);
255 cv.drawTextLine (font, font.sampleIconText);
260 cv.drawTextLine (font, font.getSampleIconText, -1);
256261 cv.savePng (iconStoreLocation);
257262 }
258263
295300
296301 auto first = true;
297302 foreach (ref font; fonts) {
298 immutable fid = font.id;
303 immutable fid = font.getId;
299304 if (fid is null) {
300305 logWarning ("%s: Ignored font screenshot rendering due to missing ID:", cpt.getId ());
301306 continue;
306311 scr.setKind (ScreenshotKind.DEFAULT);
307312 else
308313 scr.setKind (ScreenshotKind.EXTRA);
309 scr.setCaption ("%s %s".format (font.family, font.style), "C");
314 scr.setCaption ("%s %s".format (font.getFamily, font.getStyle), "C");
310315
311316 if (first)
312317 first = false;
318323 // be used, this should not be an issue.
319324 immutable customSampleText = cpt.getCustomValue ("FontSampleText");
320325 if (!customSampleText.empty)
321 font.sampleIconText = customSampleText;
326 font.setSampleIconText (customSampleText);
322327
323328 auto cptScreenshotsUrl = buildPath (gres.gcidForComponent (cpt), "screenshots");
324329 foreach (ref size; fontScreenshotSizes) {
330335 if (!std.file.exists (imgFileName)) {
331336 // we didn't create s screenshot yet - render it
332337 auto cv = new Canvas (size.width, size.height);
333 cv.drawTextLine (font, font.sampleText);
338 cv.drawTextLine (font, font.getSampleText, -1);
334339 cv.savePng (imgFileName);
335340 }
336341
337 auto img = new appstream.Image.Image ();
342 auto img = new asImage.Image ();
338343 img.setKind (ImageKind.THUMBNAIL);
339344 img.setWidth (size.width);
340345 img.setHeight (size.height);
3333 import glib.GException : GException;
3434 import appstream.Component;
3535 import appstream.Icon;
36 static import ascompose.Utils;
37 alias AscUtils = ascompose.Utils.Utils;
38
39 import ascompose.Image : Image;
40 import ascompose.Canvas : Canvas;
41 import ascompose.c.types : ImageFormat, ImageLoadFlags, ImageSaveFlags;
3642 static import std.file;
3743
3844 import asgen.containers : HashMap;
3945 import asgen.utils;
4046 import asgen.logging;
4147 import asgen.result;
42 import asgen.image;
4348 import asgen.backends.interfaces;
4449 import asgen.contentsstore;
4550 import asgen.config : Config, IconPolicy;
229234
230235 public:
231236
232 this (string mediaPath, IconPolicy[] iconPolicy, HashMap!(string, Package) pkgMap, string iconTheme = null)
237 this (ContentsStore ccache, string mediaPath, IconPolicy[] iconPolicy, HashMap!(string, Package) pkgMap, string iconTheme = null)
233238 {
234239 logDebug ("Creating new IconHandler");
235240
275280 return null;
276281 return pkgMap.get (pkid, null);
277282 }
278
279 // open package contents cache
280 auto ccache = scoped!ContentsStore ();
281 ccache.open (conf);
282283
283284 // load data from the contents index.
284285 // we don't show mercy to memory here, we just want the icon lookup to be fast,
368369 if (iconName.endsWith (ext))
369370 return true;
370371 return false;
371 }
372
373 private ImageFormat imageKindFromFile (string fname)
374 {
375 if (fname.endsWith (".png"))
376 return ImageFormat.PNG;
377 if ((fname.endsWith (".jpg")) || (fname.endsWith (".jpeg")))
378 return ImageFormat.JPEG;
379 if (fname.endsWith (".svg"))
380 return ImageFormat.SVG;
381 if (fname.endsWith (".svgz"))
382 return ImageFormat.SVGZ;
383 if (fname.endsWith (".xpm"))
384 return ImageFormat.XPM;
385 return ImageFormat.UNKNOWN;
386372 }
387373
388374 /**
495481 IconPolicy policy)
496482 {
497483 immutable size = policy.iconSize;
498 auto iformat = imageKindFromFile (iconPath);
484 auto iformat = AscUtils.imageFormatFromFilename (iconPath);
499485 if (iformat == ImageFormat.UNKNOWN) {
500486 gres.addHint (cpt.getId (), "icon-format-unsupported", ["icon_fname": baseName (iconPath)]);
501487 return false;
502488 }
503489
504490 auto path = buildPath (cptExportPath, "icons", size.toString);
505 auto iconName = (gres.pkg.kind == PackageKind.FAKE)? baseName (iconPath) : "%s_%s".format (gres.pkgname, baseName (iconPath));
491 auto iconName = (gres.pkg.kind == PackageKind.FAKE)? baseName (iconPath) : "%s_%s".format (gres.pkg.name, baseName (iconPath));
506492
507493 if (iconName.endsWith (".svgz"))
508494 iconName = iconName.replace (".svgz", ".png");
570556 mkdirRecurse (path);
571557
572558 try {
559 import gio.MemoryInputStream : MemoryInputStream;
560 auto stream = new MemoryInputStream (cast(ubyte[]) iconData, null);
573561 auto cv = scoped!Canvas (scaled_width, scaled_height);
574 cv.renderSvg (iconData);
562 cv.renderSvg (stream);
575563 cv.savePng (iconStoreLocation);
576564 } catch (Exception e) {
577565 gres.addHint(cpt.getId (), "image-write-error", ["fname": baseName (iconPath),
582570 } else {
583571 Image img;
584572 try {
585 img = new Image (iconData, iformat);
573 img = new Image ((cast(ubyte[]) iconData).ptr, cast(ptrdiff_t)iconData.length,
574 0, ImageLoadFlags.NONE);
586575 } catch (Exception e) {
587576 gres.addHint(cpt.getId (), "image-write-error", ["fname": baseName (iconPath),
588577 "pkg_fname": baseName (sourcePkg.getFilename),
597586 // the icon is not too small
598587 if (size != ImageSize (64))
599588 return false;
600 if ((img.width < 48) || (img.height < 48))
589 if ((img.getWidth < 48) || (img.getHeight < 48))
601590 return false;
602591 } else {
603 if ((img.width < scaled_width) || (img.height < scaled_height))
592 if ((img.getWidth < scaled_width) || (img.getHeight < scaled_height))
604593 return false;
605594 }
606595 }
608597 // ensure that we don't try to make an application visible that has a really tiny icon
609598 // by upscaling it to a blurry mess
610599 if (size.scale == 1 && size.width == 64) {
611 if ((img.width < 48) || (img.height < 48)) {
600 if ((img.getWidth < 48) || (img.getHeight < 48)) {
612601 gres.addHint (cpt, "icon-too-small", ["icon_name": iconName,
613 "icon_size": "%ux%u".format (img.width, img.height)]);
602 "icon_size": "%ux%u".format (img.getWidth, img.getHeight)]);
614603 return false;
615604 }
616605 }
617606
618607 // warn about icon upscaling, it looks ugly
619 if (scaled_width > img.width) {
608 if (scaled_width > img.getWidth) {
620609 gres.addHint (cpt, "icon-scaled-up", ["icon_name": iconName,
621 "icon_size": "%ux%u".format (img.width, img.height),
610 "icon_size": "%ux%u".format (img.getWidth, img.getHeight),
622611 "scale_size": size.toString]);
623612 }
624613
627616
628617 try {
629618 img.scale (scaled_width, scaled_height);
630 img.savePng (iconStoreLocation);
619 img.saveFilename (iconStoreLocation,
620 0, 0,
621 ImageSaveFlags.OPTIMIZE);
631622 } catch (Exception e) {
632623 gres.addHint (cpt, "image-write-error", ["fname": baseName (iconPath),
633624 "pkg_fname": baseName (sourcePkg.getFilename),
3030 import asgen.containers: HashMap;
3131 import asgen.logging;
3232 import asgen.result : GeneratorResult;
33 import asgen.contentsstore : ContentsStore;
3334 import asgen.backends.interfaces : Package;
3435
3536
108109 private:
109110 HashMap!(string, Package) localeIdPkgMap;
110111
111 public this (Package[] pkgList)
112 public this (ContentsStore cstore, Package[] pkgList)
112113 {
113114 import std.array : array;
114115 import std.typecons : scoped;
115 import asgen.contentsstore : ContentsStore;
116116 import asgen.config : Config;
117117
118118 logDebug ("Creating new LocaleHandler.");
130130 if (!conf.feature.processLocale)
131131 return; // don't load the expensive locale<->package mapping if we don't need it
132132
133 // open package contents cache
134 auto ccache = scoped!ContentsStore ();
135 ccache.open (conf);
136
137133 // we make the assumption here that all locale for a given domain are in one package.
138134 // otherwise this global search will get even more insane.
139135 // the key of the map returned by getLocaleMap will therefore contain only the locale
140136 // file basename instead of a full path
141 auto dbLocaleMap = ccache.getLocaleMap (array(pkgMap.byKey));
137 auto dbLocaleMap = cstore.getLocaleMap (array(pkgMap.byKey));
142138 foreach (info; dbLocaleMap.byPair) {
143139 immutable id = info.key;
144140 immutable pkgid = info.value;
+0
-151
src/asgen/handlers/metainfoparser.d less more
0 /*
1 * Copyright (C) 2016-2018 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this software. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.handlers.metainfoparser;
20
21 import std.path : baseName;
22 import std.uni : toLower;
23 import std.string : format;
24 import std.array : empty;
25 import std.stdio;
26 import appstream.Metadata;
27 import appstream.Component;
28
29 import asgen.result;
30 import asgen.utils;
31
32
33 immutable MAX_RELEASE_INFO_COUNT = 6; /// Maximum amount of releases present in output data
34
35 private bool isAcceptableMetainfoLicense (string licenseExpression) pure
36 {
37 import asgen.bindings.appstream_utils : spdxLicenseTokenize, spdxLicenseIsMetadataLicense;
38
39 bool requiresAllTokens = true;
40 uint licenseGoodCnt = 0;
41 uint licenseBadCnt = 0;
42
43 auto tokens = spdxLicenseTokenize (licenseExpression);
44 if (tokens.length == 0)
45 return false;
46
47 // we don't consider very complex expressions valid
48 foreach (const ref t; tokens) {
49 if (t == "(" || t == ")")
50 return false;
51 }
52
53 // this is a simple expression parser and can be easily tricked
54 foreach (const ref t; tokens) {
55 if (t == "+")
56 continue;
57 if (t == "|") {
58 requiresAllTokens = false;
59 continue;
60 }
61 if (t == "&") {
62 requiresAllTokens = true;
63 continue;
64 }
65
66 if (spdxLicenseIsMetadataLicense (t))
67 licenseGoodCnt++;
68 else
69 licenseBadCnt++;
70 }
71
72 // any valid token makes this valid
73 if (!requiresAllTokens && licenseGoodCnt > 0)
74 return true;
75
76 // all tokens are required to be valid
77 if (requiresAllTokens && licenseBadCnt == 0)
78 return true;
79
80 // this license or license expression was bad
81 return false;
82 }
83
84 Component parseMetaInfoData (Metadata mdata, GeneratorResult gres, const string data, const string mfname)
85 {
86 try {
87 mdata.parse (data, FormatKind.XML);
88 } catch (Exception e) {
89 gres.addHint ("general", "metainfo-parsing-error", ["fname": mfname, "error": e.msg]);
90 return null;
91 }
92
93 auto cpt = mdata.getComponent ();
94 if (cpt is null)
95 return null;
96
97 // check if we have a component-id, a component without ID is invalid
98 if (cpt.getId.empty) {
99 gres.addHint (null, "metainfo-no-id", ["fname": mfname]);
100 return null;
101 }
102 gres.addComponent (cpt);
103
104 // check if we can actually legally use this metadata
105 if (!isAcceptableMetainfoLicense (cpt.getMetadataLicense())) {
106 gres.addHint (cpt, "metainfo-license-invalid", ["license": cpt.getMetadataLicense()]);
107 return null;
108 }
109
110 // quit immediately if we have an unknown component type
111 if (cpt.getKind == ComponentKind.UNKNOWN) {
112 gres.addHint (cpt, "metainfo-unknown-type");
113 return null;
114 }
115
116 // limit the amount of releases that we add to the output metadata.
117 // since releases are sorted with the newest one at the top, we will only
118 // remove the older ones.
119 auto releases = cpt.getReleases;
120 if (releases.len > MAX_RELEASE_INFO_COUNT) {
121 releases.setSize (MAX_RELEASE_INFO_COUNT);
122 }
123
124 return cpt;
125 }
126
127 Component parseMetaInfoData (GeneratorResult gres, const string data, const string mfname)
128 {
129 auto mdata = new Metadata ();
130 mdata.setLocale ("ALL");
131 mdata.setFormatStyle (FormatStyle.METAINFO);
132
133 auto cpt = parseMetaInfoData (mdata, gres, data, mfname);
134 gres.updateComponentGCID (cpt, data);
135 return cpt;
136 }
137
138 unittest {
139 import std.stdio : writeln;
140 writeln ("TEST: ", "Metainfo Parser");
141
142 assert (isAcceptableMetainfoLicense ("FSFAP"));
143 assert (isAcceptableMetainfoLicense ("CC0"));
144 assert (isAcceptableMetainfoLicense ("CC0-1.0"));
145 assert (isAcceptableMetainfoLicense ("0BSD"));
146 assert (isAcceptableMetainfoLicense ("MIT AND FSFAP"));
147 assert (!isAcceptableMetainfoLicense ("GPL-2.0 AND FSFAP"));
148 assert (isAcceptableMetainfoLicense ("GPL-3.0+ or GFDL-1.3-only"));
149 assert (!isAcceptableMetainfoLicense ("GPL-3.0+ and GFDL-1.3-only"));
150 }
+0
-82
src/asgen/handlers/metainfovalidator.d less more
0 /*
1 * Copyright (C) 2016 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this software. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.handlers.metainfovalidator;
20
21 import std.path : baseName;
22 import std.uni : toLower;
23 import std.string : format;
24 import std.stdio;
25 import std.typecons : scoped;
26
27 import appstream.Validator : Validator;
28 import appstream.ValidatorIssue;
29 import appstream.Component;
30 import glib.ListG;
31 import gobject.ObjectG;
32
33 import asgen.result;
34 import asgen.utils;
35
36
37 void validateMetaInfoFile (GeneratorResult res, Component cpt, string data, string miBasename)
38 {
39 // create thread-local validator for efficiency
40 static Validator validator = null;
41 if (validator is null)
42 validator = new Validator;
43
44 validator.setCheckUrls (false); // don't check web URLs for validity
45 validator.clearIssues (); // remove issues from a previous use of this validator
46
47 try {
48 validator.validateData (data);
49 } catch (Exception e) {
50 res.addHint (cpt.getId (), "metainfo-validation-error", e.msg);
51 return;
52 }
53
54 auto issueList = validator.getIssues ();
55 for (ListG l = issueList; l !is null; l = l.next) {
56 auto issue = ObjectG.getDObject!ValidatorIssue (cast (typeof(ValidatorIssue.tupleof[0])) l.data);
57
58 // create a tag for asgen out of the AppStream validator tag by prefixing it
59 immutable asvTag = "asv-%s".format (issue.getTag);
60
61 // we have a special hint tag for legacy metadata,
62 // with its proper "error" priority
63 if (asvTag == "asv-metainfo-ancient") {
64 res.addHint (cpt.getId (), "ancient-metadata");
65 continue;
66 }
67
68 immutable line = issue.getLine;
69 string location;
70 if (line >= 0)
71 location = "%s:%s".format (miBasename, line);
72 else
73 location = miBasename;
74
75 // we don't need to do much here, with the tag generated here,
76 // the hint registry will automatically assign the right explanation
77 // text and severity to the issue.
78 res.addHint (cpt, asvTag, ["location": location,
79 "hint": issue.getHint]);
80 }
81 }
2121 public import asgen.handlers.desktopparser;
2222 public import asgen.handlers.fonthandler;
2323 public import asgen.handlers.iconhandler;
24 public import asgen.handlers.metainfoparser;
25 public import asgen.handlers.metainfovalidator;
2624 public import asgen.handlers.screenshothandler;
2725 public import asgen.handlers.localehandler : LocaleHandler;
3030 import appstream.Screenshot : AsScreenshot, Screenshot, ScreenshotMediaKind;
3131 import appstream.Image : AsImage, Image, ImageKind;
3232 import appstream.Video : AsVideo, Video, VideoContainerKind, VideoCodecKind;
33 import ascompose.c.types : ImageFormat, ImageLoadFlags, ImageSaveFlags;
34 static import ascompose.Image;
3335 static import std.file;
3436
3537 import asgen.config : Config;
3739 import asgen.downloader : Downloader;
3840 import asgen.utils : ImageSize, filenameFromURI, getFileContents;
3941 import asgen.logging;
40 static import asgen.image;
4142
4243
4344 private immutable screenshotSizes = [ImageSize (1248, 702), ImageSize (752, 423), ImageSize (624, 351), ImageSize (224, 126)];
357358 immutable srcImgUrl = buildPath (scrBaseUrl, srcImgName);
358359
359360 // save the source screenshot as PNG image
360 auto srcImg = new asgen.image.Image (imgData, asgen.image.ImageFormat.PNG);
361 srcImg.savePng (srcImgPath);
361 auto srcImg = new ascompose.Image.Image (imgData.ptr, cast(ptrdiff_t)imgData.length,
362 0, ImageLoadFlags.NONE);
363 srcImg.saveFilename (srcImgPath,
364 0, 0,
365 ImageSaveFlags.OPTIMIZE);
362366
363367 auto img = new Image ();
364368 img.setKind (ImageKind.SOURCE);
365369 img.setLocale (origImageLocale);
366370
367 sourceScrWidth = srcImg.width;
368 sourceScrHeight = srcImg.height;
371 sourceScrWidth = srcImg.getWidth;
372 sourceScrHeight = srcImg.getHeight;
369373 img.setWidth (sourceScrWidth);
370374 img.setHeight (sourceScrHeight);
371375
398402 continue;
399403
400404 try {
401 auto thumb = new asgen.image.Image (imgData, asgen.image.ImageFormat.PNG);
405 auto thumb = new ascompose.Image.Image (imgData.ptr, cast(ptrdiff_t)imgData.length,
406 0, ImageLoadFlags.NONE);
402407 if (size.width > size.height)
403408 thumb.scaleToWidth (size.width);
404409 else
405410 thumb.scaleToHeight (size.height);
406411
407412 // create thumbnail storage path and URL component
408 auto thumbImgName = "image-%s_%sx%s.png".format (scrNo, thumb.width, thumb.height);
413 auto thumbImgName = "image-%s_%sx%s.png".format (scrNo, thumb.getWidth, thumb.getHeight);
409414 auto thumbImgPath = buildPath (scrExportDir, thumbImgName);
410415 auto thumbImgUrl = buildPath (scrBaseUrl, thumbImgName);
411416
412417 // store the thumbnail image on disk
413 thumb.savePng (thumbImgPath);
418 thumb.saveFilename(thumbImgPath,
419 0, 0,
420 ImageSaveFlags.OPTIMIZE);
414421
415422 // finally prepare the thumbnail definition and add it to the metadata
416423 auto img = new Image ();
417424 img.setLocale (origImageLocale);
418425 img.setKind (ImageKind.THUMBNAIL);
419 img.setWidth (thumb.width);
420 img.setHeight (thumb.height);
426 img.setWidth (thumb.getWidth);
427 img.setHeight (thumb.getHeight);
421428 img.setUrl (thumbImgUrl);
422429 scr.addImage (img);
423430 } catch (Exception e) {
+0
-311
src/asgen/hint.d less more
0 /*
1 * Copyright (C) 2016-2020 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this software. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.hint;
20 @safe:
21
22 import std.stdio;
23 import std.string;
24 import std.json;
25
26 import asgen.logging;
27 import asgen.utils;
28 import appstream.Validator : Validator;
29
30
31 /**
32 * Severity assigned with an issue hint.
33
34 * ERROR: A fatal error which resulted in the component being excluded from the final metadata.
35 * WARNING: An issue which did not prevent generating meaningful data, but which is still serious
36 * and should be fixed (warning of this kind usually result in less data).
37 * INFO: Information, no immediate action needed (but will likely be an issue later).
38 * PEDANTIC: Information which may improve the data, but could also be ignored.
39 */
40 enum HintSeverity
41 {
42 UNKNOWN,
43 ERROR,
44 WARNING,
45 INFO,
46 PEDANTIC
47 }
48
49 private HintSeverity severityFromString (string str) pure
50 {
51 switch (str) {
52 case "error":
53 return HintSeverity.ERROR;
54 case "warning":
55 return HintSeverity.WARNING;
56 case "info":
57 return HintSeverity.INFO;
58 case "pedantic":
59 return HintSeverity.INFO;
60 default:
61 return HintSeverity.UNKNOWN;
62 }
63 }
64
65 private string severityToString (HintSeverity severity) pure
66 {
67 switch (severity) {
68 case HintSeverity.ERROR:
69 return "error";
70 case HintSeverity.WARNING:
71 return "warning";
72 case HintSeverity.INFO:
73 return "info";
74 case HintSeverity.PEDANTIC:
75 return "pedantic";
76 default:
77 return null;
78 }
79 }
80
81 /**
82 * Information about issues that occurred during the
83 * metadata generation process.
84 */
85 struct GeneratorHint
86 {
87
88 private:
89 string tag;
90 string cid;
91
92 string[string] vars;
93
94 HintSeverity severity;
95
96 public:
97
98 this (string tag, string cid) @trusted
99 {
100 this.tag = tag;
101 this.cid = cid;
102
103 severity = HintTagRegistry.get.getSeverity (tag);
104 if (severity == HintSeverity.UNKNOWN)
105 logWarning ("Severity of hint tag '%s' is unknown. This likely means that this tag is not registered and should not be emitted.", tag);
106 }
107
108 @safe
109 bool isError () pure
110 {
111 return severity == HintSeverity.ERROR;
112 }
113
114 @safe
115 void setVars (string[string] vars) pure
116 {
117 this.vars = vars;
118 }
119
120 @safe
121 auto toJsonNode () pure
122 {
123 JSONValue json = JSONValue(["tag": JSONValue (tag),
124 "vars": JSONValue (vars)
125 ]);
126 return json;
127 }
128 }
129
130 /**
131 * Singleton holding information about the hint tags we know about.
132 **/
133 final class HintTagRegistry
134 {
135 // Thread local
136 private static bool instantiated_;
137
138 // Thread global
139 private __gshared HintTagRegistry instance_;
140
141 @trusted
142 static HintTagRegistry get()
143 {
144 if (!instantiated_) {
145 synchronized (HintTagRegistry.classinfo) {
146 if (!instance_)
147 instance_ = new HintTagRegistry ();
148
149 instantiated_ = true;
150 }
151 }
152
153 return instance_;
154 }
155
156 struct HintDefinition
157 {
158 string tag;
159 string text;
160 HintSeverity severity;
161 bool internal;
162 bool valid;
163 }
164
165 private HintDefinition[string] hintDefs;
166
167 private this () @trusted
168 {
169 import std.path;
170 static import std.file;
171
172 // find the hint definition file
173 auto hintsDefFile = getDataPath ("asgen-hints.json");
174 if (!std.file.exists (hintsDefFile)) {
175 logError ("Hints definition file '%s' was not found! This means we can not determine severity of issue tags and not render report pages.", hintsDefFile);
176 return;
177 }
178
179 // read the hints definition JSON file
180 auto f = File (hintsDefFile, "r");
181 string jsonData;
182 string line;
183 while ((line = f.readln ()) !is null)
184 jsonData ~= line;
185
186 auto hintDefsJSON = parseJSON (jsonData);
187
188 foreach (ref tag; hintDefsJSON.object.byKey) {
189 auto j = hintDefsJSON[tag];
190 HintDefinition hdef;
191
192 hdef.tag = tag;
193 hdef.severity = severityFromString (j["severity"].str);
194
195 if (j["text"].type == JSONType.array) {
196 foreach (l; j["text"].array)
197 hdef.text ~= l.str ~ "\n";
198 } else {
199 hdef.text = j["text"].str;
200 }
201
202 if ("internal" in j)
203 hdef.internal = j["internal"].type == JSONType.true_;
204 hdef.valid = true;
205
206 hintDefs[tag] = hdef;
207 }
208
209 // add AppStream validator hint tags to the registry
210 auto validator = new Validator;
211 foreach (ref tag; validator.getTags)
212 addHintDefForValidatorTag (validator, tag);
213 }
214
215 @trusted
216 void saveToFile (string fname)
217 {
218 // is this really the only way you can set a type for JSONValue?
219 auto map = JSONValue (["null": 0]);
220 map.object.remove ("null");
221
222 foreach (hdef; hintDefs.byValue) {
223
224 auto jval = JSONValue (["text": JSONValue (hdef.text),
225 "severity": JSONValue (severityToString (hdef.severity))]);
226 if (hdef.internal)
227 jval.object["internal"] = JSONValue (true);
228 map.object[hdef.tag] = jval;
229 }
230
231 File file = File(fname, "w");
232 file.writeln (map.toJSON (true));
233 file.close ();
234 }
235
236 private auto addHintDefForValidatorTag (Validator validator, const string tag) @trusted
237 {
238 import appstream.Validator : IssueSeverity;
239 HintDefinition hdef;
240 hdef.valid = false;
241
242 immutable asgenTag = "asv-" ~ tag;
243 immutable explanation = validator.getTagExplanation (tag);
244 if (explanation.empty)
245 return hdef;
246 immutable asSeverity = validator.getTagSeverity (tag);
247
248 // Translate an AppStream validator hint severity to a generator
249 // severity. An error is just a warning here for now, as any error yields
250 // to an instant reject of the component (and as long as we extrcated *some*
251 // data, that seems a bit harsh)
252 HintSeverity severity;
253 switch (asSeverity) {
254 case IssueSeverity.ERROR:
255 severity = HintSeverity.WARNING;
256 break;
257 case IssueSeverity.WARNING:
258 severity = HintSeverity.WARNING;
259 break;
260 case IssueSeverity.INFO:
261 severity = HintSeverity.INFO;
262 break;
263 case IssueSeverity.PEDANTIC:
264 severity = HintSeverity.PEDANTIC;
265 break;
266 default:
267 severity = HintSeverity.UNKNOWN;
268 }
269
270 hdef.tag = asgenTag;
271 hdef.severity = severity;
272 hdef.text = "<code>{{location}}</code> - <em>{{hint}}</em><br/>%s".format (escapeXml (explanation));
273 hdef.valid = true;
274
275 hintDefs[asgenTag] = hdef;
276
277 return hdef;
278 }
279
280 @safe
281 HintDefinition getHintDef (string tag) pure
282 {
283 auto defP = (tag in hintDefs);
284 if (defP is null)
285 return HintDefinition ();
286 return *defP;
287 }
288
289 @safe
290 HintSeverity getSeverity (string tag) pure
291 {
292 auto hDef = getHintDef (tag);
293 return hDef.severity;
294 }
295 }
296
297 unittest
298 {
299 writeln ("TEST: ", "Issue Hints");
300
301 auto hint = GeneratorHint ("just-a-unittest", "org.freedesktop.foobar.desktop");
302 hint.vars = ["rainbows": "yes", "unicorns": "no", "storage": "towel"];
303 auto root = hint.toJsonNode ();
304
305 writeln (root.toJSON (true));
306
307 auto registry = HintTagRegistry.get ();
308 registry.getHintDef ("asv-relation-item-invalid-vercmp");
309 registry.saveToFile ("/tmp/testsuite-asgen-hints.json");
310 }
0 /*
1 * Copyright (C) 2016-2020 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this software. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.hint;
20 @safe:
21
22 import std.stdio;
23 import std.string;
24 import std.json;
25 import std.conv : to;
26
27 import appstream.Validator : Validator;
28 import appstream.c.types : IssueSeverity;
29 import ascompose.Hint : Hint;
30 import ascompose.Globals : Globals;
31 static import appstream.Utils;
32 alias AsUtils = appstream.Utils.Utils;
33
34 import asgen.logging;
35 import asgen.utils;
36
37
38 /**
39 * Each issue hint type has a severity assigned to it:
40
41 * ERROR: A fatal error which resulted in the component being excluded from the final metadata.
42 * WARNING: An issue which did not prevent generating meaningful data, but which is still serious
43 * and should be fixed (warning of this kind usually result in less data).
44 * INFO: Information, no immediate action needed (but will likely be an issue later).
45 * PEDANTIC: Information which may improve the data, but could also be ignored.
46 */
47
48 /**
49 * Definition of a issue hint.
50 */
51 struct HintDefinition
52 {
53 string tag; /// Unique issue tag
54 IssueSeverity severity; /// Issue severity
55 string explanation; /// Explanation template
56 }
57
58 /**
59 * Load all issue hints from file and register them globally.
60 */
61 void loadHintsRegistry () @trusted
62 {
63 import std.path;
64 static import std.file;
65
66 // find the hint definition file
67 auto hintsDefFile = getDataPath ("asgen-hints.json");
68 if (!std.file.exists (hintsDefFile)) {
69 logError ("Hints definition file '%s' was not found! This means we can not determine severity of issue tags and not render report pages.", hintsDefFile);
70 return;
71 }
72
73 // read the hints definition JSON file
74 auto f = File (hintsDefFile, "r");
75 string jsonData;
76 string line;
77 while ((line = f.readln ()) !is null)
78 jsonData ~= line;
79
80 auto hintDefsJSON = parseJSON (jsonData);
81
82 bool checkAlreadyLoaded = true;
83 foreach (ref tag; hintDefsJSON.object.byKey) {
84 auto j = hintDefsJSON[tag];
85 immutable severity = AsUtils.severityFromString (j["severity"].str);
86
87 if (checkAlreadyLoaded) {
88 if (Globals.hintTagSeverity (tag) != IssueSeverity.UNKNOWN) {
89 logDebug ("Global hints registry already loaded.");
90 break;
91 }
92 checkAlreadyLoaded = false;
93 }
94
95 string explanation = "";
96 if (j["text"].type == JSONType.array) {
97 foreach (l; j["text"].array)
98 explanation ~= l.str ~ "\n";
99 } else {
100 explanation = j["text"].str;
101 }
102
103 Globals.addHintTag (tag, severity, explanation);
104 }
105 }
106
107 /**
108 * Save information about all hint templates we know about to a JSON file.
109 */
110 void saveHintsRegistryToJsonFile (const string fname) @trusted
111 {
112 // FIXME: is this really the only way you can set a type for JSONValue?
113 auto map = JSONValue (["null": 0]);
114 map.object.remove ("null");
115
116 foreach (const htag; Globals.getHintTags) {
117 const hdef = retrieveHintDef (htag);
118 auto jval = JSONValue (["text": JSONValue (hdef.explanation),
119 "severity": JSONValue (AsUtils.severityToString (hdef.severity))]);
120 map.object[hdef.tag] = jval;
121 }
122
123 File file = File(fname, "w");
124 file.writeln (map.toJSON (true));
125 file.close ();
126 }
127
128 HintDefinition retrieveHintDef (string tag) @trusted
129 {
130 HintDefinition hdef;
131 hdef.tag = tag;
132 hdef.severity = Globals.hintTagSeverity (tag);
133 if (hdef.severity == IssueSeverity.UNKNOWN)
134 return HintDefinition ();
135 hdef.explanation = Globals.hintTagExplanation (tag);
136 return hdef;
137 }
138
139 auto toJsonValue (Hint hint) @trusted
140 {
141 auto hintList = hint.getExplanationVarsList;
142 string[string] vars;
143 for (uint i = 0; i < hintList.len; i++) {
144 if (i % 2 != 0)
145 continue;
146 const auto key = fromStringz (cast(char*) hintList.index (i)).to!string;
147 const auto value = fromStringz (cast(char*) hintList.index (i + 1)).to!string;
148 vars[key] = value;
149 }
150
151 return JSONValue(["tag": JSONValue (hint.getTag),
152 "vars": JSONValue (vars)]);
153 }
154
155 @trusted
156 unittest
157 {
158 import std.exception : assertThrown;
159 import glib.GException : GException;
160 writeln ("TEST: ", "Issue Hints");
161
162 assertThrown!GException (new Hint ("desktop-file-error"));
163
164 loadHintsRegistry ();
165 auto hint = new Hint ("desktop-file-error");
166
167 foreach (k, v; ["rainbows": "yes", "unicorns": "no", "storage": "towel"])
168 hint.addExplanationVar (k, v);
169 auto root = hint.toJsonValue ();
170 writeln (root.toJSON (true));
171
172 assert (retrieveHintDef ("asv-relation-item-invalid-vercmp").severity != IssueSeverity.UNKNOWN);
173 saveHintsRegistryToJsonFile ("/tmp/testsuite-asgen-hints.json");
174 }
+0
-512
src/asgen/image.d less more
0 /*
1 * Copyright (C) 2016-2020 Matthias Klumpp <matthias@tenstral.net>
2 *
3 * Licensed under the GNU Lesser General Public License Version 3
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser 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 software 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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this software. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 module asgen.image;
20
21 import std.stdio;
22 import std.string;
23 import std.conv : to;
24 import std.path : baseName;
25 import std.math;
26 import core.stdc.stdarg;
27 import core.stdc.stdio;
28
29 import asgen.bindings.cairo;
30 import asgen.bindings.rsvg;
31 import asgen.bindings.gdkpixbuf;
32
33 import glib.c.types;
34 import glib.c.functions;
35
36 import asgen.logging;
37 import asgen.config;
38 import asgen.font : Font;
39 import core.sync.mutex;
40
41 private __gshared Mutex fontconfigMutex = null;
42
43
44 enum ImageFormat {
45 UNKNOWN,
46 PNG,
47 JPEG,
48 GIF,
49 SVG,
50 SVGZ,
51 XPM
52 }
53
54 private void optimizePNG (string fname)
55 {
56 import glib.Spawn : Spawn, SpawnFlags;
57
58 auto conf = asgen.config.Config.get ();
59 if (!conf.feature.optipng)
60 return;
61
62 int exitStatus;
63 string opngStdout;
64 string opngStderr;
65 try {
66 // NOTE: Maybe add an option to run optipng with stronger optimization? (>= -o4)
67 Spawn.sync (null, // working directory
68 [conf.optipngBinary, fname ], // argv
69 [], // envp
70 SpawnFlags.LEAVE_DESCRIPTORS_OPEN,
71 null, // child setup
72 null, // user data
73 opngStdout, // out stdout
74 opngStderr, // out stderr
75 exitStatus);
76 } catch (Exception e) {
77 logError ("Failed to spawn optipng: %s", e.to!string);
78 return;
79 }
80
81 if (exitStatus != 0) {
82 if (!opngStdout.empty) {
83 if (opngStderr.empty)
84 opngStderr = opngStdout;
85 else
86 opngStderr = opngStderr ~ "\n" ~ opngStdout;
87 }
88 logWarning ("Optipng on '%s' failed with error code %s: %s", fname, exitStatus, opngStderr);
89 }
90 }
91
92 /**
93 * Helper method required so we do not modify the Fontconfig
94 * global state while reading it with another process.
95 *
96 * This prevents a weird deadlock when multiple threads are
97 * redering stuff that contains fonts.
98 **/
99 private void
100 enterFontconfigCriticalSection () @trusted
101 {
102 if (fontconfigMutex is null)
103 return;
104 fontconfigMutex.lock ();
105 }
106
107 /**
108 * Helper method required so we do not modify the Fontconfig
109 * global state while reading it with another process.
110 *
111 * This prevents a weird deadlock when multiple threads are
112 * redering stuff that contains fonts.
113 **/
114 private void
115 leaveFontconfigCriticalSection () @trusted
116 {
117 if (fontconfigMutex is null)
118 return;
119 fontconfigMutex.unlock ();
120 }
121
122 public void
123 setupFontconfigMutex () @trusted
124 {
125 fontconfigMutex = new Mutex;
126 }
127
128 final class Image
129 {
130
131 private:
132 GdkPixbuf pix;
133
134 public:
135
136 private void throwGError (GError *error, string pretext = null)
137 {
138 if (error !is null) {
139 auto msg = fromStringz (error.message).dup;
140 g_error_free (error);
141
142 if (pretext is null)
143 throw new Exception (to!string (msg));
144 else
145 throw new Exception (format ("%s: %s", pretext, to!string (msg)));
146 }
147 }
148
149 this (string fname)
150 {
151 GError *error = null;
152 pix = gdk_pixbuf_new_from_file (fname.toStringz (), &error);
153 throwGError (error, format ("Unable to open image '%s'", baseName (fname)));
154 }
155
156 this (const(ubyte)[] imgBytes, ImageFormat ikind)
157 {
158 import gio.c.functions;
159 import gio.MemoryInputStream;
160
161 auto istream = new MemoryInputStream ();
162 istream.addData (cast(ubyte[]) imgBytes, null);
163
164 GError *error = null;
165 pix = gdk_pixbuf_new_from_stream (cast(GInputStream*) istream.getMemoryInputStreamStruct (), null, &error);
166 throwGError (error, "Failed to load image data");
167 }
168
169 ~this ()
170 {
171 if (pix !is null)
172 g_object_unref (pix);
173 }
174
175 @property
176 uint width ()
177 {
178 return pix.gdk_pixbuf_get_width ();
179 }
180
181 @property
182 uint height ()
183 {
184 return pix.gdk_pixbuf_get_height ();
185 }
186
187 /**
188 * Scale the image to the given size.
189 */
190 void scale (uint newWidth, uint newHeight)
191 {
192 auto resPix = gdk_pixbuf_scale_simple (pix, newWidth, newHeight, GdkInterpType.BILINEAR);
193 if (resPix is null)
194 throw new Exception (format ("Scaling of image to %sx%s failed.", newWidth, newHeight));
195
196 // set our current image to the scaled version
197 g_object_unref (pix);
198 pix = resPix;
199 }
200
201 /**
202 * Scale the image to the given width, preserving
203 * its aspect ratio.
204 */
205 void scaleToWidth (uint newWidth)
206 {
207 float scaleFactor = cast(float) newWidth / cast (float) width;
208 uint newHeight = to!uint (floor (height * scaleFactor));
209
210 scale (newWidth, newHeight);
211 }
212
213 /**
214 * Scale the image to the given height, preserving
215 * its aspect ratio.
216 */
217 void scaleToHeight (uint newHeight)
218 {
219 float scaleFactor = cast(float) newHeight / cast(float) height;
220 immutable newWidth = to!uint (floor (width * scaleFactor));
221
222 scale (newWidth, newHeight);
223 }
224
225 /**
226 * Scale the image to fir in a square with the given edge length,
227 * and keep its aspect ratio.
228 */
229 void scaleToFit (uint size)
230 {
231 if (height > width) {
232 scaleToHeight (size);
233 } else {
234 scaleToWidth (size);
235 }
236 }
237
238 void savePng (string fname)
239 {
240 GError *error = null;
241 gdk_pixbuf_save (pix, fname.toStringz (), "png", &error, null);
242 throwGError (error);
243
244 optimizePNG (fname);
245 }
246 }
247
248 final class Canvas
249 {
250
251 private:
252 cairo_surface_p srf;
253 cairo_p cr;
254
255 int width_;
256 int height_;
257
258 public:
259
260 this (int w, int h)
261 {
262 srf = cairo_image_surface_create (cairo_format_t.FORMAT_ARGB32, w, h);
263 cr = cairo_create (srf);
264
265 width_ = w;
266 height_ = h;
267 }
268
269 ~this ()
270 {
271 if (cr !is null)
272 cairo_destroy (cr);
273 if (srf !is null)
274 cairo_surface_destroy (srf);
275 }
276
277 @property
278 uint width ()
279 {
280 return width_;
281 //! return srf.cairo_image_surface_get_width ();
282 }
283
284 @property
285 uint height ()
286 {
287 return height_;
288 //! return srf.cairo_image_surface_get_height ();
289 }
290
291 void renderSvg (const(ubyte)[] svgBytes)
292 {
293 // NOTE: unfortunately, Cairo/RSvg uses Fontconfig internally, so
294 // we need to lock this down since a parallel-processed font
295 // might need to access this too.
296 // This can likely be optimized by checking whether it's really
297 // a Font that is holding the lock (= make only fonts increase the
298 // Mutex counter)
299 enterFontconfigCriticalSection ();
300
301 auto handle = rsvg_handle_new ();
302 scope (exit) {
303 g_object_unref (handle);
304 leaveFontconfigCriticalSection ();
305 }
306
307 auto svgBSize = ubyte.sizeof * svgBytes.length;
308 GError *error = null;
309 rsvg_handle_write (handle, cast(ubyte*) svgBytes, svgBSize, &error);
310 if (error !is null) {
311 auto msg = fromStringz (error.message).dup;
312 g_error_free (error);
313 throw new Exception (to!string (msg));
314 }
315
316 rsvg_handle_close (handle, &error);
317 if (error !is null) {
318 auto msg = fromStringz (error.message).dup;
319 g_error_free (error);
320 throw new Exception (to!string (msg));
321 }
322
323 RsvgDimensionData dims;
324 rsvg_handle_get_dimensions (handle, &dims);
325
326 auto w = cast(double) cairo_image_surface_get_width (srf);
327 auto h = cast(double) cairo_image_surface_get_height (srf);
328
329 // cairo_translate (cr, (w - dims.width) / 2, (h - dims.height) / 2);
330 cairo_scale (cr, w / dims.width, h / dims.height);
331
332 cr.cairo_save ();
333 scope (exit) cr.cairo_restore ();
334 if (!rsvg_handle_render_cairo (handle, cr))
335 throw new Exception ("Rendering of SVG images failed!");
336 }
337
338 /**
339 * Draw a simple line of text without linebreaks to fill the canvas.
340 **/
341 void drawTextLine (const ref Font font, string text, uint borderWidth = 4)
342 {
343 import asgen.bindings.freetype : FT_LOAD_DEFAULT;
344 enterFontconfigCriticalSection ();
345 scope (exit) leaveFontconfigCriticalSection ();
346
347 auto cff = cairo_ft_font_face_create_for_ft_face (font.fontFace, FT_LOAD_DEFAULT);
348 scope (exit) cairo_font_face_destroy (cff);
349
350 // set font face for Cairo surface
351 auto status = cairo_font_face_status (cff);
352 if (status != cairo_status_t.STATUS_SUCCESS)
353 throw new Exception ("Could not set font face for Cairo: %s".format (to!string (status)));
354 cairo_set_font_face (cr, cff);
355
356 cairo_text_extents_t te;
357 uint textSize = 128;
358 while (textSize-- > 0) {
359 cairo_set_font_size (cr, textSize);
360 cairo_text_extents (cr, text.toStringz, &te);
361 if (te.width <= 0.01f || te.height <= 0.01f)
362 continue;
363 if (te.width < this.width - (borderWidth * 2) &&
364 te.height < this.height - (borderWidth * 2))
365 break;
366 }
367
368 // draw text
369 cairo_move_to (cr,
370 (this.width / 2) - te.width / 2 - te.x_bearing,
371 (this.height / 2) - te.height / 2 - te.y_bearing);
372 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
373 cairo_show_text (cr, text.toStringz);
374
375 cairo_save (cr);
376 }
377
378 /**
379 * Draw a longer text with linebreaks.
380 */
381 void drawText (const ref Font font, string text, const uint borderWidth = 4, const uint linePad = 2)
382 {
383 import asgen.bindings.freetype : FT_LOAD_DEFAULT;
384 enterFontconfigCriticalSection ();
385 scope (exit) leaveFontconfigCriticalSection ();
386
387 auto cff = cairo_ft_font_face_create_for_ft_face (font.fontFace, FT_LOAD_DEFAULT);
388 scope (exit) cairo_font_face_destroy (cff);
389
390 // set font face for Cairo surface
391 auto status = cairo_font_face_status (cff);
392 if (status != cairo_status_t.STATUS_SUCCESS)
393 throw new Exception (format ("Could not set font face for Cairo: %s", to!string (status)));
394 cairo_set_font_face (cr, cff);
395
396 // calculate best font size
397 uint linePadding = linePad;
398 auto lines = text.split ("\n");
399 string longestLine;
400 if (lines.length <= 1) {
401 linePadding = 0;
402 longestLine = text;
403 } else {
404 ulong ll = 0;
405 longestLine = lines[0];
406 foreach (line; lines) {
407 if (line.length > ll)
408 longestLine = line;
409 ll = line.length;
410 }
411 }
412
413 cairo_text_extents_t te;
414 uint text_size = 128;
415 while (text_size-- > 0) {
416 cairo_set_font_size (cr, text_size);
417 cairo_text_extents (cr, longestLine.toStringz, &te);
418 if (te.width <= 0.01f || te.height <= 0.01f)
419 continue;
420 if (te.width < this.width - (borderWidth * 2) &&
421 (te.height * lines.length + linePadding) < this.height - (borderWidth * 2))
422 break;
423 }
424
425 // center text and draw it
426 auto xPos = (this.width / 2) - te.width / 2 - te.x_bearing;
427 auto teHeight = te.height * lines.length + linePadding * (lines.length.to!long - 1);
428 auto yPos = (teHeight / 2) - teHeight / 2 - te.y_bearing + borderWidth;
429 cairo_move_to (cr, xPos, yPos);
430 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
431
432 foreach (line; lines) {
433 cairo_show_text (cr, line.toStringz ());
434 yPos += te.height + linePadding;
435 cairo_move_to (cr, xPos, yPos);
436 }
437 cairo_save (cr);
438 }
439
440 void savePng (string fname)
441 {
442 auto status = cairo_surface_write_to_png (srf, fname.toStringz ());
443 if (status != cairo_status_t.STATUS_SUCCESS)
444 throw new Exception (format ("Could not save canvas to PNG: %s", to!string (status)));
445
446 optimizePNG (fname);
447 }
448 }
449
450 unittest
451 {
452 import std.file : getcwd;
453 import std.path : buildPath;
454 import asgen.utils : getTestSamplesDir;
455 writeln ("TEST: ", "Image");
456
457 // check if our GdkPixbuf supports the minimum amount of image formats we need
458 const pixbufFormatNames = gdkPixbufGetFormatNames ();
459 assert ("png" in pixbufFormatNames);
460 assert ("svg" in pixbufFormatNames);
461 assert ("jpeg" in pixbufFormatNames);
462
463 auto sampleImgPath = buildPath (getTestSamplesDir (), "appstream-logo.png");
464 writeln ("Loading image (file)");
465 auto img = new Image (sampleImgPath);
466
467 writeln ("Scaling image");
468 assert (img.width == 134);
469 assert (img.height == 132);
470 img.scale (64, 64);
471 assert (img.width == 64);
472 assert (img.height == 64);
473
474 writeln ("Storing image");
475 img.savePng ("/tmp/ag-iscale_test.png");
476
477 writeln ("Loading image (data)");
478 ubyte[] data;
479 auto f = File (sampleImgPath, "r");
480 while (!f.eof) {
481 char[300] buf;
482 data ~= f.rawRead (buf);
483 }
484
485 img = new Image (data, ImageFormat.PNG);
486 writeln ("Scaling image (data)");
487 img.scale (124, 124);
488 writeln ("Storing image (data)");
489 img.savePng ("/tmp/ag-iscale-d_test.png");
490
491 writeln ("Rendering SVG");
492 auto sampleSvgPath = buildPath (getTestSamplesDir (), "table.svgz");
493 data = null;
494 f = File (sampleSvgPath, "r");
495 while (!f.eof) {
496 char[300] buf;
497 data ~= f.rawRead (buf);
498 }
499 auto cv = new Canvas (512, 512);
500 cv.renderSvg (data);
501 writeln ("Saving rendered PNG");
502 cv.savePng ("/tmp/ag-svgrender_test1.png");
503
504 writeln ("Font rendering");
505 auto font = new Font (buildPath (getTestSamplesDir (), "NotoSans-Regular.ttf"));
506
507 cv = new Canvas (400, 100);
508 cv.drawText (font,
509 "Hello World!\nSecond Line!\nThird line - äöüß!\nA very, very, very long line.");
510 cv.savePng ("/tmp/ag-fontrender_test1.png");
511 }
1313 #
1414 asgen_sources = [
1515 '../app.d',
16 'bindings/appstream_utils.d',
17 'bindings/cairo.d',
18 'bindings/fontconfig.d',
19 'bindings/freetype.d',
20 'bindings/freetypeTypes.d',
21 'bindings/gdkpixbuf.d',
2216 'bindings/libarchive.d',
2317 'bindings/lmdb.d',
24 'bindings/pango.d',
25 'bindings/rsvg.d',
2618 'bindings/soup.d',
2719 'containers/package.d',
2820 'containers/hash.d',
3527 'downloader.d',
3628 'engine.d',
3729 'extractor.d',
38 'font.d',
3930 'handlers/desktopparser.d',
4031 'handlers/fonthandler.d',
4132 'handlers/iconhandler.d',
4233 'handlers/localehandler.d',
43 'handlers/metainfoparser.d',
44 'handlers/metainfovalidator.d',
4534 'handlers/package.d',
4635 'handlers/screenshothandler.d',
47 'hint.d',
48 'image.d',
36 'hintregistry.d',
4937 'logging.d',
5038 'mustache.d',
5139 'reportgenerator.d',
6351 'backends/dummy/pkgindex.d',
6452
6553 'backends/alpinelinux/package.d',
54 'backends/alpinelinux/apkindexutils.d',
6655 'backends/alpinelinux/apkpkg.d',
6756 'backends/alpinelinux/apkpkgindex.d',
6857
9685 gir_bind_dir],
9786 dependencies: [glibd_dep,
9887 appstream_dep,
88 ascompose_dep,
9989 lmdb_dep,
10090 archive_dep,
101 soup_dep,
102 cairo_dep,
103 gdkpixbuf_dep,
104 rsvg_dep,
105 freetype_dep,
106 fontconfig_dep,
107 pango_dep],
91 soup_dep],
10892 link_with: [girbind_lib],
10993 d_import_dirs: [data_import_dirs],
11094 install: true
117101 gir_bind_dir],
118102 dependencies: [glibd_dep,
119103 appstream_dep,
104 ascompose_dep,
120105 lmdb_dep,
121106 archive_dep,
122 soup_dep,
123 cairo_dep,
124 gdkpixbuf_dep,
125 rsvg_dep,
126 freetype_dep,
127 fontconfig_dep,
128 pango_dep],
107 soup_dep],
129108 link_with: [girbind_lib],
130109 d_import_dirs: [data_import_dirs],
131110 d_unittest: true
3131
3232 import mustache;
3333 import appstream.Metadata;
34 import appstream.c.types : IssueSeverity;
35 import ascompose.Hint : Hint;
36 static import appstream.Utils;
37 alias AsUtils = appstream.Utils.Utils;
3438
3539 import asgen.defines : ASGEN_VERSION;
3640 import asgen.utils;
3741 import asgen.config;
3842 import asgen.logging;
39 import asgen.hint;
43 import asgen.hintregistry;
4044 import asgen.backends.interfaces;
4145 import asgen.datastore;
4246
127131 mustache.ext = "html";
128132
129133 // create version information to display on every page
130 import asgen.bindings.appstream_utils : as_get_appstream_version;
131 import std.string : fromStringz;
132 versionInfo = "%s, AS: %s".format (ASGEN_VERSION, as_get_appstream_version.fromStringz);
134 versionInfo = "%s, AS: %s".format (ASGEN_VERSION, AsUtils.appstreamVersion);
133135 }
134136
135137 private string[] splitBlockData (string str, string blockType)
448450 DataSummary dsum;
449451
450452 logInfo ("Collecting data about hints and available metainfo for %s/%s", suiteName, section);
451 auto hintTagRegistry = HintTagRegistry.get ();
452453
453454 auto dtype = conf.metadataType;
454455 auto mdata = scoped!Metadata ();
565566
566567 foreach (jhint; jhints.array) {
567568 auto tag = jhint["tag"].str;
568 auto hdef = hintTagRegistry.getHintDef (tag);
569 if (hdef.tag is null) {
569
570 Hint hint;
571 try {
572 hint = new Hint (tag);
573 } catch (Exception e) {
570574 logError ("Encountered invalid tag '%s' in component '%s' of package '%s'", tag, cid, pkid);
571575
572576 // emit an internal error, invalid tags shouldn't happen
573 hdef = hintTagRegistry.getHintDef ("internal-unknown-tag");
574 assert (hdef.tag !is null);
577 tag = "internal-unknown-tag";
578 hint = new Hint (tag);
575579 jhint["vars"] = ["tag": tag];
576580 }
577581
578582 // render the full message using the static template and data from the hint
579 auto context = new Mustache.Context;
580 foreach (var; jhint["vars"].object.byKey ()) {
581 context[var] = jhint["vars"][var].str;
582 }
583 auto msg = mustache.renderString (hdef.text, context);
583 foreach (var; jhint["vars"].object.byKey ())
584 hint.addExplanationVar (var, jhint["vars"][var].str);
585 const msg = hint.formatExplanation ();
584586
585587 // add the new hint to the right category
586 auto severity = hintTagRegistry.getSeverity (tag);
587 if (severity == HintSeverity.INFO) {
588 const severity = hint.getSeverity;
589 if (severity == IssueSeverity.INFO) {
588590 he.infos ~= HintTag (tag, msg);
589591 pkgsummary.infoCount++;
590 } else if (severity == HintSeverity.WARNING) {
592 } else if (severity == IssueSeverity.WARNING) {
591593 he.warnings ~= HintTag (tag, msg);
592594 pkgsummary.warningCount++;
593 } else if (severity == HintSeverity.PEDANTIC) {
595 } else if (severity == IssueSeverity.PEDANTIC) {
594596 // We ignore pedantic issues completely for now
595597 } else {
596598 he.errors ~= HintTag (tag, msg);
00 /*
1 * Copyright (C) 2016-2017 Matthias Klumpp <matthias@tenstral.net>
1 * Copyright (C) 2016-2021 Matthias Klumpp <matthias@tenstral.net>
22 *
33 * Licensed under the GNU Lesser General Public License Version 3
44 *
1919 module asgen.result;
2020
2121 import std.stdio;
22 import std.string;
22 import std.string : format, fromStringz, toStringz;
2323 import std.array : empty;
2424 import std.conv : to;
25 import std.algorithm : endsWith;
2526 import std.json;
2627 import appstream.Component;
28 import appstream.c.types : BundleKind;
29 import ascompose.Hint : Hint;
30 import ascompose.Result : Result;
31 import ascompose.c.types : AscHint;
32 static import appstream.Utils;
33 alias AsUtils = appstream.Utils.Utils;
2734
2835 import asgen.containers : HashMap;
29 import asgen.hint;
36 import asgen.hintregistry;
3037 import asgen.utils : buildCptGlobalID;
3138 import asgen.backends.interfaces;
3239 import asgen.config : Config;
5259 * Holds metadata generator result(s) and issue hints
5360 * for a single package.
5461 */
55 final class GeneratorResult
62 final class GeneratorResult : Result
5663 {
5764
58 private:
59 Component[string] cpts;
60 string[Component] cptGCID;
61 HashMap!(string, string) mdataHashes;
62 HashMap!(string, GeneratorHint[]) hints;
63
6465 public:
65 immutable string pkid;
66 immutable string pkgname;
6766 Package pkg;
6867
69
7068 this (Package pkg)
7169 {
72 this.pkid = pkg.id;
73 this.pkgname = pkg.name;
70 super();
71 setBundleKind (BundleKind.PACKAGE);
72 setBundleId (pkg.name);
7473 this.pkg = pkg;
7574 }
7675
77 @safe
78 bool packageIsIgnored () pure
79 {
80 return (cpts.length == 0) && (hints.length == 0);
81 }
82
83 @safe
84 Component getComponent (string id) pure
85 {
86 auto ptr = (id in cpts);
87 if (ptr is null)
88 return null;
89 return *ptr;
90 }
91
92 @trusted
93 Component[] getComponents () pure
94 {
95 return cpts.values ();
76 @property
77 string pkid ()
78 {
79 return pkg.id;
9680 }
9781
9882 @trusted
9983 bool isIgnored (Component cpt)
10084 {
101 return getComponent (cpt.getId ()) is null;
102 }
103
104 @trusted
105 void updateComponentGCID (Component cpt, string data)
106 {
107 import std.digest.md;
108
109 auto cid = cpt.getId ();
110 if (data.empty) {
111 cptGCID[cpt] = buildCptGlobalID (cid, "???-NO_CHECKSUM-???");
112 return;
113 }
114
115 auto oldHashP = (cid in mdataHashes);
116 string oldHash = "";
117 if (oldHashP !is null)
118 oldHash = *oldHashP;
119
120 auto hash = md5Of (oldHash ~ data);
121 auto checksum = toHexString (hash);
122 auto newHash = to!string (checksum);
123
124 mdataHashes[cid] = newHash;
125 cptGCID[cpt] = buildCptGlobalID (cid, newHash);
126 }
127
128 @trusted
129 void addComponent (Component cpt, string data = "")
130 {
131 string cid = cpt.getId;
132 if (cid.empty)
133 throw new Exception ("Can not add component from '%s' without ID to results set: %s".format (this.pkid, cpt.toString));
134
135 // web applications, operating systems, repositories
136 // and component-removal merges don't (need to) have a package name set
137 if (cpt.getKind != ComponentKind.WEB_APP &&
138 cpt.getKind != ComponentKind.OPERATING_SYSTEM &&
139 cpt.getMergeKind != MergeKind.REMOVE_COMPONENT) {
140 if (pkg.kind != PackageKind.FAKE)
141 cpt.setPkgnames ([this.pkgname]);
142 }
143
144 cpts[cid] = cpt;
145 updateComponentGCID (cpt, data);
146 }
147
148 @safe
149 void dropComponent (string cid) pure
150 {
151 auto cpt = getComponent (cid);
152 if (cpt is null)
153 return;
154 cpts.remove (cid);
155 cptGCID.remove (cpt);
85 return getComponent (cpt.getId) is null;
15686 }
15787
15888 /**
177107 immutable cid = id.getId ();
178108 }
179109
180 auto hint = GeneratorHint (tag, cid);
181 hint.setVars (params);
182 if (cid in hints)
183 hints[cid] ~= hint;
184 else
185 hints[cid] = [hint];
186
187 // we stop dealing with this component when we encounter a fatal
188 // error.
189 if (hint.isError) {
190 dropComponent (cid);
191 return false;
192 }
193
194 return true;
110 string[] paramsFlat;
111 foreach (const ref varName, ref varValue; params)
112 paramsFlat ~= [varName, varValue];
113
114 return addHintByCid (cid, tag, paramsFlat);
195115 }
196116
197117 /**
218138 */
219139 string hintsToJson ()
220140 {
221 if (hints.length == 0)
141 if (hintsCount () == 0)
222142 return null;
223143
224 // is this really the only way you can set a type for JSONValue?
144 // FIXME: is this really the only way you can set a type for JSONValue?
225145 auto map = JSONValue (["null": 0]);
226146 map.object.remove ("null");
227147
228 foreach (cid; hints.byKey) {
229 auto cptHints = hints[cid];
148 foreach (ref cid; getComponentIdsWithHints ()) {
149 auto cptHints = getHints (cid);
230150 auto hintNodes = JSONValue ([0, 0]);
231151 hintNodes.array = [];
232 foreach (ref hint; cptHints) {
233 hintNodes.array ~= hint.toJsonNode ();
152
153 for (uint i = 0; i < cptHints.len; i++) {
154 auto hint = new Hint (cast (AscHint*) cptHints.index (i));
155 hintNodes.array ~= hint.toJsonValue;
234156 }
235
236157 map.object[cid] = hintNodes;
237158 }
238159
247168 {
248169 auto conf = Config.get ();
249170
250 // we need to duplicate the associative array, because the addHint() function
251 // may remove entries from "cpts", breaking our foreach loop.
252 foreach (cpt; cpts.dup.byValue) {
171 // the fetchComponents() method creates a new PtrArray with references to the #AsComponent instances.
172 // so we are free to call addHint & Co. which may remove components from the pool.
173 auto cptsPtrArray = fetchComponents ();
174 for (uint i = 0; i < cptsPtrArray.len; i++) {
175 auto cpt = new Component (cast (AsComponent*) cptsPtrArray.index (i));
253176 immutable ckind = cpt.getKind;
254177 cpt.setActiveLocale ("C");
255178
288211
289212 // strip out any release artifact information of components that have a
290213 // distribution package association
291 if (!conf.feature.propagateMetainfoArtifacts) {
214 if (!conf.feature.propagateMetaInfoArtifacts) {
292215 import appstream.c.functions : as_release_get_artifacts;
293216 import glib.c.functions : g_ptr_array_set_size;
294217
295218 auto relArr = cpt.getReleases;
296 for (uint i = 0; i < relArr.len; i++) {
297 auto releasePtr = cast (AsRelease*) relArr.index (i);
219 for (uint j = 0; j < relArr.len; j++) {
220 auto releasePtr = cast (AsRelease*) relArr.index (j);
298221 g_ptr_array_set_size (as_release_get_artifacts (releasePtr), 0);
299222 }
300223 }
345268 cpt.setDescription (desc, lang);
346269 desc_added = true;
347270 }
271
272 if (conf.feature.warnNoMetaInfo) {
273 if (!addHint (cpt, "no-metainfo"))
274 continue;
275 }
276
348277 if (desc_added) {
349 if (!addHint (cpt, "description-from-package"))
350 continue;
278 if (!conf.feature.warnNoMetaInfo) {
279 if (!addHint (cpt, "description-from-package"))
280 continue;
281 }
351282 } else {
352 import asgen.bindings.appstream_utils : componentKindToString;
353
354283 if ((ckind == ComponentKind.DESKTOP_APP) ||
355284 (ckind == ComponentKind.CONSOLE_APP) ||
356285 (ckind == ComponentKind.WEB_APP)) {
357 if (!addHint (cpt, "description-missing", ["kind": componentKindToString (ckind)]))
286 if (!addHint (cpt, "description-missing", ["kind": AsUtils.componentKindToString (ckind)]))
358287 continue;
359288 }
360289 }
390319
391320 } // end of components loop
392321 }
393
394 /**
395 * Return the number of components we've found.
396 **/
397 @safe
398 ulong componentsCount () pure
399 {
400 return cpts.length;
401 }
402
403 /**
404 * Return the number of hints that have been emitted.
405 **/
406 @safe
407 ulong hintsCount () pure
408 {
409 return hints.length;
410 }
411
412 @safe
413 string gcidForComponent (Component cpt) pure
414 {
415 auto cgp = (cpt in cptGCID);
416 if (cgp is null)
417 return null;
418 return *cgp;
419 }
420
421 @trusted
422 string[] getGCIDs () pure
423 {
424 return cptGCID.values ();
425 }
426
427322 }
428323
429324 unittest
430325 {
431326 import asgen.backends.dummy.dummypkg;
432327 writeln ("TEST: ", "GeneratorResult");
328 loadHintsRegistry ();
433329
434330 auto pkg = new DummyPackage ("foobar", "1.0", "amd64");
435331 auto res = new GeneratorResult (pkg);
436332
437333 auto vars = ["rainbows": "yes", "unicorns": "no", "storage": "towel"];
438 res.addHint ("org.freedesktop.foobar.desktop", "just-a-unittest", vars);
439 res.addHint ("org.freedesktop.awesome-bar.desktop", "metainfo-chocolate-missing", "Nothing is good without chocolate. Add some.");
440 res.addHint ("org.freedesktop.awesome-bar.desktop", "metainfo-does-not-frobnicate", "Frobnicate functionality is missing.");
334 res.addHint ("org.freedesktop.foobar.desktop", "desktop-file-hidden-set", vars);
335 res.addHint ("org.freedesktop.awesome-bar.desktop", "metainfo-validation-error", "Nothing is good without chocolate. Add some.");
336 res.addHint ("org.freedesktop.awesome-bar.desktop", "screenshot-video-check-failed", "Frobnicate functionality is missing.");
441337
442338 writeln (res.hintsToJson ());
443339 }
3232 import std.traits : Unqual, hasIndirections;
3333 static import std.file;
3434
35 import glib.Bytes : Bytes;
3536 import appstream.Component;
3637 import appstream.Icon;
38 static import appstream.Utils;
39 alias AsUtils = appstream.Utils.Utils;
3740
3841 import asgen.defines;
3942 import asgen.logging;
165168 * (in a short check on Debian, it covered all TLDs in use there)
166169 */
167170 @trusted
168 bool isTopLevelDomain (const string value) pure
169 {
170 import asgen.bindings.appstream_utils : as_utils_is_tld;
171 bool isTopLevelDomain (const string value)
172 {
171173 if (value.empty)
172174 return false;
173175
174 return as_utils_is_tld (value.toStringz);
176 return AsUtils.isTld (value);
175177 }
176178
177179 /**
183185 * associated with this component.
184186 **/
185187 @trusted
186 string buildCptGlobalID (string cid, string checksum, bool allowNoChecksum = false) pure
188 string buildCptGlobalID (string cid, string checksum, bool allowNoChecksum = false)
187189 in { assert (cid.length >= 2); }
188190 do
189191 {
217219 * Get the component-id back from a global component-id.
218220 */
219221 @trusted
220 string getCidFromGlobalID (string gcid) pure
222 string getCidFromGlobalID (string gcid)
221223 {
222224 auto parts = gcid.split ("/");
223225 if (parts.length != 4)
559561 return bname;
560562 }
561563
564 auto toStaticGBytes (const(ubyte)[] data) @trusted
565 {
566 import glib.c.functions : g_bytes_new_static;
567 auto nc = cast(ubyte[]) data;
568 auto gBytes = g_bytes_new_static (nc.ptr, cast(size_t) nc.length);
569 return new Bytes (gBytes, true);
570 }
571
572 auto toStaticGBytes (ubyte[] data) @trusted
573 {
574 import glib.c.functions : g_bytes_new_static;
575 auto gBytes = g_bytes_new_static (data.ptr, cast(size_t) data.length);
576 return new Bytes (gBytes, true);
577 }
578
579 auto toStaticGBytes (const string data) @trusted
580 {
581 return toStaticGBytes (cast(immutable(ubyte)[]) data.representation);
582 }
583
562584 @trusted
563585 unittest
564586 {
335335 while (archive_read_next_header (ar, &en) == ARCHIVE_OK) {
336336 auto pathname = fromStringz (archive_entry_pathname (en));
337337
338 auto m = matchFirst (pathname, re);
338 const m = matchFirst (pathname, re);
339339 if (!m.empty) {
340340 auto fdest = buildPath (destdir, baseName (pathname));
341341 this.extractEntryTo (ar, fdest);
66 RUN apt-get update -qq
77
88 # install build essentials
9 RUN apt-get install -yq eatmydata git gcc gdc ldc gdc-10
9 RUN apt-get install -yq eatmydata git gcc gdc ldc
1010
1111 # install dependencies used by both appstream and appstream-generator
1212 RUN eatmydata apt-get install -yq --no-install-recommends \
3636 libpango1.0-dev
3737
3838 # Misc
39 RUN apt-get install -yq --no-install-recommends curl gnupg ffmpeg
39 RUN apt-get install -yq --no-install-recommends \
40 curl \
41 gnupg \
42 ffmpeg \
43 yarnpkg
4044
4145 # Install dscanner
4246 RUN mkdir -p /usr/local/bin/
43 RUN curl -L https://github.com/dlang-community/D-Scanner/releases/download/v0.5.11/dscanner-v0.5.11-linux-x86_64.tar.gz -o /tmp/dscanner.tar.gz
47 RUN curl -L https://github.com/dlang-community/D-Scanner/releases/download/v0.11.0/dscanner-v0.11.0-linux-x86_64.tar.gz -o /tmp/dscanner.tar.gz
4448 RUN tar -xzf /tmp/dscanner.tar.gz -C /usr/local/bin/
4549 RUN rm /tmp/dscanner.tar.gz
4650 RUN dscanner --version
47
48 # JavaScript stuff
49 RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
50 RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
51 RUN apt-get update -qq
52 RUN apt-get install -yq --no-install-recommends nodejs yarn
5351
5452 # build & install the current Git snapshot of AppStream
5553 RUN mkdir /build-tmp
6159 meson --prefix=/usr \
6260 -Dmaintainer=true \
6361 -Dapt-support=true \
62 -Dcompose=true \
6463 -Dapidocs=false \
6564 ..
6665 RUN cd /build-tmp/appstream/build && \
0 #!/bin/sh
1 #
2 # This script is supposed to run inside the AppStream Generator Docker container
3 # on the CI system.
4 #
5 set -e
6 export LANG=C.UTF-8
7
8 if [ "$DC" = "ldc" ];
9 then
10 export DC=ldc2
11 fi
12
13 echo "D compiler: $DC"
14 set -v
15 $DC --version
16 meson --version
17
18 #
19 # Build & Test
20 #
21 mkdir -p build && cd build
22 meson -Ddownload-js=true ..
23 ninja -j8
24
25 # Run tests
26 ninja test -v
27
28 # Test install
29 DESTDIR=/tmp/install-ninja ninja install
30 cd ..
31
32 #
33 # Other checks
34 #
35
36 # run D-Scanner
37 ./tests/ci/run-dscanner.py . tests/dscanner.ini
+0
-39
tests/ci/build_and_test.sh less more
0 #!/bin/sh
1 #
2 # This script is supposed to run inside the AppStream Generator Docker container
3 # on the CI system.
4 #
5 set -e
6 export LANG=C.UTF-8
7
8 # prefer GDC 10 over the default for now
9 if [ "$DC" = "gdc" ];
10 then
11 export DC="gdc-10"
12 fi
13
14 echo "D compiler: $DC"
15 set -v
16 $DC --version
17 meson --version
18
19 #
20 # Build & Test
21 #
22 mkdir -p build && cd build
23 meson -Ddownload-js=true ..
24 ninja -j8
25
26 # Run tests
27 ninja test -v
28
29 # Test install
30 DESTDIR=/tmp/install-ninja ninja install
31 cd ..
32
33 #
34 # Other checks
35 #
36
37 # run D-Scanner
38 ./tests/ci/run-dscanner.py . tests/dscanner.ini
2323 unused_variable_check="enabled"
2424 ; Checks for unused labels
2525 unused_label_check="enabled"
26 ; Checks for unused function parameters
27 unused_parameter_check="disabled"
2628 ; Checks for duplicate attributes
2729 duplicate_attribute="enabled"
2830 ; Checks that opEquals and toHash are both defined or neither are defined
9193 assert_without_msg="disabled"
9294 ; Check indent of if constraints
9395 if_constraints_indent="enabled"
96 ; Check for @trusted applied to a bigger scope than a single function
97 trust_too_much="enabled"
98 ; Check for redundant storage classes on variable declarations
99 redundant_storage_classes="enabled"
100 ; Check for unused function return values
101 unused_result="disabled"
102 ; ModuleFilters for selectively enabling (+std) and disabling (-std.internal) i
103 ; ndividual checks
94104
95105 [analysis.config.ModuleFilters]
96106 style_check = "-asgen.bindings"
+0
-88
tests/samples/Noto.LICENSE less more
0 Copyright: 2010-2015, Google Corporation
1 License: SIL-1.1
2 PREAMBLE
3 .
4 The goals of the Open Font License (OFL) are to stimulate worldwide
5 development of collaborative font projects, to support the font
6 creation efforts of academic and linguistic communities, and to provide
7 a free and open framework in which fonts may be shared and improved in
8 partnership with others.
9 .
10 The OFL allows the licensed fonts to be used, studied, modified and
11 redistributed freely as long as they are not sold by themselves. The
12 fonts, including any derivative works, can be bundled, embedded,
13 redistributed and/or sold with any software provided that any reserved
14 names are not used by derivative works. The fonts and derivatives,
15 however, cannot be released under any other type of license. The
16 requirement for fonts to remain under this license does not apply to
17 any document created using the fonts or their derivatives.
18 .
19 DEFINITIONS
20 .
21 "Font Software" refers to the set of files released by the Copyright
22 Holder(s) under this license and clearly marked as such. This may
23 include source files, build scripts and documentation.
24 .
25 "Reserved Font Name" refers to any names specified as such after the
26 copyright statement(s).
27 .
28 "Original Version" refers to the collection of Font Software components
29 as distributed by the Copyright Holder(s).
30 .
31 "Modified Version" refers to any derivative made by adding to,deleting,
32 or substituting -- in part or in whole -- any of the components of the
33 Original Version, by changing formats or by porting the Font Software
34 to a new environment.
35 .
36 "Author" refers to any designer, engineer, programmer, technical writer
37 or other person who contributed to the Font Software.
38 .
39 PERMISSION & CONDITIONS
40 .
41 Permission is hereby granted, free of charge, to any person obtaining a
42 copy of the Font Software, to use, study, copy, merge, embed, modify,
43 redistribute, and sell modified and unmodified copies of the Font
44 Software, subject to the following conditions:
45 .
46 1) Neither the Font Software nor any of its individual components, in
47 Original or Modified Versions, may be sold by itself.
48 .
49 2) Original or Modified Versions of the Font Software may be bundled,
50 redistributed and/or sold with any software, provided that each copy
51 contains the above copyright notice and this license. These can be
52 included either as stand-alone text files, human-readable headers or in
53 the appropriate machine-readable metadata fields within text or binary
54 files as long as those fields can be easily viewed by the user.
55 .
56 3) No Modified Version of the Font Software may use the Reserved Font
57 Name(s) unless explicit written permission is granted by the
58 corresponding Copyright Holder. This restriction only applies to the
59 primary font name as presented to the users.
60 .
61 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
62 Software shall not be used to promote, endorse or advertise any
63 Modified Version, except to acknowledge the contribution(s) of the
64 Copyright Holder(s) and the Author(s) or with their explicit written
65 permission.
66 .
67 5) The Font Software, modified or unmodified, in part or in whole, must
68 be distributed entirely under this license, and must not be distributed
69 under any other license. The requirement for fonts to remain under this
70 license does not apply to any document created using the Font Software.
71 .
72 TERMINATION
73 .
74 This license becomes null and void if any of the above conditions are
75 not met.
76 .
77 DISCLAIMER
78 .
79 THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
80 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
81 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
82 OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
83 COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
84 INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
85 DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
86 FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
87 OTHER DEALINGS IN THE FONT SOFTWARE.
tests/samples/NotoSans-Regular.ttf less more
Binary diff not shown
tests/samples/table.svgz less more
Binary diff not shown