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
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 | # 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 | ||
0 | 31 | Version 0.8.2 |
1 | 32 | ~~~~~~~~~~~~~~ |
2 | 33 | Released: 2020-05-12 |
10 | 10 | |
11 | 11 | |
12 | 12 | ## 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) | |
14 | 14 | |
15 | 15 | ### Build dependencies |
16 | 16 | |
21 | 21 | * libarchive (>= 3.2) [5] |
22 | 22 | * LMDB [6] |
23 | 23 | * GirToD [7] |
24 | * LibSoup | |
24 | 25 | * Cairo |
25 | 26 | * GdkPixbuf 2.0 |
26 | 27 | * RSvg 2.0 |
1 | 1 | |
2 | 2 | 1. Write NEWS entries for AppStream Generator in the same format as usual. |
3 | 3 | |
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 | |
5 | 5 | |
6 | 6 | -------------------------------------------------------------------------------- |
7 | Version 0.8.2 | |
7 | Version 0.8.3 | |
8 | 8 | ~~~~~~~~~~~~~~ |
9 | 9 | Released: 2020-xx-xx |
10 | 10 | |
17 | 17 | |
18 | 18 | 2. Commit changes in Git: |
19 | 19 | |
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> | |
22 | 22 | git push --tags |
23 | 23 | git push |
24 | 24 | |
32 | 32 | 5. Send an email to appstream@lists.freedesktop.org |
33 | 33 | |
34 | 34 | ================================================= |
35 | AppStream Generator 0.8.2 released! | |
35 | AppStream Generator 0.8.3 released! | |
36 | 36 | |
37 | 37 | Tarballs available here: https://github.com/ximion/appstream-generator/releases |
38 | 38 |
42 | 42 | |
43 | 43 | srcDir: . |
44 | 44 | |
45 | lookup: APILookupGdkPixbuf.txt | |
45 | 46 | lookup: APILookupAppStream.txt |
46 | #lookup: APILookupSoup.txt | |
47 | lookup: APILookupAppStreamCompose.txt |
25 | 25 | public import core.stdc.stdio; |
26 | 26 | public import core.stdc.string; |
27 | 27 | 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 | # | |
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 |
31 | 31 | }, |
32 | 32 | |
33 | 33 | "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}}", | |
35 | 35 | "severity": "error" |
36 | 36 | }, |
37 | 37 | |
62 | 62 | }, |
63 | 63 | |
64 | 64 | "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,", | |
66 | 66 | "or we could not find a matching icon for this application." |
67 | 67 | ], |
68 | 68 | "severity": "error" |
70 | 70 | |
71 | 71 | "font-without-icon": { |
72 | 72 | "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?" | |
74 | 74 | ], |
75 | 75 | "severity": "warning" |
76 | 76 | }, |
109 | 109 | "severity": "error" |
110 | 110 | }, |
111 | 111 | |
112 | "metainfo-no-id": { | |
113 | "text": ["Could not determine an ID for the component in '{{fname}}'. The AppStream metainfo file likely lacks an <code><id/></code> tag.<br/>", | |
114 | "The identifier tag is essential for AppStream metadata, and must not be missing."], | |
115 | "severity": "error" | |
116 | }, | |
117 | ||
118 | 112 | "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}}", | |
126 | 114 | "severity": "warning" |
127 | 115 | }, |
128 | 116 | |
129 | 117 | "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."], | |
132 | 120 | "severity": "warning" |
133 | 121 | }, |
134 | 122 | |
164 | 152 | "severity": "info" |
165 | 153 | }, |
166 | 154 | |
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 | ||
173 | 155 | "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.", | |
175 | 157 | "severity": "error" |
176 | 158 | }, |
177 | 159 | |
178 | 160 | "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", | |
180 | 162 | "has a <code>Comment=</code> field set.<br/>", |
181 | 163 | "More information can be found in the <a href=\"http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s05.html\">Desktop Entry specification</a>", |
182 | 164 | "and the <a href=\"https://www.freedesktop.org/software/appstream/docs/sect-Metadata-Application.html#tag-dapp-summary\">MetaInfo specification</a>."], |
191 | 173 | }, |
192 | 174 | |
193 | 175 | "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><launchable type=\"desktop-id\"/></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><launchable type=\"desktop-id\"/></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/>", | |
196 | 178 | "Please fix the packaging or work with upstream to resolve this issue.<br/>", |
197 | 179 | "<small>For older metadata, the desktop-id is inferred from the <code><id/></code> tag. If the component metadata has no <code>launchable</code> tag and no", |
198 | 180 | "<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>"], |
200 | 182 | }, |
201 | 183 | |
202 | 184 | "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", | |
204 | 186 | "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."], | |
206 | 188 | "severity": "warning" |
207 | 189 | }, |
208 | 190 | |
209 | 191 | "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/>", | |
211 | 193 | "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" | |
215 | 209 | }, |
216 | 210 | |
217 | 211 | "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."], | |
219 | 213 | "severity": "error" |
220 | 214 | }, |
221 | 215 | |
235 | 229 | "severity": "error" |
236 | 230 | }, |
237 | 231 | |
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 | ||
245 | 232 | "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/>", | |
247 | 234 | "This can mean that the <code><provides> - <font></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/>", |
248 | 235 | "Fonts in this package: <em>{{font_names}}</em>"], |
249 | 236 | "severity": "error" |
271 | 258 | |
272 | 259 | "no-translation-statistics": { |
273 | 260 | "text": ["We could not extract any translation statitics, althout metadata hints were given that the data exists.", |
274 | "Please check the <translation/> metainfo tag for spelling mistakes.", | |
261 | "Please check the <translation/> MetaInfo tag for spelling mistakes.", | |
275 | 262 | "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>"], |
276 | 263 | "severity": "warning" |
277 | 264 | }, |
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. |
17 | 17 | a { |
18 | 18 | color: #337ab7; |
19 | 19 | text-decoration: none; |
20 | } | |
21 | ||
22 | a { | |
23 | 20 | background-color: transparent; |
24 | 21 | } |
25 | 22 | |
235 | 232 | position: absolute; |
236 | 233 | width: 1px; |
237 | 234 | } |
235 | ||
236 | .permalink { | |
237 | font-size: 75%; | |
238 | color: #999; | |
239 | line-height: 100%; | |
240 | font-weight: normal; | |
241 | text-decoration: none; | |
242 | } |
17 | 17 | a { |
18 | 18 | color: #337ab7; |
19 | 19 | text-decoration: none; |
20 | } | |
21 | ||
22 | a { | |
23 | 20 | background-color: transparent; |
24 | 21 | } |
25 | 22 |
95 | 95 | processGStreamer | Synthesise `type=codec` metadata from available GStreamer packages. Requires support in the backend, currently only implemented for Debian. *Default: `ON`* |
96 | 96 | processLocale | Try to extract the software's localization status from Gettext data. *Default: `ON`* |
97 | 97 | 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`* | |
99 | 100 | |
100 | 101 | ### Configuring icon policies |
101 | 102 |
1 | 1 | meson_version : '>=0.46', |
2 | 2 | subproject_dir : 'contrib/subprojects', |
3 | 3 | license : 'LGPL-3.0+', |
4 | version : '0.8.2' | |
4 | version : '0.8.3' | |
5 | 5 | ) |
6 | 6 | |
7 | 7 | asgen_version = meson.project_version() |
15 | 15 | src_dir = include_directories('src/') |
16 | 16 | |
17 | 17 | 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') | |
20 | 21 | archive_dep = dependency('libarchive', version : '>= 3.2') |
21 | 22 | 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') | |
28 | 23 | |
29 | 24 | # |
30 | 25 | # Build interfaces from GIR |
19 | 19 | |
20 | 20 | name: appstream-generator |
21 | 21 | license: LGPL-3.0 |
22 | base: core18 | |
22 | base: core20 | |
23 | 23 | adopt-info: appstream-generator |
24 | 24 | |
25 | 25 | confinement: strict |
71 | 71 | |
72 | 72 | # adjust to an absolute path to help finding the GIR file from the AppStream part |
73 | 73 | 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 | |
74 | 75 | |
75 | 76 | # actually build asgen - we need to run everything manually here, |
76 | 77 | # because snapcraft will kill the build if run with maximum amount of ninja jobs, |
78 | 79 | meson --prefix=/usr --buildtype=debugoptimized -Ddownload-js=true snapbuild |
79 | 80 | cd snapbuild |
80 | 81 | ninja -j4 |
81 | ninja test | |
82 | #meson test --verbose | |
82 | 83 | DESTDIR=$SNAPCRAFT_PART_INSTALL ninja install |
83 | 84 | override-prime: | |
84 | 85 | set -eux |
157 | 158 | appstream: |
158 | 159 | source: https://github.com/ximion/appstream.git |
159 | 160 | source-type: git |
160 | source-tag: v0.12.11 | |
161 | source-branch: master | |
161 | 162 | |
162 | 163 | plugin: meson |
163 | 164 | meson-parameters: |
164 | 165 | - --prefix=/usr |
165 | 166 | - --buildtype=release |
167 | - -Dcompose=true | |
166 | 168 | - -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 | |
173 | 169 | build-packages: |
174 | 170 | - build-essential |
175 | 171 | - docbook-xsl |
187 | 183 | - meson |
188 | 184 | - xsltproc |
189 | 185 | stage-packages: |
190 | - libicu60 | |
186 | - libicu66 | |
191 | 187 | - liblmdb0 |
192 | 188 | - libsoup2.4-1 |
193 | 189 | - libstemmer0d |
196 | 192 | gir-to-d: |
197 | 193 | source: https://github.com/gtkd-developers/gir-to-d.git |
198 | 194 | source-type: git |
199 | source-tag: v0.21.0 | |
195 | source-tag: v0.22.0 | |
200 | 196 | |
201 | 197 | plugin: meson |
202 | 198 | meson-parameters: |
227 | 223 | |
228 | 224 | ldc: |
229 | 225 | 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 | |
231 | 227 | |
232 | 228 | override-build: | |
233 | 229 | # link shared by default |
0 | 0 | /* |
1 | * Copyright (C) 2016-2017 Matthias Klumpp <matthias@tenstral.net> | |
1 | * Copyright (C) 2016-2021 Matthias Klumpp <matthias@tenstral.net> | |
2 | 2 | * |
3 | 3 | * Licensed under the GNU Lesser General Public License Version 3 |
4 | 4 | * |
52 | 52 | --verbose Show extra debugging information. |
53 | 53 | --force Force action. |
54 | 54 | -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."; | |
56 | 57 | |
57 | 58 | version (unittest) { |
58 | 59 | void main () {} |
94 | 95 | bool showVersion; |
95 | 96 | bool forceAction; |
96 | 97 | string wdir; |
98 | string exportDir; | |
97 | 99 | string configFname; |
98 | 100 | |
99 | 101 | // parse command-line options |
104 | 106 | "version", &showVersion, |
105 | 107 | "force", &forceAction, |
106 | 108 | "workspace|w", &wdir, |
107 | "config|c", &configFname); | |
109 | "config|c", &configFname, | |
110 | "export-dir", &exportDir); | |
108 | 111 | } catch (Exception e) { |
109 | 112 | writeln ("Unable to parse parameters: ", e.msg); |
110 | 113 | exit (1); |
139 | 142 | } |
140 | 143 | |
141 | 144 | try { |
142 | conf.loadFromFile (configFname, wdir); | |
145 | conf.loadFromFile (configFname, wdir, exportDir); | |
143 | 146 | } catch (Exception e) { |
144 | 147 | writefln ("Unable to load configuration: %s", e.msg); |
145 | 148 | 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 | } |
21 | 21 | |
22 | 22 | module asgen.backends.alpinelinux.apkpkg; |
23 | 23 | |
24 | import std.stdio; | |
25 | import std.string; | |
26 | 24 | import std.array : empty; |
25 | import std.format : format; | |
26 | import std.path : baseName, buildNormalizedPath, buildPath; | |
27 | 27 | |
28 | import asgen.logging; | |
29 | import asgen.zarchive; | |
30 | 28 | import asgen.backends.interfaces; |
29 | import asgen.config : Config; | |
30 | import asgen.downloader : Downloader; | |
31 | import asgen.utils : isRemote; | |
32 | import asgen.zarchive : ArchiveDecompressor; | |
31 | 33 | |
32 | 34 | final class AlpinePackage : Package |
33 | 35 | { |
38 | 40 | string pkgmaintainer; |
39 | 41 | string[string] desc; |
40 | 42 | string pkgFname; |
43 | string localPkgFName; | |
44 | string tmpDir; | |
41 | 45 | |
42 | string[] contentsL; | |
46 | string[] contentsL = null; | |
43 | 47 | |
44 | 48 | ArchiveDecompressor archive; |
45 | 49 | |
46 | 50 | 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 | ||
47 | 61 | override @property string name () const |
48 | 62 | { |
49 | 63 | return this.pkgname; |
84 | 98 | this.pkgFname = fname; |
85 | 99 | } |
86 | 100 | |
87 | override @property string getFilename () const | |
101 | override @property string getFilename () | |
88 | 102 | { |
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 | } | |
90 | 118 | } |
91 | 119 | |
92 | 120 | override @property string maintainer () const |
114 | 142 | |
115 | 143 | @property override string[] contents () |
116 | 144 | { |
145 | if (!this.contentsL.empty) | |
146 | return this.contentsL; | |
147 | ||
148 | ArchiveDecompressor ad; | |
149 | ad.open (this.getFilename); | |
150 | this.contentsL = ad.readContents (); | |
151 | ||
117 | 152 | return this.contentsL; |
118 | 153 | } |
119 | 154 |
18 | 18 | |
19 | 19 | module asgen.backends.alpinelinux.apkpkgindex; |
20 | 20 | |
21 | import std.algorithm : canFind, filter, endsWith, remove; | |
22 | import std.array : appender, join, split; | |
21 | import std.array : appender; | |
23 | 22 | import std.conv : to; |
24 | 23 | import std.exception : enforce; |
25 | import std.file : dirEntries, exists, SpanMode; | |
24 | import std.file : exists; | |
26 | 25 | import std.format : format; |
27 | 26 | 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; | |
31 | 28 | |
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; | |
35 | 33 | import asgen.backends.interfaces; |
36 | 34 | import asgen.backends.alpinelinux.apkpkg; |
35 | import asgen.backends.alpinelinux.apkindexutils; | |
37 | 36 | |
38 | 37 | final class AlpinePackageIndex : PackageIndex |
39 | 38 | { |
40 | 39 | |
41 | 40 | private: |
42 | 41 | string rootDir; |
42 | string tmpDir; | |
43 | 43 | Package[][string] pkgCache; |
44 | 44 | |
45 | 45 | public: |
46 | 46 | |
47 | 47 | this (string dir) |
48 | 48 | { |
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 | ||
50 | 52 | this.rootDir = dir; |
53 | ||
54 | auto conf = Config.get (); | |
55 | tmpDir = buildPath (conf.getTmpDir, dir.baseName); | |
51 | 56 | } |
52 | 57 | |
53 | 58 | override void release () |
64 | 69 | pkg.setDescription (desc, "C"); |
65 | 70 | } |
66 | 71 | |
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 | ||
94 | 72 | private Package[] loadPackages (string suite, string section, string arch) |
95 | 73 | { |
96 | 74 | 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; | |
97 | 77 | 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); | |
99 | 82 | |
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; | |
103 | 85 | AlpinePackage pkg; |
104 | 86 | if (fileName in pkgsMap) { |
105 | 87 | pkg = pkgsMap[fileName]; |
106 | 88 | } else { |
107 | pkg = new AlpinePackage (); | |
89 | pkg = new AlpinePackage (pkgInfo.pkgname, pkgInfo.pkgversion, pkgInfo.arch); | |
108 | 90 | pkgsMap[fileName] = pkg; |
109 | 91 | } |
110 | 92 | |
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); | |
142 | 96 | } |
143 | 97 | |
144 | 98 | // perform a sanity check, so we will never emit invalid packages |
62 | 62 | |
63 | 63 | if (fileName.isRemote) { |
64 | 64 | try { |
65 | /* This should use download(), but that doesn't throw errors */ | |
66 | 65 | downloader.downloadFile (fileName, destFileName); |
67 | ||
68 | 66 | return destFileName; |
69 | 67 | } catch (DownloadException e) { |
70 | 68 | logDebug ("Unable to download: %s", e.msg); |
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 | /* | |
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 | /* | |
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 | /* | |
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 | /* | |
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 | /* | |
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 | } |
44 | 44 | enum { |
45 | 45 | MDB_VERSION_MAJOR = 0, |
46 | 46 | 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", | |
49 | 49 | } |
50 | 50 | |
51 | 51 | struct MDB_env_s {} |
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 | /* | |
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); |
0 | 0 | /* |
1 | * Copyright (C) 2016-2020 Matthias Klumpp <matthias@tenstral.net> | |
1 | * Copyright (C) 2016-2021 Matthias Klumpp <matthias@tenstral.net> | |
2 | 2 | * |
3 | 3 | * Licensed under the GNU Lesser General Public License Version 3 |
4 | 4 | * |
21 | 21 | import std.stdio; |
22 | 22 | import std.array; |
23 | 23 | import std.string : format, toLower; |
24 | import std.path : dirName, buildPath, buildNormalizedPath; | |
24 | import std.path : dirName, buildPath, buildNormalizedPath, isAbsolute, absolutePath; | |
25 | 25 | import std.conv : to; |
26 | 26 | import std.json; |
27 | 27 | import std.typecons; |
28 | 28 | import std.file : getcwd, thisExePath, exists; |
29 | 29 | |
30 | import ascompose.Globals : Globals; | |
30 | 31 | public import appstream.c.types : FormatVersion; |
31 | 32 | |
32 | 33 | import asgen.utils : existsAndIsDir, randomString, ImageSize; |
89 | 90 | bool processGStreamer; |
90 | 91 | bool processLocale; |
91 | 92 | bool screenshotVideos; |
92 | bool propagateMetainfoArtifacts; | |
93 | bool propagateMetaInfoArtifacts; | |
94 | bool warnNoMetaInfo; | |
93 | 95 | } |
94 | 96 | |
95 | 97 | /// Fake package name AppStream Generator uses internally to inject additional metainfo on users' request |
149 | 151 | // find all the external binaries we (may) need |
150 | 152 | // we search for them unconditionally, because the unittests may rely on their absolute |
151 | 153 | // paths being set even if a particular feature flag that requires them isn't. |
152 | optipngBinary = Util.findProgramInPath ("optipng"); | |
154 | optipngBinary = Globals.optipngBinary; | |
153 | 155 | ffprobeBinary = Util.findProgramInPath ("ffprobe"); |
154 | 156 | } |
155 | 157 | |
200 | 202 | @property |
201 | 203 | string formatVersionStr () |
202 | 204 | { |
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); | |
208 | 208 | } |
209 | 209 | |
210 | 210 | @property |
226 | 226 | auto tdir = buildPath (workspaceDir, "templates"); |
227 | 227 | tdir = getVendorTemplateDir (tdir, true); |
228 | 228 | |
229 | if (tdir is null) { | |
229 | if (tdir.empty) { | |
230 | 230 | immutable exeDir = dirName (thisExePath ()); |
231 | 231 | tdir = buildNormalizedPath (exeDir, "..", "..", "..", "data", "templates"); |
232 | 232 | tdir = getVendorTemplateDir (tdir); |
233 | 233 | |
234 | if (tdir is null) { | |
234 | if (tdir.empty) { | |
235 | 235 | tdir = getVendorTemplateDir (buildPath (DATADIR, "templates")); |
236 | 236 | |
237 | if (tdir is null) { | |
237 | if (tdir.empty) { | |
238 | 238 | tdir = buildNormalizedPath (exeDir, "..", "data", "templates"); |
239 | 239 | tdir = getVendorTemplateDir (tdir); |
240 | 240 | } |
250 | 250 | private string getVendorTemplateDir (const string dir, bool allowRoot = false) @safe |
251 | 251 | { |
252 | 252 | string tdir; |
253 | if (projectName !is null) { | |
254 | tdir = buildPath (dir, projectName.toLower ()); | |
253 | if (!projectName.empty) { | |
254 | tdir = buildPath (dir, projectName.toLower); | |
255 | 255 | if (existsAndIsDir (tdir)) |
256 | 256 | return tdir; |
257 | 257 | } |
266 | 266 | return null; |
267 | 267 | } |
268 | 268 | |
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 | { | |
273 | 271 | // read the configuration JSON file |
274 | 272 | auto f = File (fname, "r"); |
275 | 273 | string jsonData; |
290 | 288 | // allow overriding the workspace location |
291 | 289 | if (!enforcedWorkspaceDir.empty) |
292 | 290 | workspaceDir = enforcedWorkspaceDir; |
291 | if (!workspaceDir.isAbsolute) | |
292 | workspaceDir = workspaceDir.absolutePath; | |
293 | 293 | |
294 | 294 | this.projectName = "Unknown"; |
295 | 295 | if ("ProjectName" in root) |
305 | 305 | if ("HtmlBaseUrl" in root) |
306 | 306 | this.htmlBaseUrl = root["HtmlBaseUrl"].str; |
307 | 307 | |
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 | ||
308 | 318 | // 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"; | |
315 | 324 | |
316 | 325 | if ("ExportDirs" in root) { |
317 | 326 | auto edirs = root["ExportDirs"].object; |
335 | 344 | } |
336 | 345 | } |
337 | 346 | |
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 | ||
338 | 353 | // a place where external metainfo data can be injected |
354 | auto extraMetainfoDir = buildPath (workspaceDir, "extra-metainfo"); | |
339 | 355 | if ("ExtraMetainfoDir" in root) |
340 | 356 | extraMetainfoDir = root["ExtraMetainfoDir"].str; |
341 | 357 | |
538 | 554 | feature.processGStreamer = true; |
539 | 555 | feature.processLocale = true; |
540 | 556 | feature.screenshotVideos = true; |
557 | feature.warnNoMetaInfo = true; | |
541 | 558 | |
542 | 559 | // apply vendor feature settings |
543 | 560 | if ("Features" in root.object) { |
580 | 597 | case "screenshotVideos": |
581 | 598 | feature.screenshotVideos = featuresObj[featureId].type == JSONType.true_; |
582 | 599 | 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_; | |
585 | 605 | break; |
586 | 606 | default: |
587 | 607 | break; |
590 | 610 | } |
591 | 611 | |
592 | 612 | // check if we need to disable features because some prerequisites are not met |
613 | Globals.setUseOptipng (feature.optipng); | |
593 | 614 | if (feature.optipng) { |
594 | 615 | if (optipngBinary.empty) { |
595 | 616 | feature.optipng = false; |
621 | 642 | } |
622 | 643 | |
623 | 644 | if (!feature.validate) |
624 | logWarning ("Metainfo validation has been disabled in configuration."); | |
645 | logWarning ("MetaInfo validation has been disabled in configuration."); | |
625 | 646 | |
626 | 647 | // sanity check to warn if our GdkPixbuf does not support the minimum amount |
627 | 648 | // 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)) { | |
630 | 655 | logError ("The currently used GdkPixbuf does not seem to support all image formats we require to run normally (png/svg/jpeg). " ~ |
631 | 656 | "This may be a problem with your installation of appstream-generator or gdk-pixbuf."); |
632 | 657 | } |
642 | 667 | */ |
643 | 668 | string getTmpDir () |
644 | 669 | { |
645 | if (tmpDir.empty) { | |
646 | synchronized (this) { | |
670 | synchronized (this) { | |
671 | if (tmpDir.empty) { | |
647 | 672 | string root; |
648 | 673 | if (cacheRootDir.empty) |
649 | 674 | root = "/tmp/"; |
651 | 676 | root = cacheRootDir; |
652 | 677 | |
653 | 678 | 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 | } | |
657 | 684 | return tmpDir; |
658 | 685 | } |
659 | 686 | } |
55 | 55 | |
56 | 56 | ~this () |
57 | 57 | { |
58 | if (opened) | |
59 | dbEnv.mdb_env_close (); | |
58 | close (); | |
60 | 59 | } |
61 | 60 | |
62 | 61 | private void checkError (int rc, string msg) |
98 | 97 | |
99 | 98 | // open database |
100 | 99 | rc = dbEnv.mdb_env_open (dir.toStringz (), |
101 | MDB_NOMETASYNC | MDB_NOTLS, | |
100 | MDB_NOMETASYNC, | |
102 | 101 | octal!755); |
103 | 102 | checkError (rc, "mdb_env_open"); |
104 | 103 | |
132 | 131 | this.open (buildPath (conf.databaseDir, "contents")); |
133 | 132 | } |
134 | 133 | |
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) | |
136 | 145 | { |
137 | 146 | import core.stdc.string : strlen; |
138 | 147 | MDB_val mval; |
143 | 152 | } |
144 | 153 | |
145 | 154 | private MDB_txnp newTransaction (uint flags = 0) |
155 | in { assert (opened); } | |
156 | do | |
146 | 157 | { |
147 | 158 | int rc; |
148 | 159 | MDB_txnp txn; |
191 | 202 | checkError (res, "mdb_del (locale)"); |
192 | 203 | } |
193 | 204 | |
194 | bool packageExists (string pkid) | |
205 | bool packageExists (const string pkid) | |
195 | 206 | { |
196 | 207 | MDB_val dkey; |
197 | 208 | MDB_cursorp cur; |
301 | 312 | auto data = fromStringz (cast(char*) cval.mv_data); |
302 | 313 | auto contents = to!string (data); |
303 | 314 | |
304 | foreach (ref c; contents.split ("\n")) { | |
315 | foreach (const ref c; contents.split ("\n")) { | |
305 | 316 | if (useBaseName) |
306 | 317 | pkgCMap[c.baseName] = pkid; |
307 | 318 | else |
413 | 424 | } |
414 | 425 | |
415 | 426 | void sync () |
427 | in { assert (opened); } | |
428 | do | |
416 | 429 | { |
417 | 430 | dbEnv.mdb_env_sync (1); |
418 | 431 | } |
80 | 80 | |
81 | 81 | ~this () |
82 | 82 | { |
83 | if (opened) | |
84 | dbEnv.mdb_env_close (); | |
83 | close (); | |
85 | 84 | } |
86 | 85 | |
87 | 86 | @property |
111 | 110 | static import std.math; |
112 | 111 | |
113 | 112 | int rc; |
114 | assert (opened == false); | |
113 | assert (!opened); | |
115 | 114 | |
116 | 115 | // add LMDB version we are using to the debug output |
117 | 116 | printVersionDbg (); |
138 | 137 | |
139 | 138 | // open database |
140 | 139 | rc = dbEnv.mdb_env_open (dir.toStringz (), |
141 | MDB_NOMETASYNC | MDB_NOTLS, | |
140 | MDB_NOMETASYNC, | |
142 | 141 | octal!755); |
143 | 142 | checkError (rc, "mdb_env_open"); |
144 | 143 | |
171 | 170 | |
172 | 171 | this.mediaDir = buildPath (mediaBaseDir, "pool"); |
173 | 172 | 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 | } | |
174 | 183 | } |
175 | 184 | |
176 | 185 | void open (Config conf) |
189 | 198 | } |
190 | 199 | |
191 | 200 | private MDB_txnp newTransaction (uint flags = 0) |
201 | in { assert (opened); } | |
202 | do | |
192 | 203 | { |
193 | 204 | int rc; |
194 | 205 | MDB_txnp txn; |
321 | 332 | { |
322 | 333 | // if the package has no components or hints, |
323 | 334 | // mark it as always-ignore |
324 | if (gres.packageIsIgnored ()) { | |
335 | if (gres.unitIgnored) { | |
325 | 336 | setPackageIgnore (gres.pkid); |
326 | 337 | return; |
327 | 338 | } |
328 | 339 | |
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)); | |
330 | 343 | auto gcid = gres.gcidForComponent (cpt); |
331 | 344 | if (metadataExists (dtype, gcid) && !alwaysRegenerate) { |
332 | 345 | // we already have seen this exact metadata - only adjust the reference, |
346 | 359 | data = mdata.componentsToCollection (FormatKind.YAML); |
347 | 360 | } |
348 | 361 | } catch (Exception e) { |
349 | gres.addHint (cpt.getId (), "metadata-serialization-failed", e.msg); | |
362 | gres.addHint (cpt, "metadata-serialization-failed", e.msg); | |
350 | 363 | continue; |
351 | 364 | } |
352 | 365 | // remove trailing whitespaces and linebreaks |
363 | 376 | setHints (gres.pkid, hintsJson); |
364 | 377 | } |
365 | 378 | |
366 | auto gcids = gres.getGCIDs (); | |
379 | auto gcids = gres.getComponentGcids (); | |
367 | 380 | if (gcids.empty) { |
368 | 381 | // no global components, and we're not ignoring this component. |
369 | 382 | // this means we likely have hints stored for this one. Mark it |
373 | 386 | import std.array : join; |
374 | 387 | // store global component IDs for this package as newline-separated list |
375 | 388 | auto gcidVal = join (gcids, "\n"); |
376 | ||
377 | 389 | putKeyValue (dbPackages, gres.pkid, gcidVal); |
378 | 390 | } |
379 | 391 | } |
186 | 186 | scope(exit) g_object_unref (stream); |
187 | 187 | |
188 | 188 | ptrdiff_t len; |
189 | ubyte[GENERIC_BUFFER_SIZE] buffer; | |
189 | 190 | do { |
190 | ubyte[GENERIC_BUFFER_SIZE] buffer; | |
191 | ||
192 | 191 | len = g_input_stream_read (stream, cast(void*)buffer.ptr, cast(size_t)buffer.length, null, null); |
193 | 192 | if (len > 0) |
194 | 193 | dFile.rawWrite (buffer[0..len]); |
205 | 204 | |
206 | 205 | auto result = appender!(ubyte[]); |
207 | 206 | ptrdiff_t len; |
207 | ubyte[GENERIC_BUFFER_SIZE] buffer; | |
208 | 208 | do { |
209 | ubyte[GENERIC_BUFFER_SIZE] buffer; | |
210 | ||
211 | 209 | 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]; | |
213 | 212 | } while (len > 0); |
214 | 213 | |
215 | 214 | return result.data; |
281 | 280 | import std.stdio : writeln; |
282 | 281 | import std.exception : assertThrown; |
283 | 282 | import std.file : remove, readText; |
283 | import std.process : environment; | |
284 | 284 | asgen.logging.setVerbose (true); |
285 | 285 | |
286 | 286 | writeln ("TEST: ", "Downloader"); |
287 | 287 | |
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 | ||
288 | 293 | immutable urlFirefoxDetectportal = "https://detectportal.firefox.com/"; |
289 | ||
290 | 294 | auto dl = new Downloader; |
291 | 295 | string detectPortalRes; |
292 | 296 | try { |
293 | 297 | detectPortalRes = dl.downloadText (urlFirefoxDetectportal); |
294 | 298 | } 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, ")"); | |
296 | 300 | return; |
297 | 301 | } |
298 | 302 | writeln ("I: Running network-dependent tests."); |
96 | 96 | throw new Exception ("No backend specified, can not continue!"); |
97 | 97 | } |
98 | 98 | |
99 | // load global registry of issue hint templates | |
100 | loadHintsRegistry (); | |
101 | ||
99 | 102 | // create cache in cache directory on workspace |
100 | 103 | dstore = new DataStore (); |
101 | 104 | dstore.open (conf); |
103 | 106 | // open package contents cache |
104 | 107 | cstore = new ContentsStore (); |
105 | 108 | cstore.open (conf); |
106 | ||
107 | // for Cairo/Fontconfig issues with multithreading | |
108 | import asgen.image : setupFontconfigMutex; | |
109 | if (conf.feature.processFonts) | |
110 | setupFontconfigMutex (); | |
111 | 109 | } |
112 | 110 | |
113 | 111 | @property |
132 | 130 | private void logVersionInfo () |
133 | 131 | { |
134 | 132 | 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; | |
137 | 135 | |
138 | 136 | string backendInfo = ""; |
139 | 137 | if (!conf.backendName.empty) |
140 | 138 | 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); | |
142 | 140 | } |
143 | 141 | |
144 | 142 | /** |
147 | 145 | */ |
148 | 146 | private void processPackages (ref Package[] pkgs, IconHandler iconh) |
149 | 147 | { |
150 | auto localeh = new LocaleHandler (pkgs); | |
148 | auto localeh = new LocaleHandler (cstore, pkgs); | |
151 | 149 | auto mde = new DataExtractor (dstore, iconh, localeh); |
152 | 150 | foreach (ref pkg; parallel (pkgs)) { |
153 | 151 | immutable pkid = pkg.id; |
489 | 487 | // save a copy of the hints registry to be used by other tools |
490 | 488 | // (this allows other apps to just resolve the hint tags to severities and explanations |
491 | 489 | // 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")); | |
494 | 491 | } |
495 | 492 | |
496 | 493 | private HashMap!(string, Package) getIconCandidatePackages (Suite suite, string section, string arch) |
548 | 545 | cpt.setMergeKind (MergeKind.REMOVE_COMPONENT); |
549 | 546 | cpt.setId (cid); |
550 | 547 | |
551 | gres.addComponent (cpt, metainfoDir ~ "/-" ~ cid); | |
548 | gres.addComponentWithString (cpt, metainfoDir ~ "/-" ~ cid); | |
552 | 549 | } |
553 | 550 | } |
554 | 551 | |
561 | 558 | import std.stdio : File; |
562 | 559 | import std.path : baseName; |
563 | 560 | 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; | |
566 | 563 | import asgen.handlers.screenshothandler : processScreenshots; |
567 | 564 | |
568 | 565 | foreach (miFname; metainfoDir.dirEntries ("*.xml", SpanMode.shallow, false)) { |
578 | 575 | // we want some replacement logic for arch-specific injected metainfo files, |
579 | 576 | // so arch-specific xml files can replace generic ones. To archieve that we assume |
580 | 577 | // 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); | |
584 | 583 | if (cpt is null) |
585 | 584 | continue; |
586 | 585 | |
587 | 586 | // 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 | } | |
590 | 590 | |
591 | 591 | // get icon |
592 | 592 | iconh.process (gres, cpt); |
666 | 666 | |
667 | 667 | // process new packages |
668 | 668 | auto pkgs = pkgIndex.packagesFor (suite.name, section, arch); |
669 | auto iconh = new IconHandler (dstore.mediaExportPoolDir, | |
669 | auto iconh = new IconHandler (cstore, | |
670 | dstore.mediaExportPoolDir, | |
670 | 671 | conf.iconSettings, |
671 | 672 | getIconCandidatePackages (suite, section, arch), |
672 | 673 | suite.iconTheme); |
798 | 799 | } |
799 | 800 | |
800 | 801 | // process new packages |
801 | auto iconh = new IconHandler (dstore.mediaExportPoolDir, | |
802 | auto iconh = new IconHandler (cstore, | |
803 | dstore.mediaExportPoolDir, | |
802 | 804 | conf.iconSettings, |
803 | 805 | getIconCandidatePackages (suite, sectionName, arch), |
804 | 806 | suite.iconTheme); |
26 | 26 | import std.typecons : scoped; |
27 | 27 | import appstream.Component; |
28 | 28 | import appstream.Metadata; |
29 | import ascompose.Hint : Hint; | |
30 | import ascompose.MetaInfoUtils : MetaInfoUtils; | |
31 | import glib.Bytes : Bytes; | |
29 | 32 | |
30 | 33 | import asgen.containers : HashMap, SList; |
31 | 34 | import asgen.config; |
32 | import asgen.hint; | |
35 | import asgen.hintregistry; | |
33 | 36 | import asgen.result; |
34 | 37 | import asgen.backends.interfaces; |
35 | 38 | import asgen.datastore; |
36 | 39 | import asgen.handlers; |
37 | import asgen.utils : componentGetStockIcon; | |
40 | import asgen.utils : componentGetStockIcon, toStaticGBytes; | |
38 | 41 | |
39 | 42 | |
40 | 43 | final class DataExtractor |
42 | 45 | |
43 | 46 | private: |
44 | 47 | Component[] cpts; |
45 | GeneratorHint[] hints; | |
48 | Hint[] hints; | |
46 | 49 | |
47 | 50 | DataStore dstore; |
48 | 51 | IconHandler iconh; |
61 | 64 | dtype = conf.metadataType; |
62 | 65 | } |
63 | 66 | |
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 | ||
64 | 83 | GeneratorResult processPackage (Package pkg) |
65 | 84 | { |
66 | 85 | // create a new result container |
91 | 110 | |
92 | 111 | // now process metainfo XML files |
93 | 112 | 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; | |
98 | 117 | |
99 | 118 | mdata.clearComponents (); |
100 | auto cpt = parseMetaInfoData (mdata, gres, data, mfname); | |
119 | auto cpt = MetaInfoUtils.parseMetainfoData (gres, mdata, dataBytes, mfname); | |
101 | 120 | if (cpt is null) |
102 | 121 | continue; |
103 | 122 | |
111 | 130 | |
112 | 131 | // we need to add the version to re-download screenshot on every new upload. |
113 | 132 | // 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); | |
115 | 134 | |
116 | 135 | // validate the desktop-id launchables and merge the desktop file data |
117 | 136 | // in case we find it. |
138 | 157 | parseDesktopFile (gres, cpt, dfname, ddata, true); |
139 | 158 | |
140 | 159 | // update GCID checksum |
141 | gres.updateComponentGCID (cpt, ddata); | |
160 | gres.updateComponentGcid (cpt, ddataBytes.toStaticGBytes); | |
142 | 161 | |
143 | 162 | // drop the .desktop file from the list, it has been handled |
144 | 163 | desktopFiles.remove (desktopId); |
164 | 183 | // Otherwise we take the data and see how far we get. |
165 | 184 | |
166 | 185 | // finalize GCID checksum and continue |
167 | gres.updateComponentGCID (cpt, data); | |
186 | gres.updateComponentGcid (cpt, dataBytes); | |
168 | 187 | |
169 | 188 | gres.addHint (cpt, "missing-desktop-file"); |
170 | 189 | // we have a desktop-application component, but no .desktop file. |
172 | 191 | continue; |
173 | 192 | } else { |
174 | 193 | // 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); | |
178 | 197 | |
179 | 198 | // update GCID checksum |
180 | gres.updateComponentGCID (cpt, ddata); | |
199 | gres.updateComponentGcid (cpt, ddataBytes); | |
181 | 200 | |
182 | 201 | // drop the .desktop file from the list, it has been handled |
183 | 202 | desktopFiles.remove (cid); |
188 | 207 | // the user to disable this feature. |
189 | 208 | if (conf.feature.validate) { |
190 | 209 | if (!dstore.metadataExists (dtype, gres.gcidForComponent (cpt))) |
191 | validateMetaInfoFile (gres, cpt, data, mfname.baseName); | |
210 | validateMetaInfoData (gres, cpt, dataBytes, mfname.baseName); | |
192 | 211 | } |
193 | 212 | } |
194 | 213 | |
195 | 214 | // process the remaining .desktop files |
196 | 215 | 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); | |
200 | 219 | if (cpt !is null) |
201 | gres.updateComponentGCID (cpt, ddata); | |
220 | gres.updateComponentGcid (cpt, ddataBytes); | |
202 | 221 | } |
203 | 222 | |
204 | 223 | if (conf.feature.processGStreamer && !pkg.gst.isNull && pkg.gst.get.isNotEmpty) { |
214 | 233 | data ~= desc; |
215 | 234 | } |
216 | 235 | |
217 | gres.addComponent (cpt); | |
218 | gres.updateComponentGCID (cpt, data.data); | |
236 | gres.addComponent (cpt, data.data.toStaticGBytes); | |
219 | 237 | } |
220 | 238 | |
221 | 239 | 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)); | |
223 | 243 | auto gcid = gres.gcidForComponent (cpt); |
224 | 244 | |
225 | 245 | // don't run expensive operations if the metadata already exists |
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 | } |
32 | 32 | import appstream.Provided; |
33 | 33 | import appstream.Icon; |
34 | 34 | import appstream.Launchable : Launchable, LaunchableKind; |
35 | static import appstream.Utils; | |
36 | alias AsUtils = appstream.Utils.Utils; | |
35 | 37 | static import std.regex; |
36 | 38 | |
37 | 39 | import asgen.result; |
91 | 93 | */ |
92 | 94 | private auto filterCategories (Component cpt, GeneratorResult gres, ref string[] cats) |
93 | 95 | { |
94 | import asgen.bindings.appstream_utils : as_utils_is_category_name; | |
95 | ||
96 | 96 | auto res = appender!(string[]); |
97 | 97 | res.reserve (cats.length / 2); |
98 | 98 | foreach (const cat; cats) { |
106 | 106 | break; |
107 | 107 | default: |
108 | 108 | if (!cat.empty && !cat.toLower.startsWith ("x-")) { |
109 | if (as_utils_is_category_name (cat.toStringz)) | |
109 | if (AsUtils.isCategoryName(cat)) | |
110 | 110 | res ~= cat; |
111 | 111 | else |
112 | 112 | gres.addHint (cpt, "category-name-invalid", ["category": cat]); |
178 | 178 | |
179 | 179 | try { |
180 | 180 | immutable onlyShowIn = df.getString (DESKTOP_GROUP, "OnlyShowIn"); |
181 | if (onlyShowIn.empty) { | |
181 | if (onlyShowIn.empty) | |
182 | 182 | 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 | |
186 | 189 | } catch (GException) {} |
187 | 190 | |
188 | 191 | /* check this is a valid desktop file */ |
214 | 217 | cpt.setId (fnameBase); |
215 | 218 | |
216 | 219 | cpt.setKind (ComponentKind.DESKTOP_APP); |
217 | gres.addComponent (cpt); | |
220 | gres.addComponent (cpt, null); | |
218 | 221 | } |
219 | 222 | } |
220 | 223 | |
367 | 370 | ecpt.setId ("org.example.foobar"); |
368 | 371 | ecpt.setName ("TestX", "C"); |
369 | 372 | ecpt.setSummary ("Summary of TestX", "C"); |
370 | res.addComponent (ecpt); | |
373 | res.addComponent (ecpt, null); | |
371 | 374 | |
372 | 375 | cpt = parseDesktopFile (res, null, "org.example.foobar.desktop", data, false); |
373 | 376 | assert (cpt !is null); |
26 | 26 | import appstream.Component; |
27 | 27 | import appstream.Icon; |
28 | 28 | import appstream.Screenshot; |
29 | static import appstream.Image; | |
29 | static import asImage = appstream.Image; | |
30 | 30 | static import std.file; |
31 | import ascompose.Font : Font; | |
32 | import ascompose.Canvas : Canvas; | |
33 | import ascompose.c.types : ImageFormat; | |
31 | 34 | |
32 | 35 | import asgen.utils; |
33 | 36 | import asgen.logging; |
34 | 37 | import asgen.result; |
35 | import asgen.image : Canvas; | |
36 | import asgen.font : Font; | |
37 | 38 | import asgen.config : Config, IconPolicy; |
38 | 39 | |
39 | 40 | |
65 | 66 | // the font class locks the global mutex internally when reading data with Fontconfig |
66 | 67 | Font font; |
67 | 68 | try { |
68 | font = new Font (fdata, fontBaseName); | |
69 | font = new Font ((cast(ubyte[]) fdata).ptr, cast(ptrdiff_t)fdata.length, fontBaseName); | |
69 | 70 | } catch (Exception e) { |
70 | 71 | gres.addHint (null, "font-load-error", ["fname": fontBaseName, |
71 | 72 | "pkg_fname": gres.pkg.getFilename.baseName, |
73 | 74 | return; |
74 | 75 | } |
75 | 76 | |
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)); | |
81 | 84 | if (cpt.getKind () != ComponentKind.FONT) |
82 | 85 | continue; |
83 | 86 | |
125 | 128 | // also ensure that the font style list is sorted for more |
126 | 129 | // deterministic results |
127 | 130 | 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; | |
130 | 133 | if (!regularFound && fontStyleId.canFind ("regular")) { |
131 | 134 | auto tmp = selectedFonts.data.dup; |
132 | 135 | selectedFonts.clear (); |
159 | 162 | if (selectedFonts.data.length == 0) { |
160 | 163 | auto fontNamesStr = appender!string; |
161 | 164 | foreach (ref font; allFonts.byValue) |
162 | fontNamesStr ~= fontNamesStr.data.empty? font.fullName : ("; " ~ font.fullName); | |
165 | fontNamesStr ~= fontNamesStr.data.empty? font.getFullname : ("; " ~ font.getFullname); | |
163 | 166 | if (fontNamesStr.data.empty) |
164 | 167 | fontNamesStr ~= "None"; |
165 | 168 | gres.addHint (cpt, "font-metainfo-but-no-font", ["font_names": fontNamesStr.data]); |
173 | 176 | auto firstLang = (cast(char*) cptLanguages.first.data).fromStringz; |
174 | 177 | |
175 | 178 | foreach (ref font; selectedFonts.data) |
176 | font.preferredLanguage = firstLang.to!string; | |
179 | font.setPreferredLanguage (firstLang.to!string); | |
177 | 180 | |
178 | 181 | // add languages mentioned in the metainfo file to list of supported languages |
179 | 182 | // of the respective font |
192 | 195 | // process font files |
193 | 196 | auto hasIcon = false; |
194 | 197 | foreach (ref font; selectedFonts.data) { |
195 | logDebug ("Processing font '%s'", font.id); | |
198 | import glib.Str; | |
199 | logDebug ("Processing font '%s'", font.getId); | |
196 | 200 | |
197 | 201 | // 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); | |
199 | 204 | // we have no idea how well the font supports the language's script, |
200 | 205 | // but since it adverties support in its metadata, we just assume 100% here |
201 | 206 | cpt.addLanguage (lang, 100); |
211 | 216 | |
212 | 217 | // set additional metadata. The font metadata might be terrible, but if the data is bad |
213 | 218 | // 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); | |
219 | 224 | } |
220 | 225 | } |
221 | 226 | |
243 | 248 | // check if we have a custom icon text value (useful for symbolic fonts) |
244 | 249 | immutable customIconText = cpt.getCustomValue ("FontIconText"); |
245 | 250 | 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); | |
250 | 255 | immutable iconStoreLocation = buildPath (path, iconName); |
251 | 256 | |
252 | 257 | if (!std.file.exists (iconStoreLocation)) { |
253 | 258 | // we didn't create an icon yet - render it |
254 | 259 | auto cv = new Canvas (size.width, size.height); |
255 | cv.drawTextLine (font, font.sampleIconText); | |
260 | cv.drawTextLine (font, font.getSampleIconText, -1); | |
256 | 261 | cv.savePng (iconStoreLocation); |
257 | 262 | } |
258 | 263 | |
295 | 300 | |
296 | 301 | auto first = true; |
297 | 302 | foreach (ref font; fonts) { |
298 | immutable fid = font.id; | |
303 | immutable fid = font.getId; | |
299 | 304 | if (fid is null) { |
300 | 305 | logWarning ("%s: Ignored font screenshot rendering due to missing ID:", cpt.getId ()); |
301 | 306 | continue; |
306 | 311 | scr.setKind (ScreenshotKind.DEFAULT); |
307 | 312 | else |
308 | 313 | 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"); | |
310 | 315 | |
311 | 316 | if (first) |
312 | 317 | first = false; |
318 | 323 | // be used, this should not be an issue. |
319 | 324 | immutable customSampleText = cpt.getCustomValue ("FontSampleText"); |
320 | 325 | if (!customSampleText.empty) |
321 | font.sampleIconText = customSampleText; | |
326 | font.setSampleIconText (customSampleText); | |
322 | 327 | |
323 | 328 | auto cptScreenshotsUrl = buildPath (gres.gcidForComponent (cpt), "screenshots"); |
324 | 329 | foreach (ref size; fontScreenshotSizes) { |
330 | 335 | if (!std.file.exists (imgFileName)) { |
331 | 336 | // we didn't create s screenshot yet - render it |
332 | 337 | auto cv = new Canvas (size.width, size.height); |
333 | cv.drawTextLine (font, font.sampleText); | |
338 | cv.drawTextLine (font, font.getSampleText, -1); | |
334 | 339 | cv.savePng (imgFileName); |
335 | 340 | } |
336 | 341 | |
337 | auto img = new appstream.Image.Image (); | |
342 | auto img = new asImage.Image (); | |
338 | 343 | img.setKind (ImageKind.THUMBNAIL); |
339 | 344 | img.setWidth (size.width); |
340 | 345 | img.setHeight (size.height); |
33 | 33 | import glib.GException : GException; |
34 | 34 | import appstream.Component; |
35 | 35 | 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; | |
36 | 42 | static import std.file; |
37 | 43 | |
38 | 44 | import asgen.containers : HashMap; |
39 | 45 | import asgen.utils; |
40 | 46 | import asgen.logging; |
41 | 47 | import asgen.result; |
42 | import asgen.image; | |
43 | 48 | import asgen.backends.interfaces; |
44 | 49 | import asgen.contentsstore; |
45 | 50 | import asgen.config : Config, IconPolicy; |
229 | 234 | |
230 | 235 | public: |
231 | 236 | |
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) | |
233 | 238 | { |
234 | 239 | logDebug ("Creating new IconHandler"); |
235 | 240 | |
275 | 280 | return null; |
276 | 281 | return pkgMap.get (pkid, null); |
277 | 282 | } |
278 | ||
279 | // open package contents cache | |
280 | auto ccache = scoped!ContentsStore (); | |
281 | ccache.open (conf); | |
282 | 283 | |
283 | 284 | // load data from the contents index. |
284 | 285 | // we don't show mercy to memory here, we just want the icon lookup to be fast, |
368 | 369 | if (iconName.endsWith (ext)) |
369 | 370 | return true; |
370 | 371 | 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; | |
386 | 372 | } |
387 | 373 | |
388 | 374 | /** |
495 | 481 | IconPolicy policy) |
496 | 482 | { |
497 | 483 | immutable size = policy.iconSize; |
498 | auto iformat = imageKindFromFile (iconPath); | |
484 | auto iformat = AscUtils.imageFormatFromFilename (iconPath); | |
499 | 485 | if (iformat == ImageFormat.UNKNOWN) { |
500 | 486 | gres.addHint (cpt.getId (), "icon-format-unsupported", ["icon_fname": baseName (iconPath)]); |
501 | 487 | return false; |
502 | 488 | } |
503 | 489 | |
504 | 490 | 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)); | |
506 | 492 | |
507 | 493 | if (iconName.endsWith (".svgz")) |
508 | 494 | iconName = iconName.replace (".svgz", ".png"); |
570 | 556 | mkdirRecurse (path); |
571 | 557 | |
572 | 558 | try { |
559 | import gio.MemoryInputStream : MemoryInputStream; | |
560 | auto stream = new MemoryInputStream (cast(ubyte[]) iconData, null); | |
573 | 561 | auto cv = scoped!Canvas (scaled_width, scaled_height); |
574 | cv.renderSvg (iconData); | |
562 | cv.renderSvg (stream); | |
575 | 563 | cv.savePng (iconStoreLocation); |
576 | 564 | } catch (Exception e) { |
577 | 565 | gres.addHint(cpt.getId (), "image-write-error", ["fname": baseName (iconPath), |
582 | 570 | } else { |
583 | 571 | Image img; |
584 | 572 | try { |
585 | img = new Image (iconData, iformat); | |
573 | img = new Image ((cast(ubyte[]) iconData).ptr, cast(ptrdiff_t)iconData.length, | |
574 | 0, ImageLoadFlags.NONE); | |
586 | 575 | } catch (Exception e) { |
587 | 576 | gres.addHint(cpt.getId (), "image-write-error", ["fname": baseName (iconPath), |
588 | 577 | "pkg_fname": baseName (sourcePkg.getFilename), |
597 | 586 | // the icon is not too small |
598 | 587 | if (size != ImageSize (64)) |
599 | 588 | return false; |
600 | if ((img.width < 48) || (img.height < 48)) | |
589 | if ((img.getWidth < 48) || (img.getHeight < 48)) | |
601 | 590 | return false; |
602 | 591 | } else { |
603 | if ((img.width < scaled_width) || (img.height < scaled_height)) | |
592 | if ((img.getWidth < scaled_width) || (img.getHeight < scaled_height)) | |
604 | 593 | return false; |
605 | 594 | } |
606 | 595 | } |
608 | 597 | // ensure that we don't try to make an application visible that has a really tiny icon |
609 | 598 | // by upscaling it to a blurry mess |
610 | 599 | if (size.scale == 1 && size.width == 64) { |
611 | if ((img.width < 48) || (img.height < 48)) { | |
600 | if ((img.getWidth < 48) || (img.getHeight < 48)) { | |
612 | 601 | 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)]); | |
614 | 603 | return false; |
615 | 604 | } |
616 | 605 | } |
617 | 606 | |
618 | 607 | // warn about icon upscaling, it looks ugly |
619 | if (scaled_width > img.width) { | |
608 | if (scaled_width > img.getWidth) { | |
620 | 609 | 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), | |
622 | 611 | "scale_size": size.toString]); |
623 | 612 | } |
624 | 613 | |
627 | 616 | |
628 | 617 | try { |
629 | 618 | img.scale (scaled_width, scaled_height); |
630 | img.savePng (iconStoreLocation); | |
619 | img.saveFilename (iconStoreLocation, | |
620 | 0, 0, | |
621 | ImageSaveFlags.OPTIMIZE); | |
631 | 622 | } catch (Exception e) { |
632 | 623 | gres.addHint (cpt, "image-write-error", ["fname": baseName (iconPath), |
633 | 624 | "pkg_fname": baseName (sourcePkg.getFilename), |
30 | 30 | import asgen.containers: HashMap; |
31 | 31 | import asgen.logging; |
32 | 32 | import asgen.result : GeneratorResult; |
33 | import asgen.contentsstore : ContentsStore; | |
33 | 34 | import asgen.backends.interfaces : Package; |
34 | 35 | |
35 | 36 | |
108 | 109 | private: |
109 | 110 | HashMap!(string, Package) localeIdPkgMap; |
110 | 111 | |
111 | public this (Package[] pkgList) | |
112 | public this (ContentsStore cstore, Package[] pkgList) | |
112 | 113 | { |
113 | 114 | import std.array : array; |
114 | 115 | import std.typecons : scoped; |
115 | import asgen.contentsstore : ContentsStore; | |
116 | 116 | import asgen.config : Config; |
117 | 117 | |
118 | 118 | logDebug ("Creating new LocaleHandler."); |
130 | 130 | if (!conf.feature.processLocale) |
131 | 131 | return; // don't load the expensive locale<->package mapping if we don't need it |
132 | 132 | |
133 | // open package contents cache | |
134 | auto ccache = scoped!ContentsStore (); | |
135 | ccache.open (conf); | |
136 | ||
137 | 133 | // we make the assumption here that all locale for a given domain are in one package. |
138 | 134 | // otherwise this global search will get even more insane. |
139 | 135 | // the key of the map returned by getLocaleMap will therefore contain only the locale |
140 | 136 | // file basename instead of a full path |
141 | auto dbLocaleMap = ccache.getLocaleMap (array(pkgMap.byKey)); | |
137 | auto dbLocaleMap = cstore.getLocaleMap (array(pkgMap.byKey)); | |
142 | 138 | foreach (info; dbLocaleMap.byPair) { |
143 | 139 | immutable id = info.key; |
144 | 140 | immutable pkgid = info.value; |
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 | /* | |
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 | } |
21 | 21 | public import asgen.handlers.desktopparser; |
22 | 22 | public import asgen.handlers.fonthandler; |
23 | 23 | public import asgen.handlers.iconhandler; |
24 | public import asgen.handlers.metainfoparser; | |
25 | public import asgen.handlers.metainfovalidator; | |
26 | 24 | public import asgen.handlers.screenshothandler; |
27 | 25 | public import asgen.handlers.localehandler : LocaleHandler; |
30 | 30 | import appstream.Screenshot : AsScreenshot, Screenshot, ScreenshotMediaKind; |
31 | 31 | import appstream.Image : AsImage, Image, ImageKind; |
32 | 32 | import appstream.Video : AsVideo, Video, VideoContainerKind, VideoCodecKind; |
33 | import ascompose.c.types : ImageFormat, ImageLoadFlags, ImageSaveFlags; | |
34 | static import ascompose.Image; | |
33 | 35 | static import std.file; |
34 | 36 | |
35 | 37 | import asgen.config : Config; |
37 | 39 | import asgen.downloader : Downloader; |
38 | 40 | import asgen.utils : ImageSize, filenameFromURI, getFileContents; |
39 | 41 | import asgen.logging; |
40 | static import asgen.image; | |
41 | 42 | |
42 | 43 | |
43 | 44 | private immutable screenshotSizes = [ImageSize (1248, 702), ImageSize (752, 423), ImageSize (624, 351), ImageSize (224, 126)]; |
357 | 358 | immutable srcImgUrl = buildPath (scrBaseUrl, srcImgName); |
358 | 359 | |
359 | 360 | // 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); | |
362 | 366 | |
363 | 367 | auto img = new Image (); |
364 | 368 | img.setKind (ImageKind.SOURCE); |
365 | 369 | img.setLocale (origImageLocale); |
366 | 370 | |
367 | sourceScrWidth = srcImg.width; | |
368 | sourceScrHeight = srcImg.height; | |
371 | sourceScrWidth = srcImg.getWidth; | |
372 | sourceScrHeight = srcImg.getHeight; | |
369 | 373 | img.setWidth (sourceScrWidth); |
370 | 374 | img.setHeight (sourceScrHeight); |
371 | 375 | |
398 | 402 | continue; |
399 | 403 | |
400 | 404 | 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); | |
402 | 407 | if (size.width > size.height) |
403 | 408 | thumb.scaleToWidth (size.width); |
404 | 409 | else |
405 | 410 | thumb.scaleToHeight (size.height); |
406 | 411 | |
407 | 412 | // 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); | |
409 | 414 | auto thumbImgPath = buildPath (scrExportDir, thumbImgName); |
410 | 415 | auto thumbImgUrl = buildPath (scrBaseUrl, thumbImgName); |
411 | 416 | |
412 | 417 | // store the thumbnail image on disk |
413 | thumb.savePng (thumbImgPath); | |
418 | thumb.saveFilename(thumbImgPath, | |
419 | 0, 0, | |
420 | ImageSaveFlags.OPTIMIZE); | |
414 | 421 | |
415 | 422 | // finally prepare the thumbnail definition and add it to the metadata |
416 | 423 | auto img = new Image (); |
417 | 424 | img.setLocale (origImageLocale); |
418 | 425 | img.setKind (ImageKind.THUMBNAIL); |
419 | img.setWidth (thumb.width); | |
420 | img.setHeight (thumb.height); | |
426 | img.setWidth (thumb.getWidth); | |
427 | img.setHeight (thumb.getHeight); | |
421 | 428 | img.setUrl (thumbImgUrl); |
422 | 429 | scr.addImage (img); |
423 | 430 | } catch (Exception e) { |
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 | /* | |
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 | } |
13 | 13 | # |
14 | 14 | asgen_sources = [ |
15 | 15 | '../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', | |
22 | 16 | 'bindings/libarchive.d', |
23 | 17 | 'bindings/lmdb.d', |
24 | 'bindings/pango.d', | |
25 | 'bindings/rsvg.d', | |
26 | 18 | 'bindings/soup.d', |
27 | 19 | 'containers/package.d', |
28 | 20 | 'containers/hash.d', |
35 | 27 | 'downloader.d', |
36 | 28 | 'engine.d', |
37 | 29 | 'extractor.d', |
38 | 'font.d', | |
39 | 30 | 'handlers/desktopparser.d', |
40 | 31 | 'handlers/fonthandler.d', |
41 | 32 | 'handlers/iconhandler.d', |
42 | 33 | 'handlers/localehandler.d', |
43 | 'handlers/metainfoparser.d', | |
44 | 'handlers/metainfovalidator.d', | |
45 | 34 | 'handlers/package.d', |
46 | 35 | 'handlers/screenshothandler.d', |
47 | 'hint.d', | |
48 | 'image.d', | |
36 | 'hintregistry.d', | |
49 | 37 | 'logging.d', |
50 | 38 | 'mustache.d', |
51 | 39 | 'reportgenerator.d', |
63 | 51 | 'backends/dummy/pkgindex.d', |
64 | 52 | |
65 | 53 | 'backends/alpinelinux/package.d', |
54 | 'backends/alpinelinux/apkindexutils.d', | |
66 | 55 | 'backends/alpinelinux/apkpkg.d', |
67 | 56 | 'backends/alpinelinux/apkpkgindex.d', |
68 | 57 | |
96 | 85 | gir_bind_dir], |
97 | 86 | dependencies: [glibd_dep, |
98 | 87 | appstream_dep, |
88 | ascompose_dep, | |
99 | 89 | lmdb_dep, |
100 | 90 | 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], | |
108 | 92 | link_with: [girbind_lib], |
109 | 93 | d_import_dirs: [data_import_dirs], |
110 | 94 | install: true |
117 | 101 | gir_bind_dir], |
118 | 102 | dependencies: [glibd_dep, |
119 | 103 | appstream_dep, |
104 | ascompose_dep, | |
120 | 105 | lmdb_dep, |
121 | 106 | 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], | |
129 | 108 | link_with: [girbind_lib], |
130 | 109 | d_import_dirs: [data_import_dirs], |
131 | 110 | d_unittest: true |
31 | 31 | |
32 | 32 | import mustache; |
33 | 33 | 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; | |
34 | 38 | |
35 | 39 | import asgen.defines : ASGEN_VERSION; |
36 | 40 | import asgen.utils; |
37 | 41 | import asgen.config; |
38 | 42 | import asgen.logging; |
39 | import asgen.hint; | |
43 | import asgen.hintregistry; | |
40 | 44 | import asgen.backends.interfaces; |
41 | 45 | import asgen.datastore; |
42 | 46 | |
127 | 131 | mustache.ext = "html"; |
128 | 132 | |
129 | 133 | // 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); | |
133 | 135 | } |
134 | 136 | |
135 | 137 | private string[] splitBlockData (string str, string blockType) |
448 | 450 | DataSummary dsum; |
449 | 451 | |
450 | 452 | logInfo ("Collecting data about hints and available metainfo for %s/%s", suiteName, section); |
451 | auto hintTagRegistry = HintTagRegistry.get (); | |
452 | 453 | |
453 | 454 | auto dtype = conf.metadataType; |
454 | 455 | auto mdata = scoped!Metadata (); |
565 | 566 | |
566 | 567 | foreach (jhint; jhints.array) { |
567 | 568 | 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) { | |
570 | 574 | logError ("Encountered invalid tag '%s' in component '%s' of package '%s'", tag, cid, pkid); |
571 | 575 | |
572 | 576 | // 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); | |
575 | 579 | jhint["vars"] = ["tag": tag]; |
576 | 580 | } |
577 | 581 | |
578 | 582 | // 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 (); | |
584 | 586 | |
585 | 587 | // 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) { | |
588 | 590 | he.infos ~= HintTag (tag, msg); |
589 | 591 | pkgsummary.infoCount++; |
590 | } else if (severity == HintSeverity.WARNING) { | |
592 | } else if (severity == IssueSeverity.WARNING) { | |
591 | 593 | he.warnings ~= HintTag (tag, msg); |
592 | 594 | pkgsummary.warningCount++; |
593 | } else if (severity == HintSeverity.PEDANTIC) { | |
595 | } else if (severity == IssueSeverity.PEDANTIC) { | |
594 | 596 | // We ignore pedantic issues completely for now |
595 | 597 | } else { |
596 | 598 | he.errors ~= HintTag (tag, msg); |
0 | 0 | /* |
1 | * Copyright (C) 2016-2017 Matthias Klumpp <matthias@tenstral.net> | |
1 | * Copyright (C) 2016-2021 Matthias Klumpp <matthias@tenstral.net> | |
2 | 2 | * |
3 | 3 | * Licensed under the GNU Lesser General Public License Version 3 |
4 | 4 | * |
19 | 19 | module asgen.result; |
20 | 20 | |
21 | 21 | import std.stdio; |
22 | import std.string; | |
22 | import std.string : format, fromStringz, toStringz; | |
23 | 23 | import std.array : empty; |
24 | 24 | import std.conv : to; |
25 | import std.algorithm : endsWith; | |
25 | 26 | import std.json; |
26 | 27 | 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; | |
27 | 34 | |
28 | 35 | import asgen.containers : HashMap; |
29 | import asgen.hint; | |
36 | import asgen.hintregistry; | |
30 | 37 | import asgen.utils : buildCptGlobalID; |
31 | 38 | import asgen.backends.interfaces; |
32 | 39 | import asgen.config : Config; |
52 | 59 | * Holds metadata generator result(s) and issue hints |
53 | 60 | * for a single package. |
54 | 61 | */ |
55 | final class GeneratorResult | |
62 | final class GeneratorResult : Result | |
56 | 63 | { |
57 | 64 | |
58 | private: | |
59 | Component[string] cpts; | |
60 | string[Component] cptGCID; | |
61 | HashMap!(string, string) mdataHashes; | |
62 | HashMap!(string, GeneratorHint[]) hints; | |
63 | ||
64 | 65 | public: |
65 | immutable string pkid; | |
66 | immutable string pkgname; | |
67 | 66 | Package pkg; |
68 | 67 | |
69 | ||
70 | 68 | this (Package pkg) |
71 | 69 | { |
72 | this.pkid = pkg.id; | |
73 | this.pkgname = pkg.name; | |
70 | super(); | |
71 | setBundleKind (BundleKind.PACKAGE); | |
72 | setBundleId (pkg.name); | |
74 | 73 | this.pkg = pkg; |
75 | 74 | } |
76 | 75 | |
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; | |
96 | 80 | } |
97 | 81 | |
98 | 82 | @trusted |
99 | 83 | bool isIgnored (Component cpt) |
100 | 84 | { |
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; | |
156 | 86 | } |
157 | 87 | |
158 | 88 | /** |
177 | 107 | immutable cid = id.getId (); |
178 | 108 | } |
179 | 109 | |
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); | |
195 | 115 | } |
196 | 116 | |
197 | 117 | /** |
218 | 138 | */ |
219 | 139 | string hintsToJson () |
220 | 140 | { |
221 | if (hints.length == 0) | |
141 | if (hintsCount () == 0) | |
222 | 142 | return null; |
223 | 143 | |
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? | |
225 | 145 | auto map = JSONValue (["null": 0]); |
226 | 146 | map.object.remove ("null"); |
227 | 147 | |
228 | foreach (cid; hints.byKey) { | |
229 | auto cptHints = hints[cid]; | |
148 | foreach (ref cid; getComponentIdsWithHints ()) { | |
149 | auto cptHints = getHints (cid); | |
230 | 150 | auto hintNodes = JSONValue ([0, 0]); |
231 | 151 | 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; | |
234 | 156 | } |
235 | ||
236 | 157 | map.object[cid] = hintNodes; |
237 | 158 | } |
238 | 159 | |
247 | 168 | { |
248 | 169 | auto conf = Config.get (); |
249 | 170 | |
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)); | |
253 | 176 | immutable ckind = cpt.getKind; |
254 | 177 | cpt.setActiveLocale ("C"); |
255 | 178 | |
288 | 211 | |
289 | 212 | // strip out any release artifact information of components that have a |
290 | 213 | // distribution package association |
291 | if (!conf.feature.propagateMetainfoArtifacts) { | |
214 | if (!conf.feature.propagateMetaInfoArtifacts) { | |
292 | 215 | import appstream.c.functions : as_release_get_artifacts; |
293 | 216 | import glib.c.functions : g_ptr_array_set_size; |
294 | 217 | |
295 | 218 | 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); | |
298 | 221 | g_ptr_array_set_size (as_release_get_artifacts (releasePtr), 0); |
299 | 222 | } |
300 | 223 | } |
345 | 268 | cpt.setDescription (desc, lang); |
346 | 269 | desc_added = true; |
347 | 270 | } |
271 | ||
272 | if (conf.feature.warnNoMetaInfo) { | |
273 | if (!addHint (cpt, "no-metainfo")) | |
274 | continue; | |
275 | } | |
276 | ||
348 | 277 | 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 | } | |
351 | 282 | } else { |
352 | import asgen.bindings.appstream_utils : componentKindToString; | |
353 | ||
354 | 283 | if ((ckind == ComponentKind.DESKTOP_APP) || |
355 | 284 | (ckind == ComponentKind.CONSOLE_APP) || |
356 | 285 | (ckind == ComponentKind.WEB_APP)) { |
357 | if (!addHint (cpt, "description-missing", ["kind": componentKindToString (ckind)])) | |
286 | if (!addHint (cpt, "description-missing", ["kind": AsUtils.componentKindToString (ckind)])) | |
358 | 287 | continue; |
359 | 288 | } |
360 | 289 | } |
390 | 319 | |
391 | 320 | } // end of components loop |
392 | 321 | } |
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 | ||
427 | 322 | } |
428 | 323 | |
429 | 324 | unittest |
430 | 325 | { |
431 | 326 | import asgen.backends.dummy.dummypkg; |
432 | 327 | writeln ("TEST: ", "GeneratorResult"); |
328 | loadHintsRegistry (); | |
433 | 329 | |
434 | 330 | auto pkg = new DummyPackage ("foobar", "1.0", "amd64"); |
435 | 331 | auto res = new GeneratorResult (pkg); |
436 | 332 | |
437 | 333 | 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."); | |
441 | 337 | |
442 | 338 | writeln (res.hintsToJson ()); |
443 | 339 | } |
32 | 32 | import std.traits : Unqual, hasIndirections; |
33 | 33 | static import std.file; |
34 | 34 | |
35 | import glib.Bytes : Bytes; | |
35 | 36 | import appstream.Component; |
36 | 37 | import appstream.Icon; |
38 | static import appstream.Utils; | |
39 | alias AsUtils = appstream.Utils.Utils; | |
37 | 40 | |
38 | 41 | import asgen.defines; |
39 | 42 | import asgen.logging; |
165 | 168 | * (in a short check on Debian, it covered all TLDs in use there) |
166 | 169 | */ |
167 | 170 | @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 | { | |
171 | 173 | if (value.empty) |
172 | 174 | return false; |
173 | 175 | |
174 | return as_utils_is_tld (value.toStringz); | |
176 | return AsUtils.isTld (value); | |
175 | 177 | } |
176 | 178 | |
177 | 179 | /** |
183 | 185 | * associated with this component. |
184 | 186 | **/ |
185 | 187 | @trusted |
186 | string buildCptGlobalID (string cid, string checksum, bool allowNoChecksum = false) pure | |
188 | string buildCptGlobalID (string cid, string checksum, bool allowNoChecksum = false) | |
187 | 189 | in { assert (cid.length >= 2); } |
188 | 190 | do |
189 | 191 | { |
217 | 219 | * Get the component-id back from a global component-id. |
218 | 220 | */ |
219 | 221 | @trusted |
220 | string getCidFromGlobalID (string gcid) pure | |
222 | string getCidFromGlobalID (string gcid) | |
221 | 223 | { |
222 | 224 | auto parts = gcid.split ("/"); |
223 | 225 | if (parts.length != 4) |
559 | 561 | return bname; |
560 | 562 | } |
561 | 563 | |
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 | ||
562 | 584 | @trusted |
563 | 585 | unittest |
564 | 586 | { |
335 | 335 | while (archive_read_next_header (ar, &en) == ARCHIVE_OK) { |
336 | 336 | auto pathname = fromStringz (archive_entry_pathname (en)); |
337 | 337 | |
338 | auto m = matchFirst (pathname, re); | |
338 | const m = matchFirst (pathname, re); | |
339 | 339 | if (!m.empty) { |
340 | 340 | auto fdest = buildPath (destdir, baseName (pathname)); |
341 | 341 | this.extractEntryTo (ar, fdest); |
6 | 6 | RUN apt-get update -qq |
7 | 7 | |
8 | 8 | # 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 | |
10 | 10 | |
11 | 11 | # install dependencies used by both appstream and appstream-generator |
12 | 12 | RUN eatmydata apt-get install -yq --no-install-recommends \ |
36 | 36 | libpango1.0-dev |
37 | 37 | |
38 | 38 | # 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 | |
40 | 44 | |
41 | 45 | # Install dscanner |
42 | 46 | 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 | |
44 | 48 | RUN tar -xzf /tmp/dscanner.tar.gz -C /usr/local/bin/ |
45 | 49 | RUN rm /tmp/dscanner.tar.gz |
46 | 50 | 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 | |
53 | 51 | |
54 | 52 | # build & install the current Git snapshot of AppStream |
55 | 53 | RUN mkdir /build-tmp |
61 | 59 | meson --prefix=/usr \ |
62 | 60 | -Dmaintainer=true \ |
63 | 61 | -Dapt-support=true \ |
62 | -Dcompose=true \ | |
64 | 63 | -Dapidocs=false \ |
65 | 64 | .. |
66 | 65 | 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 | #!/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 |
23 | 23 | unused_variable_check="enabled" |
24 | 24 | ; Checks for unused labels |
25 | 25 | unused_label_check="enabled" |
26 | ; Checks for unused function parameters | |
27 | unused_parameter_check="disabled" | |
26 | 28 | ; Checks for duplicate attributes |
27 | 29 | duplicate_attribute="enabled" |
28 | 30 | ; Checks that opEquals and toHash are both defined or neither are defined |
91 | 93 | assert_without_msg="disabled" |
92 | 94 | ; Check indent of if constraints |
93 | 95 | 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 | |
94 | 104 | |
95 | 105 | [analysis.config.ModuleFilters] |
96 | 106 | style_check = "-asgen.bindings" |
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. |