|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +from pyvips import Image, Operation, GValue, Error, \ |
| 4 | + ffi, values_for_enum, vips_lib, gobject_lib, \ |
| 5 | + type_map, type_name, type_from_name, nickname_find |
| 6 | + |
| 7 | +# This file generates the phpdoc comments for the magic methods and properties. |
| 8 | +# It's in Python, since we use the whole of FFI, not just the |
| 9 | +# small bit exposed by php-vips-ext. |
| 10 | + |
| 11 | +# Regenerate docs with something like: |
| 12 | +# |
| 13 | +# cd src |
| 14 | +# python ../examples/generate_phpdoc.py |
| 15 | + |
| 16 | +# this needs pyvips |
| 17 | +# |
| 18 | +# pip install --user pyvips |
| 19 | + |
| 20 | +# map a Python gtype to PHP argument type names |
| 21 | +gtype_to_php_arg = { |
| 22 | + GValue.gbool_type: 'bool', |
| 23 | + GValue.gint_type: 'integer', |
| 24 | + GValue.gdouble_type: 'float', |
| 25 | + GValue.gstr_type: 'string', |
| 26 | + GValue.refstr_type: 'string', |
| 27 | + GValue.genum_type: 'string', |
| 28 | + GValue.gflags_type: 'integer', |
| 29 | + GValue.gobject_type: 'string', |
| 30 | + GValue.image_type: 'Image', |
| 31 | + GValue.array_int_type: 'integer[]|integer', |
| 32 | + GValue.array_double_type: 'float[]|float', |
| 33 | + GValue.array_image_type: 'Image[]|Image', |
| 34 | + GValue.blob_type: 'string' |
| 35 | +} |
| 36 | + |
| 37 | +# php result type names are different, annoyingly, and very restricted |
| 38 | +gtype_to_php_result = { |
| 39 | + GValue.gbool_type: 'bool', |
| 40 | + GValue.gint_type: 'integer', |
| 41 | + GValue.gdouble_type: 'float', |
| 42 | + GValue.gstr_type: 'string', |
| 43 | + GValue.refstr_type: 'string', |
| 44 | + GValue.genum_type: 'string', |
| 45 | + GValue.gflags_type: 'integer', |
| 46 | + GValue.gobject_type: 'string', |
| 47 | + GValue.image_type: 'Image', |
| 48 | + GValue.array_int_type: 'array', |
| 49 | + GValue.array_double_type: 'array', |
| 50 | + GValue.array_image_type: 'array', |
| 51 | + GValue.blob_type: 'string' |
| 52 | +} |
| 53 | + |
| 54 | +# values for VipsArgumentFlags |
| 55 | +_REQUIRED = 1 |
| 56 | +_INPUT = 16 |
| 57 | +_OUTPUT = 32 |
| 58 | +_DEPRECATED = 64 |
| 59 | +_MODIFY = 128 |
| 60 | + |
| 61 | +# for VipsOperationFlags |
| 62 | +_OPERATION_DEPRECATED = 8 |
| 63 | + |
| 64 | + |
| 65 | +def gtype_to_php(gtype, result=False): |
| 66 | + """Map a gtype to PHP type name we use to represent it. |
| 67 | + """ |
| 68 | + |
| 69 | + fundamental = gobject_lib.g_type_fundamental(gtype) |
| 70 | + |
| 71 | + gtype_map = gtype_to_php_result if result else gtype_to_php_arg |
| 72 | + |
| 73 | + if gtype in gtype_map: |
| 74 | + return gtype_map[gtype] |
| 75 | + if fundamental in gtype_map: |
| 76 | + return gtype_map[fundamental] |
| 77 | + return '<unknown type>' |
| 78 | + |
| 79 | + |
| 80 | +def remove_prefix(enum_str): |
| 81 | + prefix = 'Vips' |
| 82 | + |
| 83 | + if enum_str.startswith(prefix): |
| 84 | + return enum_str[len(prefix):] |
| 85 | + |
| 86 | + return enum_str |
| 87 | + |
| 88 | + |
| 89 | +def generate_operation(operation_name): |
| 90 | + op = Operation.new_from_name(operation_name) |
| 91 | + |
| 92 | + # we are only interested in non-deprecated args |
| 93 | + args = [[name, flags] for name, flags in op.get_args() |
| 94 | + if not flags & _DEPRECATED] |
| 95 | + |
| 96 | + # find the first required input image arg, if any ... that will be self |
| 97 | + member_x = None |
| 98 | + for name, flags in args: |
| 99 | + if ((flags & _INPUT) != 0 and |
| 100 | + (flags & _REQUIRED) != 0 and |
| 101 | + op.get_typeof(name) == GValue.image_type): |
| 102 | + member_x = name |
| 103 | + break |
| 104 | + |
| 105 | + required_input = [name for name, flags in args |
| 106 | + if (flags & _INPUT) != 0 and |
| 107 | + (flags & _REQUIRED) != 0 and |
| 108 | + name != member_x] |
| 109 | + |
| 110 | + required_output = [name for name, flags in args |
| 111 | + if ((flags & _OUTPUT) != 0 and |
| 112 | + (flags & _REQUIRED) != 0) or |
| 113 | + ((flags & _INPUT) != 0 and |
| 114 | + (flags & _REQUIRED) != 0 and |
| 115 | + (flags & _MODIFY) != 0)] |
| 116 | + |
| 117 | + result = ' * @method ' |
| 118 | + if member_x is None: |
| 119 | + result += 'static ' |
| 120 | + if len(required_output) == 0: |
| 121 | + result += 'void ' |
| 122 | + elif len(required_output) == 1: |
| 123 | + result += '{0} '.format(gtype_to_php(op.get_typeof(required_output[0]), True)) |
| 124 | + else: |
| 125 | + # we generate a Returns: block for this case, see below |
| 126 | + result += 'array ' |
| 127 | + |
| 128 | + result += '{0}('.format(operation_name) |
| 129 | + for name in required_input: |
| 130 | + gtype = op.get_typeof(name) |
| 131 | + result += '{0} ${1}, '.format(gtype_to_php(gtype), name) |
| 132 | + |
| 133 | + result += 'array $options = []) ' |
| 134 | + |
| 135 | + description = op.get_description() |
| 136 | + result += description[0].upper() + description[1:] + '.\n' |
| 137 | + |
| 138 | + # find any Enums we've referenced and output @see lines for them |
| 139 | + for name in required_output + required_input: |
| 140 | + gtype = op.get_typeof(name) |
| 141 | + fundamental = gobject_lib.g_type_fundamental(gtype) |
| 142 | + |
| 143 | + if fundamental != GValue.genum_type: |
| 144 | + continue |
| 145 | + |
| 146 | + result += ' * @see {0} for possible values for ${1}\n'.format(remove_prefix(type_name(gtype)), name) |
| 147 | + |
| 148 | + if len(required_output) > 1: |
| 149 | + result += ' * Return array with: [\n' |
| 150 | + for name in required_output: |
| 151 | + gtype = op.get_typeof(name) |
| 152 | + blurb = op.get_blurb(name) |
| 153 | + result += ' * \'{0}\' => @type {1} {2}\n'.format(name, gtype_to_php(gtype), |
| 154 | + blurb[0].upper() + blurb[1:]) |
| 155 | + result += ' * ];\n' |
| 156 | + |
| 157 | + result += ' * @throws Exception\n' |
| 158 | + |
| 159 | + return result |
| 160 | + |
| 161 | + |
| 162 | +preamble = """<?php |
| 163 | +
|
| 164 | +/** |
| 165 | + * This file was generated automatically. Do not edit! |
| 166 | + * |
| 167 | + * PHP version 7 |
| 168 | + * |
| 169 | + * LICENSE: |
| 170 | + * |
| 171 | + * Copyright (c) 2016 John Cupitt |
| 172 | + * |
| 173 | + * Permission is hereby granted, free of charge, to any person obtaining |
| 174 | + * a copy of this software and associated documentation files (the |
| 175 | + * "Software"), to deal in the Software without restriction, including |
| 176 | + * without limitation the rights to use, copy, modify, merge, publish, |
| 177 | + * distribute, sublicense, and/or sell copies of the Software, and to |
| 178 | + * permit persons to whom the Software is furnished to do so, subject to |
| 179 | + * the following conditions: |
| 180 | + * |
| 181 | + * The above copyright notice and this permission notice shall be |
| 182 | + * included in all copies or substantial portions of the Software. |
| 183 | + * |
| 184 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 185 | + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 186 | + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 187 | + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| 188 | + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 189 | + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 190 | + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 191 | + * |
| 192 | + * @category Images |
| 193 | + * @package Jcupitt\\Vips |
| 194 | + * @author John Cupitt <jcupitt@gmail.com> |
| 195 | + * @copyright 2016 John Cupitt |
| 196 | + * @license https://opensource.org/licenses/MIT MIT |
| 197 | + * @link https://github.com/jcupitt/php-vips |
| 198 | + */ |
| 199 | +""" |
| 200 | + |
| 201 | +class_header = """ * @category Images |
| 202 | + * @package Jcupitt\\Vips |
| 203 | + * @author John Cupitt <jcupitt@gmail.com> |
| 204 | + * @copyright 2016 John Cupitt |
| 205 | + * @license https://opensource.org/licenses/MIT MIT |
| 206 | + * @link https://github.com/jcupitt/php-vips |
| 207 | +""" |
| 208 | + |
| 209 | + |
| 210 | +def generate_auto_doc(filename): |
| 211 | + all_nicknames = [] |
| 212 | + |
| 213 | + def add_nickname(gtype, a, b): |
| 214 | + nickname = nickname_find(gtype) |
| 215 | + try: |
| 216 | + # can fail for abstract types |
| 217 | + op = Operation.new_from_name(nickname) |
| 218 | + |
| 219 | + # we are only interested in non-deprecated operations |
| 220 | + if (op.get_flags() & _OPERATION_DEPRECATED) == 0: |
| 221 | + all_nicknames.append(nickname) |
| 222 | + except Error: |
| 223 | + pass |
| 224 | + |
| 225 | + type_map(gtype, add_nickname) |
| 226 | + |
| 227 | + return ffi.NULL |
| 228 | + |
| 229 | + type_map(type_from_name('VipsOperation'), add_nickname) |
| 230 | + |
| 231 | + # add 'missing' synonyms by hand |
| 232 | + all_nicknames.append('crop') |
| 233 | + |
| 234 | + # make list unique and sort |
| 235 | + all_nicknames = list(set(all_nicknames)) |
| 236 | + all_nicknames.sort() |
| 237 | + |
| 238 | + # these have hand-written methods, don't autodoc them |
| 239 | + no_generate = [ |
| 240 | + 'bandjoin', |
| 241 | + 'bandrank', |
| 242 | + 'ifthenelse', |
| 243 | + 'add', |
| 244 | + 'subtract', |
| 245 | + 'multiply', |
| 246 | + 'divide', |
| 247 | + 'remainder' |
| 248 | + ] |
| 249 | + all_nicknames = [x for x in all_nicknames if x not in no_generate] |
| 250 | + |
| 251 | + print('Generating {0} ...'.format(filename)) |
| 252 | + |
| 253 | + with open(filename, 'w') as f: |
| 254 | + f.write(preamble) |
| 255 | + f.write('\n') |
| 256 | + f.write('namespace Jcupitt\\Vips;\n') |
| 257 | + f.write('\n') |
| 258 | + f.write('/**\n') |
| 259 | + f.write(' * Autodocs for the Image class.\n') |
| 260 | + f.write(class_header) |
| 261 | + f.write(' *\n') |
| 262 | + |
| 263 | + for nickname in all_nicknames: |
| 264 | + f.write(generate_operation(nickname)) |
| 265 | + |
| 266 | + f.write(' *\n') |
| 267 | + |
| 268 | + # all magic properties |
| 269 | + tmp_file = Image.new_temp_file('%s.v') |
| 270 | + all_properties = tmp_file.get_fields() |
| 271 | + for name in all_properties: |
| 272 | + php_name = name.replace('-', '_') |
| 273 | + gtype = tmp_file.get_typeof(name) |
| 274 | + fundamental = gobject_lib.g_type_fundamental(gtype) |
| 275 | + |
| 276 | + f.write(' * @property {0} ${1} {2}\n'.format(gtype_to_php(gtype), php_name, tmp_file.get_blurb(name))) |
| 277 | + |
| 278 | + if fundamental == GValue.genum_type: |
| 279 | + f.write(' * @see {0} for possible values\n'.format(remove_prefix(type_name(gtype)))) |
| 280 | + |
| 281 | + f.write(' */\n') |
| 282 | + f.write('abstract class ImageAutodoc\n') |
| 283 | + f.write('{\n') |
| 284 | + f.write('}\n') |
| 285 | + |
| 286 | + |
| 287 | +def generate_enums(): |
| 288 | + # otherwise we're missing some enums |
| 289 | + vips_lib.vips_token_get_type() |
| 290 | + vips_lib.vips_saveable_get_type() |
| 291 | + vips_lib.vips_image_type_get_type() |
| 292 | + |
| 293 | + all_enums = [] |
| 294 | + |
| 295 | + def add_enum(gtype, a, b): |
| 296 | + nickname = type_name(gtype) |
| 297 | + all_enums.append(nickname) |
| 298 | + |
| 299 | + type_map(gtype, add_enum) |
| 300 | + |
| 301 | + return ffi.NULL |
| 302 | + |
| 303 | + type_map(type_from_name('GEnum'), add_enum) |
| 304 | + |
| 305 | + for name in all_enums: |
| 306 | + gtype = type_from_name(name) |
| 307 | + php_name = remove_prefix(name) |
| 308 | + |
| 309 | + print('Generating {0}.php ...'.format(php_name)) |
| 310 | + |
| 311 | + with open('{0}.php'.format(php_name), 'w') as f: |
| 312 | + f.write(preamble) |
| 313 | + f.write('\n') |
| 314 | + f.write('namespace Jcupitt\\Vips;\n') |
| 315 | + f.write('\n') |
| 316 | + f.write('/**\n') |
| 317 | + f.write(' * The {0} enum.\n'.format(php_name)) |
| 318 | + f.write(class_header) |
| 319 | + f.write(' */\n') |
| 320 | + f.write('abstract class {0}\n'.format(php_name)) |
| 321 | + f.write('{\n') |
| 322 | + |
| 323 | + for value in values_for_enum(gtype): |
| 324 | + php_name = value.replace('-', '_').upper() |
| 325 | + f.write(' const {0} = \'{1}\';\n'.format(php_name, value)) |
| 326 | + |
| 327 | + f.write('}\n') |
| 328 | + |
| 329 | + |
| 330 | +generate_auto_doc('ImageAutodoc.php') |
| 331 | +generate_enums() |
0 commit comments